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.
Files changed (66) hide show
  1. package/dist/abstract.d.mts +5 -5
  2. package/dist/abstract.mjs +4 -4
  3. package/dist/dev/abstract.mjs +4 -4
  4. package/dist/dev/chunks/dev-key-collision-warnings.mjs +47 -8
  5. package/dist/dev/chunks/devtools.mjs +1 -1
  6. package/dist/dev/directive.mjs +1 -1
  7. package/dist/dev/history.mjs +1 -2
  8. package/dist/dev/index.mjs +5 -5
  9. package/dist/dev/runtime/plugins/attaform.mjs +3 -3
  10. package/dist/dev/shared/{attaform-dev.oYrkP9dj.mjs → attaform-dev.BK6Ge2qR.mjs} +80 -341
  11. package/dist/dev/shared/{attaform-dev.Dr2YzS5B.mjs → attaform-dev.BQc5QV3Y.mjs} +11 -19
  12. package/dist/dev/shared/{attaform-dev.BR8u0YOZ.mjs → attaform-dev.BcNtyOmf.mjs} +4 -4
  13. package/dist/{shared/attaform.h0i6LijT.mjs → dev/shared/attaform-dev.BcYT6tFh.mjs} +152 -20
  14. package/dist/dev/shared/{attaform-dev.6qxD-Yqo.mjs → attaform-dev.BisFstxh.mjs} +227 -88
  15. package/dist/dev/shared/{attaform-dev.WmuhD_CZ.mjs → attaform-dev.CQ2rJiAX.mjs} +833 -656
  16. package/dist/dev/shared/{attaform-dev.P7cseiEG.mjs → attaform-dev.D6CLvaQT.mjs} +20 -11
  17. package/dist/dev/shared/{attaform-dev.D2I8e_Mm.mjs → attaform-dev.DTrHonpc.mjs} +225 -238
  18. package/dist/dev/shared/{attaform-dev.BXE2cr0L.mjs → attaform-dev.DUZckyV8.mjs} +1 -3
  19. package/dist/dev/zod-v3.mjs +6 -6
  20. package/dist/dev/zod-v4.mjs +6 -6
  21. package/dist/dev/zod.mjs +5 -5
  22. package/dist/directive.d.mts +2 -2
  23. package/dist/directive.mjs +1 -1
  24. package/dist/history.d.mts +1 -1
  25. package/dist/history.mjs +1 -2
  26. package/dist/index.d.mts +9 -7
  27. package/dist/index.mjs +5 -5
  28. package/dist/nuxt.d.mts +11 -20
  29. package/dist/nuxt.mjs +0 -1
  30. package/dist/runtime/plugins/attaform.mjs +3 -3
  31. package/dist/shared/{attaform.uLbun31C.mjs → attaform.B3resMQ1.mjs} +19 -11
  32. package/dist/shared/{attaform.Cs2Rbdlo.mjs → attaform.BK0cgu2K.mjs} +123 -139
  33. package/dist/{dev/shared/attaform-dev.By4PRjt0.mjs → shared/attaform.BRDDQeHV.mjs} +136 -26
  34. package/dist/shared/{attaform.NLmerepI.mjs → attaform.BXneBxEj.mjs} +71 -334
  35. package/dist/shared/{attaform.D6DCnnQu.mjs → attaform.ByWP6kSx.mjs} +4 -4
  36. package/dist/shared/{attaform.CJP3RkGa.mjs → attaform.CJ8DpRGj.mjs} +223 -238
  37. package/dist/shared/{attaform.C09fpdLS.mjs → attaform.CUfrMK7t.mjs} +227 -88
  38. package/dist/shared/{attaform.wfY8XFvm.mjs → attaform.CdZIEf7_.mjs} +11 -19
  39. package/dist/shared/{attaform.JfSWdEcb.d.mts → attaform.Clz0ArIi.d.mts} +1 -1
  40. package/dist/shared/{attaform.B8k8JrmK.mjs → attaform.DNJOWjQw.mjs} +826 -612
  41. package/dist/shared/{attaform.B4VkS43-.d.mts → attaform.Rc9u4Vfw.d.mts} +1 -1
  42. package/dist/shared/{attaform.D5V4tZWq.d.mts → attaform.UGyOK83T.d.mts} +200 -218
  43. package/dist/shared/{attaform.Dq4vWxuG.mjs → attaform.WiGdIqgx.mjs} +1 -3
  44. package/dist/shared/{attaform.Cgu-hdSb.d.mts → attaform.bKLzMU0o.d.mts} +321 -523
  45. package/dist/transforms.d.mts +0 -22
  46. package/dist/transforms.mjs +1 -1
  47. package/dist/vite.d.mts +13 -4
  48. package/dist/vite.mjs +8 -2
  49. package/dist/zod-v3.d.mts +6 -4
  50. package/dist/zod-v3.mjs +6 -6
  51. package/dist/zod-v4.d.mts +18 -31
  52. package/dist/zod-v4.mjs +6 -6
  53. package/dist/zod.d.mts +4 -4
  54. package/dist/zod.mjs +5 -5
  55. package/package.json +19 -15
  56. package/skills/attaform/SKILL.md +6 -3
  57. package/skills/attaform/references/custom-components.md +8 -3
  58. package/skills/attaform/references/errors.md +8 -3
  59. package/skills/attaform/references/saving.md +77 -0
  60. package/skills/attaform/references/wizards.md +10 -8
  61. package/dist/chunks/fingerprint.mjs +0 -183
  62. package/dist/chunks/fingerprint2.mjs +0 -159
  63. package/dist/dev/chunks/fingerprint.mjs +0 -183
  64. package/dist/dev/chunks/fingerprint2.mjs +0 -159
  65. package/dist/dev/shared/attaform-dev.BJGA_UOS.mjs +0 -36
  66. 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 live form handle for a step (a facade typed as a form).
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`: the currently active step's form facade.
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 tree and timing
99
+ ## Keyed injection resolves by name and timing
98
100
 
99
- A string key passed to `injectForm('account')` or `injectWizard('onboarding')` _reads_ like a global address but resolves by the **caller's component-tree position and mount timing**, not as a global lookup:
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
- - `provide` / `inject` is descendant-only, so a form created in a **leaf** is unreachable from an **ancestor**.
102
- - A keyed form is not resolvable until the creating component's `setup` has run, so mount order matters.
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
- The consequence: a purely structural refactor can silently break a lookup. The stable shape is to **lift a shared form's creation to the coordinating ancestor** (the component that owns the wizard) and have leaves `injectForm(key)` it downward by a prop key. Always chain `?.` on the result, which is `T | null`.
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 };