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
|
@@ -12,6 +12,8 @@ A step slot is one of:
|
|
|
12
12
|
- a **function** returning any of those, for runtime branching,
|
|
13
13
|
- a `lazy((ctx) => ...)`-wrapped function, which memoizes its resolution and re-fires only when its own tracked reads change.
|
|
14
14
|
|
|
15
|
+
`gate(slot)` wraps any of them rather than being a kind of its own: it marks that position a hard prerequisite and leaves how the slot resolves alone. A function slot may return one, so whether a position is a gate can itself be a runtime decision.
|
|
16
|
+
|
|
15
17
|
```ts
|
|
16
18
|
import { useForm, useWizard } from 'attaform'
|
|
17
19
|
|
|
@@ -24,7 +26,7 @@ const wizard = useWizard({ key: 'onboarding', steps: [account, 'review', profile
|
|
|
24
26
|
## Navigation vs submission
|
|
25
27
|
|
|
26
28
|
- **`wizard.tryNext(): Promise<boolean>`** is the gated Next. It validates the active step and advances only on a clean pass, revealing that step's errors in place otherwise. It resolves to whether it advanced, and it is inline-bindable: `@click="wizard.tryNext()"`.
|
|
27
|
-
- **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate.
|
|
29
|
+
- **`wizard.next()` / `wizard.back()` / `wizard.goTo(key)`** are positional moves with no validation gate. Use them for a Back button or a jump; use `tryNext` for a forward move that should validate. Two exceptions: they refuse while a submit is in flight, and `next()` on an **uncleared `gate()` step behaves as `tryNext()`**, so wiring Next straight to it can never skip the gate's confirmation. Once that gate clears, `next()` is plain navigation again and does not re-submit.
|
|
28
30
|
- **`wizard.handleSubmit(onSubmit, onError?)`** validates **every** step from any position and calls `onSubmit` once with all forms' values. It **never advances**. Wire it to the final Submit.
|
|
29
31
|
|
|
30
32
|
For a forward move that runs a custom callback before advancing, compose the step form's own submit with `next`:
|
|
@@ -58,9 +60,9 @@ const onComplete = wizard.handleSubmit(async (ctx) => {
|
|
|
58
60
|
|
|
59
61
|
The wizard handle exposes the flow's rolled-up state, all reactive:
|
|
60
62
|
|
|
61
|
-
- `wizard.forms[key]`: the
|
|
63
|
+
- `wizard.forms[key]`: the step's own form handle, identity-equal to the `useForm` ref that was slotted in.
|
|
62
64
|
- `wizard.allValues` / `wizard.allErrors`: every form's values / aggregate errors, keyed.
|
|
63
|
-
- `wizard.activeForm`:
|
|
65
|
+
- `wizard.activeForm`: a live facade over whichever step is active, so a handler captured once retargets as the pin moves. Not the handle itself: use `wizard.forms[key]` for that.
|
|
64
66
|
- `wizard.statuses`: per-form status. Plus `wizard.progress`, `wizard.canAdvance`, `wizard.canGoBack`, `wizard.isFinalStep`, `wizard.visited`.
|
|
65
67
|
|
|
66
68
|
## A declarative step registry
|
|
@@ -94,11 +96,11 @@ const wizard = useWizard({
|
|
|
94
96
|
|
|
95
97
|
Everything derives from the registry: the step slots, the titles, any persisted key list. Add, remove, reorder, or gate a step by editing the registry alone. Do not add speculative registry fields with no consumer; add a hook when the second consumer arrives.
|
|
96
98
|
|
|
97
|
-
## Keyed injection resolves by
|
|
99
|
+
## Keyed injection resolves by name and timing
|
|
98
100
|
|
|
99
|
-
A string key passed to `injectForm('account')` or `injectWizard('onboarding')`
|
|
101
|
+
A string key passed to `injectForm('account')` or `injectWizard('onboarding')` is a name in Attaform's app-level registry, not a location in the component tree:
|
|
100
102
|
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
+
- **Position-independent.** The lookup reaches any component in the app: a sibling branch, a floating toolbar, a teleported modal. Extracting a wrapper or changing how deep a component sits never changes what a key resolves to. The no-key ambient form is the opposite, and is the one that runs on `provide` / `inject`: it reaches descendants of the owning `useForm` only.
|
|
104
|
+
- **Time-dependent.** The registry entry appears when the owner's own `useForm({ key })` / `useWizard({ key })` setup runs, and not before.
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
Together they give one rule: **resolve downward.** Vue runs a parent's setup before its children's, so a descendant reaching an ancestor's keyed form always resolves, while an ancestor reaching a descendant's gets `null`. The stable shape is to **lift a shared form's creation to the coordinating ancestor** (the component that owns the wizard) and reach for it at or below that point. Always chain `?.` on the result, which is `T | null`.
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { c as canonicalStringify } from '../shared/attaform.BJGA_UOS.mjs';
|
|
2
|
-
|
|
3
|
-
const cyclicSentinel = "<cyclic>";
|
|
4
|
-
function fingerprintZodSchema(schema) {
|
|
5
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
6
|
-
const inProgress = /* @__PURE__ */ new WeakSet();
|
|
7
|
-
return visit(schema, cache, inProgress);
|
|
8
|
-
}
|
|
9
|
-
function visit(schema, cache, inProgress) {
|
|
10
|
-
const key = schema;
|
|
11
|
-
const cached = cache.get(key);
|
|
12
|
-
if (cached !== void 0) return cached;
|
|
13
|
-
if (inProgress.has(key)) return cyclicSentinel;
|
|
14
|
-
inProgress.add(key);
|
|
15
|
-
try {
|
|
16
|
-
const computed = computeFingerprint(schema, cache, inProgress);
|
|
17
|
-
cache.set(key, computed);
|
|
18
|
-
return computed;
|
|
19
|
-
} finally {
|
|
20
|
-
inProgress.delete(key);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function getDef(schema) {
|
|
24
|
-
return schema._def;
|
|
25
|
-
}
|
|
26
|
-
function computeFingerprint(schema, cache, inProgress) {
|
|
27
|
-
const def = getDef(schema);
|
|
28
|
-
const kind = def.typeName ?? "ZodUnknown";
|
|
29
|
-
const recurse = (child) => visit(child, cache, inProgress);
|
|
30
|
-
switch (kind) {
|
|
31
|
-
case "ZodString":
|
|
32
|
-
case "ZodNumber":
|
|
33
|
-
case "ZodBigInt":
|
|
34
|
-
case "ZodDate":
|
|
35
|
-
return `${kind}${formatChecks(def.checks)}`;
|
|
36
|
-
case "ZodBoolean":
|
|
37
|
-
case "ZodNull":
|
|
38
|
-
case "ZodUndefined":
|
|
39
|
-
case "ZodAny":
|
|
40
|
-
case "ZodUnknown":
|
|
41
|
-
case "ZodNaN":
|
|
42
|
-
case "ZodVoid":
|
|
43
|
-
case "ZodNever":
|
|
44
|
-
return kind;
|
|
45
|
-
case "ZodLiteral":
|
|
46
|
-
return `ZodLiteral:${canonicalStringify(def.value)}`;
|
|
47
|
-
case "ZodEnum": {
|
|
48
|
-
const values = Array.isArray(def.values) ? def.values : [];
|
|
49
|
-
const sorted = [...values].sort((a, b) => {
|
|
50
|
-
const as = String(a);
|
|
51
|
-
const bs = String(b);
|
|
52
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
53
|
-
});
|
|
54
|
-
return `ZodEnum:${canonicalStringify(sorted)}`;
|
|
55
|
-
}
|
|
56
|
-
case "ZodNativeEnum": {
|
|
57
|
-
const values = def.values && typeof def.values === "object" && !Array.isArray(def.values) ? Object.values(def.values) : [];
|
|
58
|
-
const sorted = [...values].sort((a, b) => {
|
|
59
|
-
const as = String(a);
|
|
60
|
-
const bs = String(b);
|
|
61
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
62
|
-
});
|
|
63
|
-
return `ZodNativeEnum:${canonicalStringify(sorted)}`;
|
|
64
|
-
}
|
|
65
|
-
case "ZodObject": {
|
|
66
|
-
const shape = readShapeSafely(def);
|
|
67
|
-
const sortedEntries = Object.entries(shape).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${recurse(v)}`);
|
|
68
|
-
return `ZodObject{${sortedEntries.join(",")}}${formatChecks(def.checks)}`;
|
|
69
|
-
}
|
|
70
|
-
case "ZodArray":
|
|
71
|
-
return `ZodArray[${def.type === void 0 ? "?" : recurse(def.type)}]${formatChecks(def.checks)}`;
|
|
72
|
-
case "ZodTuple": {
|
|
73
|
-
const items = def.items ?? [];
|
|
74
|
-
return `ZodTuple[${items.map(recurse).join(",")}]`;
|
|
75
|
-
}
|
|
76
|
-
case "ZodRecord": {
|
|
77
|
-
const keyPart = def.keyType === void 0 ? "?" : recurse(def.keyType);
|
|
78
|
-
const valuePart = def.valueType === void 0 ? "?" : recurse(def.valueType);
|
|
79
|
-
return `ZodRecord<${keyPart},${valuePart}>`;
|
|
80
|
-
}
|
|
81
|
-
case "ZodUnion": {
|
|
82
|
-
const options = (def.options ?? []).map(recurse).sort();
|
|
83
|
-
return `ZodUnion(${options.join("|")})`;
|
|
84
|
-
}
|
|
85
|
-
case "ZodDiscriminatedUnion": {
|
|
86
|
-
const disc = def.discriminator ?? "?";
|
|
87
|
-
const options = (def.options ?? []).map(recurse).sort();
|
|
88
|
-
return `ZodDiscriminatedUnion[${JSON.stringify(disc)}](${options.join("|")})`;
|
|
89
|
-
}
|
|
90
|
-
case "ZodOptional": {
|
|
91
|
-
const inner = def.innerType;
|
|
92
|
-
return inner === void 0 ? "ZodOptional(?)" : `ZodOptional(${recurse(inner)})`;
|
|
93
|
-
}
|
|
94
|
-
case "ZodNullable": {
|
|
95
|
-
const inner = def.innerType;
|
|
96
|
-
return inner === void 0 ? "ZodNullable(?)" : `ZodNullable(${recurse(inner)})`;
|
|
97
|
-
}
|
|
98
|
-
case "ZodDefault": {
|
|
99
|
-
const inner = def.innerType;
|
|
100
|
-
return `ZodDefault[${defaultFactoryRepr(def.defaultValue)}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
101
|
-
}
|
|
102
|
-
case "ZodReadonly": {
|
|
103
|
-
const inner = def.innerType;
|
|
104
|
-
return inner === void 0 ? "ZodReadonly(?)" : `ZodReadonly(${recurse(inner)})`;
|
|
105
|
-
}
|
|
106
|
-
case "ZodEffects": {
|
|
107
|
-
const effectType = def.effect?.type ?? "effect";
|
|
108
|
-
const inner = def.schema;
|
|
109
|
-
return `ZodEffects:${effectType}:fn:*(${inner === void 0 ? "?" : recurse(inner)})`;
|
|
110
|
-
}
|
|
111
|
-
case "ZodPipeline": {
|
|
112
|
-
const inner = def.in ?? def.out;
|
|
113
|
-
return inner === void 0 ? "ZodPipeline(?)" : `ZodPipeline(${recurse(inner)})`;
|
|
114
|
-
}
|
|
115
|
-
case "ZodCatch": {
|
|
116
|
-
const inner = def.innerType ?? def.schema;
|
|
117
|
-
const catchRepr = defaultFactoryRepr(def.catchValue);
|
|
118
|
-
return `ZodCatch[${catchRepr}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
119
|
-
}
|
|
120
|
-
case "ZodLazy": {
|
|
121
|
-
const resolve = def.getter;
|
|
122
|
-
if (typeof resolve !== "function") return "ZodLazy(?)";
|
|
123
|
-
try {
|
|
124
|
-
const inner = resolve();
|
|
125
|
-
return `ZodLazy(${recurse(inner)})`;
|
|
126
|
-
} catch {
|
|
127
|
-
return "ZodLazy(?)";
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
case "ZodIntersection": {
|
|
131
|
-
const leftPart = def.left === void 0 ? "?" : recurse(def.left);
|
|
132
|
-
const rightPart = def.right === void 0 ? "?" : recurse(def.right);
|
|
133
|
-
const parts = [leftPart, rightPart].sort();
|
|
134
|
-
return `ZodIntersection(${parts.join("&")})`;
|
|
135
|
-
}
|
|
136
|
-
case "ZodSet": {
|
|
137
|
-
const inner = def.valueType;
|
|
138
|
-
return inner === void 0 ? "ZodSet(?)" : `ZodSet<${recurse(inner)}>${formatChecks(def.checks)}`;
|
|
139
|
-
}
|
|
140
|
-
case "ZodBranded": {
|
|
141
|
-
const inner = def.type;
|
|
142
|
-
return inner === void 0 ? "ZodBranded(?)" : `ZodBranded(${recurse(inner)})`;
|
|
143
|
-
}
|
|
144
|
-
// Structural opacity — schemas whose runtime behaviour isn't
|
|
145
|
-
// introspectable via `_def` fall here. Still distinguishable
|
|
146
|
-
// from other kinds by the returned string.
|
|
147
|
-
case "ZodPromise":
|
|
148
|
-
case "ZodFunction":
|
|
149
|
-
case "ZodMap":
|
|
150
|
-
case "ZodSymbol":
|
|
151
|
-
default:
|
|
152
|
-
return `${kind}:*`;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function readShapeSafely(def) {
|
|
156
|
-
if (typeof def.shape !== "function") return {};
|
|
157
|
-
try {
|
|
158
|
-
return def.shape();
|
|
159
|
-
} catch {
|
|
160
|
-
return {};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function defaultFactoryRepr(factory) {
|
|
164
|
-
if (typeof factory !== "function") return "none";
|
|
165
|
-
let first;
|
|
166
|
-
let second;
|
|
167
|
-
try {
|
|
168
|
-
first = factory();
|
|
169
|
-
second = factory();
|
|
170
|
-
} catch {
|
|
171
|
-
return "fn:*";
|
|
172
|
-
}
|
|
173
|
-
if (!Object.is(first, second)) return "fn:*";
|
|
174
|
-
if (typeof first === "function") return "fn:*";
|
|
175
|
-
return canonicalStringify(first);
|
|
176
|
-
}
|
|
177
|
-
function formatChecks(checks) {
|
|
178
|
-
if (!Array.isArray(checks) || checks.length === 0) return "";
|
|
179
|
-
const parts = checks.map((c) => canonicalStringify(c)).sort();
|
|
180
|
-
return `[${parts.join(";")}]`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export { fingerprintZodSchema };
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { c as canonicalStringify } from '../shared/attaform.BJGA_UOS.mjs';
|
|
2
|
-
import { k as kindOf, g as getIntersectionLeft, b as getIntersectionRight, c as unwrapLazy, d as unwrapInner, e as unwrapPipe, f as getDiscriminator, h as getDiscriminatedOptions, i as getUnionOptions, j as getRecordKeyType, l as getRecordValueType, m as getTupleItems, n as getSetValueType, o as getArrayElement, p as getObjectShape, q as getEnumValues, r as getLiteralValues, s as getChecks, t as getCatchDefault, v as getDefaultValue } from '../shared/attaform.NLmerepI.mjs';
|
|
3
|
-
|
|
4
|
-
const cyclicSentinel = "<cyclic>";
|
|
5
|
-
function fingerprintZodSchema(schema) {
|
|
6
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
7
|
-
const inProgress = /* @__PURE__ */ new WeakSet();
|
|
8
|
-
return visit(schema, cache, inProgress);
|
|
9
|
-
}
|
|
10
|
-
function visit(schema, cache, inProgress) {
|
|
11
|
-
const cached = cache.get(schema);
|
|
12
|
-
if (cached !== void 0) return cached;
|
|
13
|
-
if (inProgress.has(schema)) return cyclicSentinel;
|
|
14
|
-
inProgress.add(schema);
|
|
15
|
-
try {
|
|
16
|
-
const computed = computeFingerprint(schema, cache, inProgress);
|
|
17
|
-
cache.set(schema, computed);
|
|
18
|
-
return computed;
|
|
19
|
-
} finally {
|
|
20
|
-
inProgress.delete(schema);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function computeFingerprint(schema, cache, inProgress) {
|
|
24
|
-
const kind = kindOf(schema);
|
|
25
|
-
const recurse = (child) => visit(child, cache, inProgress);
|
|
26
|
-
switch (kind) {
|
|
27
|
-
// Kind-only leaves: no further structure to descend into.
|
|
28
|
-
case "boolean":
|
|
29
|
-
case "null":
|
|
30
|
-
case "undefined":
|
|
31
|
-
case "any":
|
|
32
|
-
case "unknown":
|
|
33
|
-
case "nan":
|
|
34
|
-
case "void":
|
|
35
|
-
case "never":
|
|
36
|
-
return kind;
|
|
37
|
-
// Leaves with checks (min/max/email/regex/...). Checks are
|
|
38
|
-
// canonicalised and sorted so `.min(3).max(10)` and `.max(10).min(3)`
|
|
39
|
-
// produce identical fingerprints.
|
|
40
|
-
case "string":
|
|
41
|
-
case "number":
|
|
42
|
-
case "bigint":
|
|
43
|
-
case "date":
|
|
44
|
-
return `${kind}${formatChecks(schema)}`;
|
|
45
|
-
case "literal": {
|
|
46
|
-
const values = [...getLiteralValues(schema)].sort((a, b) => {
|
|
47
|
-
const as = canonicalStringify(a);
|
|
48
|
-
const bs = canonicalStringify(b);
|
|
49
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
50
|
-
});
|
|
51
|
-
return `literal:${canonicalStringify(values)}`;
|
|
52
|
-
}
|
|
53
|
-
case "enum":
|
|
54
|
-
return `enum:${canonicalStringify([...getEnumValues(schema)].sort((a, b) => compare(a, b)))}`;
|
|
55
|
-
case "object": {
|
|
56
|
-
const shape = getObjectShape(schema);
|
|
57
|
-
const sortedEntries = Object.entries(shape).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${recurse(v)}`);
|
|
58
|
-
return `object{${sortedEntries.join(",")}}${formatChecks(schema)}`;
|
|
59
|
-
}
|
|
60
|
-
case "array":
|
|
61
|
-
return `array[${recurse(getArrayElement(schema))}]${formatChecks(schema)}`;
|
|
62
|
-
case "set":
|
|
63
|
-
return `set<${recurse(getSetValueType(schema))}>${formatChecks(schema)}`;
|
|
64
|
-
case "tuple":
|
|
65
|
-
return `tuple[${getTupleItems(schema).map(recurse).join(",")}]`;
|
|
66
|
-
case "record":
|
|
67
|
-
return `record<${recurse(getRecordKeyType(schema))},${recurse(getRecordValueType(schema))}>`;
|
|
68
|
-
case "union": {
|
|
69
|
-
const options = getUnionOptions(schema).map(recurse).sort();
|
|
70
|
-
return `union(${options.join("|")})`;
|
|
71
|
-
}
|
|
72
|
-
case "discriminated-union": {
|
|
73
|
-
const disc = getDiscriminator(schema) ?? "?";
|
|
74
|
-
const options = getDiscriminatedOptions(schema).map(recurse).sort();
|
|
75
|
-
return `dunion[${JSON.stringify(disc)}](${options.join("|")})`;
|
|
76
|
-
}
|
|
77
|
-
case "optional": {
|
|
78
|
-
const inner = unwrapInner(schema);
|
|
79
|
-
return inner === void 0 ? "optional(?)" : `optional(${recurse(inner)})`;
|
|
80
|
-
}
|
|
81
|
-
case "nullable": {
|
|
82
|
-
const inner = unwrapInner(schema);
|
|
83
|
-
return inner === void 0 ? "nullable(?)" : `nullable(${recurse(inner)})`;
|
|
84
|
-
}
|
|
85
|
-
case "default": {
|
|
86
|
-
const inner = unwrapInner(schema);
|
|
87
|
-
return `default[${stableValueRepr(getDefaultValue, schema)}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
88
|
-
}
|
|
89
|
-
case "readonly": {
|
|
90
|
-
const inner = unwrapInner(schema);
|
|
91
|
-
return inner === void 0 ? "readonly(?)" : `readonly(${recurse(inner)})`;
|
|
92
|
-
}
|
|
93
|
-
case "pipe": {
|
|
94
|
-
const inner = unwrapPipe(schema);
|
|
95
|
-
return inner === void 0 ? "pipe(?)" : `pipe(${recurse(inner)})`;
|
|
96
|
-
}
|
|
97
|
-
case "catch": {
|
|
98
|
-
const inner = unwrapInner(schema);
|
|
99
|
-
return `catch[${stableValueRepr(getCatchDefault, schema)}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
100
|
-
}
|
|
101
|
-
case "lazy": {
|
|
102
|
-
const inner = unwrapLazy(schema);
|
|
103
|
-
return inner === void 0 ? "lazy(?)" : `lazy(${recurse(inner)})`;
|
|
104
|
-
}
|
|
105
|
-
case "intersection": {
|
|
106
|
-
const left = getIntersectionLeft(schema);
|
|
107
|
-
const right = getIntersectionRight(schema);
|
|
108
|
-
const leftFp = left === void 0 ? "?" : recurse(left);
|
|
109
|
-
const rightFp = right === void 0 ? "?" : recurse(right);
|
|
110
|
-
const parts = [leftFp, rightFp].sort();
|
|
111
|
-
return `intersection(${parts.join("&")})`;
|
|
112
|
-
}
|
|
113
|
-
// Structural shape isn't observable for these. Bucket them into
|
|
114
|
-
// kind-only fingerprints — a schema-mismatch warning can't do
|
|
115
|
-
// better than "both are `custom`" here, but that still catches
|
|
116
|
-
// `object` vs `custom` mismatches. `map` / `symbol` / `function`
|
|
117
|
-
// are rejected at adapter construction; kept here for exhaustive
|
|
118
|
-
// switch safety when `fingerprintSchema` is called from tooling.
|
|
119
|
-
case "promise":
|
|
120
|
-
case "custom":
|
|
121
|
-
case "template-literal":
|
|
122
|
-
case "transform":
|
|
123
|
-
case "file":
|
|
124
|
-
case "map":
|
|
125
|
-
case "symbol":
|
|
126
|
-
case "function":
|
|
127
|
-
return `${kind}:*`;
|
|
128
|
-
default: {
|
|
129
|
-
const _ = kind;
|
|
130
|
-
return `unknown:${String(_)}`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function stableValueRepr(get, schema) {
|
|
135
|
-
const first = get(schema);
|
|
136
|
-
const second = get(schema);
|
|
137
|
-
if (!Object.is(first, second) || typeof first === "function") return "fn:*";
|
|
138
|
-
return canonicalStringify(first);
|
|
139
|
-
}
|
|
140
|
-
function formatChecks(schema) {
|
|
141
|
-
const checks = getChecks(schema);
|
|
142
|
-
if (checks.length === 0) return "";
|
|
143
|
-
const parts = checks.map((c) => serializeCheck(c)).sort();
|
|
144
|
-
return `[${parts.join(";")}]`;
|
|
145
|
-
}
|
|
146
|
-
function serializeCheck(check) {
|
|
147
|
-
if (check !== null && typeof check === "object") {
|
|
148
|
-
const def = check._zod?.def;
|
|
149
|
-
if (def !== void 0) return canonicalStringify(def);
|
|
150
|
-
}
|
|
151
|
-
return canonicalStringify(check);
|
|
152
|
-
}
|
|
153
|
-
function compare(a, b) {
|
|
154
|
-
const as = String(a);
|
|
155
|
-
const bs = String(b);
|
|
156
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export { fingerprintZodSchema };
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { c as canonicalStringify } from '../shared/attaform-dev.BJGA_UOS.mjs';
|
|
2
|
-
|
|
3
|
-
const cyclicSentinel = "<cyclic>";
|
|
4
|
-
function fingerprintZodSchema(schema) {
|
|
5
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
6
|
-
const inProgress = /* @__PURE__ */ new WeakSet();
|
|
7
|
-
return visit(schema, cache, inProgress);
|
|
8
|
-
}
|
|
9
|
-
function visit(schema, cache, inProgress) {
|
|
10
|
-
const key = schema;
|
|
11
|
-
const cached = cache.get(key);
|
|
12
|
-
if (cached !== void 0) return cached;
|
|
13
|
-
if (inProgress.has(key)) return cyclicSentinel;
|
|
14
|
-
inProgress.add(key);
|
|
15
|
-
try {
|
|
16
|
-
const computed = computeFingerprint(schema, cache, inProgress);
|
|
17
|
-
cache.set(key, computed);
|
|
18
|
-
return computed;
|
|
19
|
-
} finally {
|
|
20
|
-
inProgress.delete(key);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function getDef(schema) {
|
|
24
|
-
return schema._def;
|
|
25
|
-
}
|
|
26
|
-
function computeFingerprint(schema, cache, inProgress) {
|
|
27
|
-
const def = getDef(schema);
|
|
28
|
-
const kind = def.typeName ?? "ZodUnknown";
|
|
29
|
-
const recurse = (child) => visit(child, cache, inProgress);
|
|
30
|
-
switch (kind) {
|
|
31
|
-
case "ZodString":
|
|
32
|
-
case "ZodNumber":
|
|
33
|
-
case "ZodBigInt":
|
|
34
|
-
case "ZodDate":
|
|
35
|
-
return `${kind}${formatChecks(def.checks)}`;
|
|
36
|
-
case "ZodBoolean":
|
|
37
|
-
case "ZodNull":
|
|
38
|
-
case "ZodUndefined":
|
|
39
|
-
case "ZodAny":
|
|
40
|
-
case "ZodUnknown":
|
|
41
|
-
case "ZodNaN":
|
|
42
|
-
case "ZodVoid":
|
|
43
|
-
case "ZodNever":
|
|
44
|
-
return kind;
|
|
45
|
-
case "ZodLiteral":
|
|
46
|
-
return `ZodLiteral:${canonicalStringify(def.value)}`;
|
|
47
|
-
case "ZodEnum": {
|
|
48
|
-
const values = Array.isArray(def.values) ? def.values : [];
|
|
49
|
-
const sorted = [...values].sort((a, b) => {
|
|
50
|
-
const as = String(a);
|
|
51
|
-
const bs = String(b);
|
|
52
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
53
|
-
});
|
|
54
|
-
return `ZodEnum:${canonicalStringify(sorted)}`;
|
|
55
|
-
}
|
|
56
|
-
case "ZodNativeEnum": {
|
|
57
|
-
const values = def.values && typeof def.values === "object" && !Array.isArray(def.values) ? Object.values(def.values) : [];
|
|
58
|
-
const sorted = [...values].sort((a, b) => {
|
|
59
|
-
const as = String(a);
|
|
60
|
-
const bs = String(b);
|
|
61
|
-
return as < bs ? -1 : as > bs ? 1 : 0;
|
|
62
|
-
});
|
|
63
|
-
return `ZodNativeEnum:${canonicalStringify(sorted)}`;
|
|
64
|
-
}
|
|
65
|
-
case "ZodObject": {
|
|
66
|
-
const shape = readShapeSafely(def);
|
|
67
|
-
const sortedEntries = Object.entries(shape).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${recurse(v)}`);
|
|
68
|
-
return `ZodObject{${sortedEntries.join(",")}}${formatChecks(def.checks)}`;
|
|
69
|
-
}
|
|
70
|
-
case "ZodArray":
|
|
71
|
-
return `ZodArray[${def.type === void 0 ? "?" : recurse(def.type)}]${formatChecks(def.checks)}`;
|
|
72
|
-
case "ZodTuple": {
|
|
73
|
-
const items = def.items ?? [];
|
|
74
|
-
return `ZodTuple[${items.map(recurse).join(",")}]`;
|
|
75
|
-
}
|
|
76
|
-
case "ZodRecord": {
|
|
77
|
-
const keyPart = def.keyType === void 0 ? "?" : recurse(def.keyType);
|
|
78
|
-
const valuePart = def.valueType === void 0 ? "?" : recurse(def.valueType);
|
|
79
|
-
return `ZodRecord<${keyPart},${valuePart}>`;
|
|
80
|
-
}
|
|
81
|
-
case "ZodUnion": {
|
|
82
|
-
const options = (def.options ?? []).map(recurse).sort();
|
|
83
|
-
return `ZodUnion(${options.join("|")})`;
|
|
84
|
-
}
|
|
85
|
-
case "ZodDiscriminatedUnion": {
|
|
86
|
-
const disc = def.discriminator ?? "?";
|
|
87
|
-
const options = (def.options ?? []).map(recurse).sort();
|
|
88
|
-
return `ZodDiscriminatedUnion[${JSON.stringify(disc)}](${options.join("|")})`;
|
|
89
|
-
}
|
|
90
|
-
case "ZodOptional": {
|
|
91
|
-
const inner = def.innerType;
|
|
92
|
-
return inner === void 0 ? "ZodOptional(?)" : `ZodOptional(${recurse(inner)})`;
|
|
93
|
-
}
|
|
94
|
-
case "ZodNullable": {
|
|
95
|
-
const inner = def.innerType;
|
|
96
|
-
return inner === void 0 ? "ZodNullable(?)" : `ZodNullable(${recurse(inner)})`;
|
|
97
|
-
}
|
|
98
|
-
case "ZodDefault": {
|
|
99
|
-
const inner = def.innerType;
|
|
100
|
-
return `ZodDefault[${defaultFactoryRepr(def.defaultValue)}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
101
|
-
}
|
|
102
|
-
case "ZodReadonly": {
|
|
103
|
-
const inner = def.innerType;
|
|
104
|
-
return inner === void 0 ? "ZodReadonly(?)" : `ZodReadonly(${recurse(inner)})`;
|
|
105
|
-
}
|
|
106
|
-
case "ZodEffects": {
|
|
107
|
-
const effectType = def.effect?.type ?? "effect";
|
|
108
|
-
const inner = def.schema;
|
|
109
|
-
return `ZodEffects:${effectType}:fn:*(${inner === void 0 ? "?" : recurse(inner)})`;
|
|
110
|
-
}
|
|
111
|
-
case "ZodPipeline": {
|
|
112
|
-
const inner = def.in ?? def.out;
|
|
113
|
-
return inner === void 0 ? "ZodPipeline(?)" : `ZodPipeline(${recurse(inner)})`;
|
|
114
|
-
}
|
|
115
|
-
case "ZodCatch": {
|
|
116
|
-
const inner = def.innerType ?? def.schema;
|
|
117
|
-
const catchRepr = defaultFactoryRepr(def.catchValue);
|
|
118
|
-
return `ZodCatch[${catchRepr}](${inner === void 0 ? "?" : recurse(inner)})`;
|
|
119
|
-
}
|
|
120
|
-
case "ZodLazy": {
|
|
121
|
-
const resolve = def.getter;
|
|
122
|
-
if (typeof resolve !== "function") return "ZodLazy(?)";
|
|
123
|
-
try {
|
|
124
|
-
const inner = resolve();
|
|
125
|
-
return `ZodLazy(${recurse(inner)})`;
|
|
126
|
-
} catch {
|
|
127
|
-
return "ZodLazy(?)";
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
case "ZodIntersection": {
|
|
131
|
-
const leftPart = def.left === void 0 ? "?" : recurse(def.left);
|
|
132
|
-
const rightPart = def.right === void 0 ? "?" : recurse(def.right);
|
|
133
|
-
const parts = [leftPart, rightPart].sort();
|
|
134
|
-
return `ZodIntersection(${parts.join("&")})`;
|
|
135
|
-
}
|
|
136
|
-
case "ZodSet": {
|
|
137
|
-
const inner = def.valueType;
|
|
138
|
-
return inner === void 0 ? "ZodSet(?)" : `ZodSet<${recurse(inner)}>${formatChecks(def.checks)}`;
|
|
139
|
-
}
|
|
140
|
-
case "ZodBranded": {
|
|
141
|
-
const inner = def.type;
|
|
142
|
-
return inner === void 0 ? "ZodBranded(?)" : `ZodBranded(${recurse(inner)})`;
|
|
143
|
-
}
|
|
144
|
-
// Structural opacity — schemas whose runtime behaviour isn't
|
|
145
|
-
// introspectable via `_def` fall here. Still distinguishable
|
|
146
|
-
// from other kinds by the returned string.
|
|
147
|
-
case "ZodPromise":
|
|
148
|
-
case "ZodFunction":
|
|
149
|
-
case "ZodMap":
|
|
150
|
-
case "ZodSymbol":
|
|
151
|
-
default:
|
|
152
|
-
return `${kind}:*`;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function readShapeSafely(def) {
|
|
156
|
-
if (typeof def.shape !== "function") return {};
|
|
157
|
-
try {
|
|
158
|
-
return def.shape();
|
|
159
|
-
} catch {
|
|
160
|
-
return {};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function defaultFactoryRepr(factory) {
|
|
164
|
-
if (typeof factory !== "function") return "none";
|
|
165
|
-
let first;
|
|
166
|
-
let second;
|
|
167
|
-
try {
|
|
168
|
-
first = factory();
|
|
169
|
-
second = factory();
|
|
170
|
-
} catch {
|
|
171
|
-
return "fn:*";
|
|
172
|
-
}
|
|
173
|
-
if (!Object.is(first, second)) return "fn:*";
|
|
174
|
-
if (typeof first === "function") return "fn:*";
|
|
175
|
-
return canonicalStringify(first);
|
|
176
|
-
}
|
|
177
|
-
function formatChecks(checks) {
|
|
178
|
-
if (!Array.isArray(checks) || checks.length === 0) return "";
|
|
179
|
-
const parts = checks.map((c) => canonicalStringify(c)).sort();
|
|
180
|
-
return `[${parts.join(";")}]`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export { fingerprintZodSchema };
|