@umpire/zod 0.1.0-alpha.6 → 0.1.0-alpha.7
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/README.md +100 -0
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @umpire/zod
|
|
2
|
+
|
|
3
|
+
Availability-aware Zod validation for forms powered by [@umpire/core](https://www.npmjs.com/package/@umpire/core). Disabled fields produce no validation errors. Required/optional follows Umpire's availability map.
|
|
4
|
+
|
|
5
|
+
[Docs](https://sdougbrown.github.io/umpire/examples/signup/) · [Quick Start](https://sdougbrown.github.io/umpire/learn/)
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @umpire/core @umpire/zod zod
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`zod` is a peer dependency — bring your own version (v3 or v4).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { z } from 'zod'
|
|
19
|
+
import { umpire, enabledWhen, requires } from '@umpire/core'
|
|
20
|
+
import { activeSchema, activeErrors, zodErrors } from '@umpire/zod'
|
|
21
|
+
|
|
22
|
+
// 1. Define availability rules
|
|
23
|
+
const ump = umpire({
|
|
24
|
+
fields: {
|
|
25
|
+
email: { required: true, isEmpty: (v) => !v },
|
|
26
|
+
companyName: { required: true, isEmpty: (v) => !v },
|
|
27
|
+
},
|
|
28
|
+
rules: [
|
|
29
|
+
enabledWhen('companyName', (_v, c) => c.plan === 'business', {
|
|
30
|
+
reason: 'business plan required',
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// 2. Define per-field Zod schemas
|
|
36
|
+
const fieldSchemas = {
|
|
37
|
+
email: z.string().email('Enter a valid email'),
|
|
38
|
+
companyName: z.string().min(1, 'Company name is required'),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 3. Compose at render time
|
|
42
|
+
const availability = ump.check(values, { plan })
|
|
43
|
+
|
|
44
|
+
const schema = activeSchema(availability, fieldSchemas, z)
|
|
45
|
+
const result = schema.safeParse(values)
|
|
46
|
+
|
|
47
|
+
if (!result.success) {
|
|
48
|
+
const errors = activeErrors(availability, zodErrors(result.error))
|
|
49
|
+
// errors.email → 'Enter a valid email' (only if email is enabled)
|
|
50
|
+
// errors.companyName → undefined (disabled on personal plan)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
### `activeSchema(availability, schemas, z)`
|
|
57
|
+
|
|
58
|
+
Builds a `z.object()` from the availability map:
|
|
59
|
+
- **Disabled fields** are excluded entirely
|
|
60
|
+
- **Enabled + required** fields use the base schema
|
|
61
|
+
- **Enabled + optional** fields get `.optional()`
|
|
62
|
+
|
|
63
|
+
Pass per-field schemas directly, or use `formSchema.shape` to extract from an existing `z.object()`.
|
|
64
|
+
|
|
65
|
+
Throws if you accidentally pass a `z.object()` instead of its `.shape` — the error message tells you what to do.
|
|
66
|
+
|
|
67
|
+
### `activeErrors(availability, errors)`
|
|
68
|
+
|
|
69
|
+
Filters normalized field errors to only include enabled fields. Returns `Partial<Record<field, message>>`.
|
|
70
|
+
|
|
71
|
+
### `zodErrors(error)`
|
|
72
|
+
|
|
73
|
+
Normalizes a Zod error's `issues` array into `{ field, message }[]` pairs for use with `activeErrors`.
|
|
74
|
+
|
|
75
|
+
## The Render Loop
|
|
76
|
+
|
|
77
|
+
The payoff — one loop renders every field regardless of availability, validation, or fouls:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
{fields.map((field) => {
|
|
81
|
+
const av = availability[field]
|
|
82
|
+
const error = validationErrors[field]
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className={av.enabled ? '' : 'disabled'}>
|
|
86
|
+
<input disabled={!av.enabled} value={values[field]} />
|
|
87
|
+
{!av.enabled && <span>{av.reason}</span>}
|
|
88
|
+
{av.enabled && error && <span className="error">{error}</span>}
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
})}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
No per-field branching. No `if (field === 'companyName' && plan === 'business')`. The rules and schemas declare everything upfront. The render loop just reads.
|
|
95
|
+
|
|
96
|
+
## Docs
|
|
97
|
+
|
|
98
|
+
- [Signup Form + Zod demo](https://sdougbrown.github.io/umpire/examples/signup/) — live interactive example
|
|
99
|
+
- [Composing with Validation](https://sdougbrown.github.io/umpire/concepts/validation/) — patterns and boundary guide
|
|
100
|
+
- [Quick Start](https://sdougbrown.github.io/umpire/learn/) — learn each rule primitive
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umpire/zod",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
".claude"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@umpire/core": "
|
|
24
|
+
"@umpire/core": "^0.1.0-alpha.7"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"zod": "^3.0.0 || ^4.0.0"
|
|
@@ -37,4 +37,4 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"zod": "^3.23.8"
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|