@zod-utils/core 0.2.0 → 0.5.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/README.md +137 -32
- package/dist/index.d.mts +231 -34
- package/dist/index.d.ts +231 -34
- package/dist/index.js +33 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@zod-utils/core)
|
|
4
4
|
[](https://www.npmjs.com/package/@zod-utils/core)
|
|
5
|
+
[](https://bundlephobia.com/package/@zod-utils/core)
|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
[](https://www.typescriptlang.org/)
|
|
7
8
|
[](https://github.com/thu-san/zod-utils/actions)
|
|
@@ -15,10 +16,15 @@ Pure TypeScript utilities for Zod schema manipulation and default extraction. No
|
|
|
15
16
|
npm install @zod-utils/core zod
|
|
16
17
|
```
|
|
17
18
|
|
|
19
|
+
## Related Packages
|
|
20
|
+
|
|
21
|
+
- **[@zod-utils/react-hook-form](https://www.npmjs.com/package/@zod-utils/react-hook-form)** - React Hook Form integration with automatic type transformation. Uses this package internally and re-exports all utilities for convenience.
|
|
22
|
+
|
|
18
23
|
## Features
|
|
19
24
|
|
|
20
25
|
- 🎯 **Extract defaults** - Get default values from Zod schemas
|
|
21
|
-
- ✅ **Check
|
|
26
|
+
- ✅ **Check validation requirements** - Determine if fields will error on empty input
|
|
27
|
+
- 🔍 **Extract validation checks** - Get all validation constraints (min/max, formats, patterns, etc.)
|
|
22
28
|
- 🔧 **Schema utilities** - Unwrap and manipulate schema types
|
|
23
29
|
- 📦 **Zero dependencies** - Only requires Zod as a peer dependency
|
|
24
30
|
- 🌐 **Universal** - Works in Node.js, browsers, and any TypeScript project
|
|
@@ -67,48 +73,64 @@ const defaults = getSchemaDefaults(schema);
|
|
|
67
73
|
|
|
68
74
|
---
|
|
69
75
|
|
|
70
|
-
### `
|
|
76
|
+
### `requiresValidInput(field)`
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
Determines if a field will show validation errors when the user submits empty or invalid input. Useful for form UIs to show which fields need valid user input (asterisks, validation indicators).
|
|
73
79
|
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
- Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)
|
|
80
|
+
**Key insight:** Defaults are just initial values - they don't prevent validation errors if the user clears the field.
|
|
81
|
+
|
|
82
|
+
**Real-world example:**
|
|
78
83
|
|
|
79
84
|
```typescript
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
// Marital status with default but validation rules
|
|
86
|
+
const maritalStatus = z.string().min(1).default('single');
|
|
87
|
+
|
|
88
|
+
// What happens in the form:
|
|
89
|
+
// 1. Initial: field shows "single" (from default)
|
|
90
|
+
// 2. User deletes the value → empty string
|
|
91
|
+
// 3. User submits form → validation fails (.min(1) rejects empty)
|
|
92
|
+
// 4. requiresValidInput(maritalStatus) → true (show *, show error)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**How it works:**
|
|
96
|
+
|
|
97
|
+
1. Removes `.default()` wrappers (defaults ≠ validation rules)
|
|
98
|
+
2. Tests if underlying schema accepts empty/invalid input:
|
|
99
|
+
- `undefined` (via `.optional()`)
|
|
100
|
+
- `null` (via `.nullable()`)
|
|
101
|
+
- Empty string (plain `z.string()`)
|
|
102
|
+
- Empty array (plain `z.array()`)
|
|
103
|
+
3. Returns `true` if validation will fail on empty input
|
|
82
104
|
|
|
83
|
-
|
|
84
|
-
const requiredString = z.string().min(1);
|
|
85
|
-
const nonemptyString = z.string().nonempty();
|
|
86
|
-
const requiredArray = z.array(z.string()).min(1);
|
|
87
|
-
const nonemptyArray = z.array(z.string()).nonempty();
|
|
105
|
+
**Examples:**
|
|
88
106
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
checkIfFieldIsRequired(nonemptyArray); // true
|
|
107
|
+
```typescript
|
|
108
|
+
import { requiresValidInput } from "@zod-utils/core";
|
|
109
|
+
import { z } from "zod";
|
|
93
110
|
|
|
94
|
-
//
|
|
95
|
-
const
|
|
96
|
-
|
|
111
|
+
// User name - required, no default
|
|
112
|
+
const userName = z.string().min(1);
|
|
113
|
+
requiresValidInput(userName); // true - will error if empty
|
|
97
114
|
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
// Marital status - required WITH default
|
|
116
|
+
const maritalStatus = z.string().min(1).default('single');
|
|
117
|
+
requiresValidInput(maritalStatus); // true - will error if user clears it
|
|
100
118
|
|
|
101
|
-
//
|
|
102
|
-
const
|
|
103
|
-
|
|
119
|
+
// Age with default - requires valid input
|
|
120
|
+
const age = z.number().default(0);
|
|
121
|
+
requiresValidInput(age); // true - numbers reject empty strings
|
|
104
122
|
|
|
105
|
-
|
|
106
|
-
|
|
123
|
+
// Optional bio - doesn't require input
|
|
124
|
+
const bio = z.string().optional();
|
|
125
|
+
requiresValidInput(bio); // false - user can leave empty
|
|
107
126
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
127
|
+
// Notes with default but NO validation
|
|
128
|
+
const notes = z.string().default('N/A');
|
|
129
|
+
requiresValidInput(notes); // false - plain z.string() accepts empty
|
|
110
130
|
|
|
111
|
-
|
|
131
|
+
// Nullable middle name
|
|
132
|
+
const middleName = z.string().nullable();
|
|
133
|
+
requiresValidInput(middleName); // false - user can leave null
|
|
112
134
|
```
|
|
113
135
|
|
|
114
136
|
---
|
|
@@ -152,17 +174,100 @@ withoutDefault.parse(undefined); // throws error
|
|
|
152
174
|
|
|
153
175
|
### `extractDefault(field)`
|
|
154
176
|
|
|
155
|
-
Extract the default value from a Zod field (recursively unwraps optional/nullable).
|
|
177
|
+
Extract the default value from a Zod field (recursively unwraps optional/nullable/union layers).
|
|
178
|
+
|
|
179
|
+
**Union handling:** For union types, extracts the default from the first option. If the first option has no default, returns `undefined` (defaults in other union options are not checked).
|
|
156
180
|
|
|
157
181
|
```typescript
|
|
158
182
|
import { extractDefault } from "@zod-utils/core";
|
|
159
183
|
import { z } from "zod";
|
|
160
184
|
|
|
185
|
+
// Basic usage
|
|
161
186
|
const field = z.string().optional().default("hello");
|
|
162
187
|
extractDefault(field); // 'hello'
|
|
163
188
|
|
|
164
189
|
const noDefault = z.string();
|
|
165
190
|
extractDefault(noDefault); // undefined
|
|
191
|
+
|
|
192
|
+
// Union with default in first option
|
|
193
|
+
const unionField = z.union([z.string().default('hello'), z.number()]);
|
|
194
|
+
extractDefault(unionField); // 'hello'
|
|
195
|
+
|
|
196
|
+
// Union with default in second option (only checks first)
|
|
197
|
+
const unionField2 = z.union([z.string(), z.number().default(42)]);
|
|
198
|
+
extractDefault(unionField2); // undefined
|
|
199
|
+
|
|
200
|
+
// Union wrapped in optional
|
|
201
|
+
const wrappedUnion = z.union([z.string().default('test'), z.number()]).optional();
|
|
202
|
+
extractDefault(wrappedUnion); // 'test'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### `getFieldChecks(field)`
|
|
208
|
+
|
|
209
|
+
Extract all validation check definitions from a Zod schema field. Returns Zod's raw check definition objects directly, including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`, `format`, `pattern`, etc.
|
|
210
|
+
|
|
211
|
+
**Automatically unwraps:** optional, nullable, and default layers. For unions, checks only the first option.
|
|
212
|
+
|
|
213
|
+
**Supported check types:** Returns any of 21 check types:
|
|
214
|
+
- **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)
|
|
215
|
+
- **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)
|
|
216
|
+
- **Numeric checks**: `greater_than`, `less_than`, `multiple_of`
|
|
217
|
+
- **Format checks**: `number_format`, `bigint_format`, `string_format` (email, url, uuid, etc.)
|
|
218
|
+
- **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`
|
|
219
|
+
- **Other checks**: `property`, `mime_type`, `overwrite`
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { getFieldChecks } from "@zod-utils/core";
|
|
223
|
+
import { z } from "zod";
|
|
224
|
+
|
|
225
|
+
// String with length constraints
|
|
226
|
+
const username = z.string().min(3).max(20);
|
|
227
|
+
const checks = getFieldChecks(username);
|
|
228
|
+
// [
|
|
229
|
+
// { check: 'min_length', minimum: 3, when: [Function], ... },
|
|
230
|
+
// { check: 'max_length', maximum: 20, when: [Function], ... }
|
|
231
|
+
// ]
|
|
232
|
+
|
|
233
|
+
// Number with range constraints
|
|
234
|
+
const age = z.number().min(18).max(120);
|
|
235
|
+
const checks = getFieldChecks(age);
|
|
236
|
+
// [
|
|
237
|
+
// { check: 'greater_than', value: 18, inclusive: true, ... },
|
|
238
|
+
// { check: 'less_than', value: 120, inclusive: true, ... }
|
|
239
|
+
// ]
|
|
240
|
+
|
|
241
|
+
// Array with item count constraints
|
|
242
|
+
const tags = z.array(z.string()).min(1).max(5);
|
|
243
|
+
const checks = getFieldChecks(tags);
|
|
244
|
+
// [
|
|
245
|
+
// { check: 'min_length', minimum: 1, ... },
|
|
246
|
+
// { check: 'max_length', maximum: 5, ... }
|
|
247
|
+
// ]
|
|
248
|
+
|
|
249
|
+
// String with format validation
|
|
250
|
+
const email = z.string().email();
|
|
251
|
+
const checks = getFieldChecks(email);
|
|
252
|
+
// [{ check: 'string_format', format: 'email', ... }]
|
|
253
|
+
|
|
254
|
+
// Unwrapping optional/nullable/default layers
|
|
255
|
+
const bio = z.string().min(10).max(500).optional();
|
|
256
|
+
const checks = getFieldChecks(bio);
|
|
257
|
+
// [
|
|
258
|
+
// { check: 'min_length', minimum: 10, ... },
|
|
259
|
+
// { check: 'max_length', maximum: 500, ... }
|
|
260
|
+
// ]
|
|
261
|
+
|
|
262
|
+
// No checks
|
|
263
|
+
const plainString = z.string();
|
|
264
|
+
getFieldChecks(plainString); // []
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Type:** The return type is `ZodUnionCheck[]`, a union of all 21 Zod check definition types. You can also import the `ZodUnionCheck` type:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { getFieldChecks, type ZodUnionCheck } from "@zod-utils/core";
|
|
166
271
|
```
|
|
167
272
|
|
|
168
273
|
---
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
+
import { $ZodCheckLessThanDef, $ZodCheckGreaterThanDef, $ZodCheckMultipleOfDef, $ZodCheckNumberFormatDef, $ZodCheckBigIntFormatDef, $ZodCheckMaxSizeDef, $ZodCheckMinSizeDef, $ZodCheckSizeEqualsDef, $ZodCheckMaxLengthDef, $ZodCheckMinLengthDef, $ZodCheckLengthEqualsDef, $ZodCheckStringFormatDef, $ZodCheckRegexDef, $ZodCheckLowerCaseDef, $ZodCheckUpperCaseDef, $ZodCheckIncludesDef, $ZodCheckStartsWithDef, $ZodCheckEndsWithDef, $ZodCheckPropertyDef, $ZodCheckMimeTypeDef, $ZodCheckOverwriteDef } from 'zod/v4/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
|
|
@@ -45,11 +46,14 @@ type Simplify<T> = {
|
|
|
45
46
|
} & {};
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
|
-
* Extracts the default value from a Zod field, recursively unwrapping optional and
|
|
49
|
+
* Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.
|
|
49
50
|
*
|
|
50
|
-
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find
|
|
51
|
+
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`, `ZodUnion`) to find
|
|
51
52
|
* the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.
|
|
52
53
|
*
|
|
54
|
+
* **Union handling:** For union types, extracts the default from the first option. If the first option
|
|
55
|
+
* has no default, returns `undefined` (defaults in other union options are not checked).
|
|
56
|
+
*
|
|
53
57
|
* @template T - The Zod type to extract default from
|
|
54
58
|
* @param field - The Zod field to extract default from
|
|
55
59
|
* @returns The default value if present, undefined otherwise
|
|
@@ -71,6 +75,22 @@ type Simplify<T> = {
|
|
|
71
75
|
* ```
|
|
72
76
|
*
|
|
73
77
|
* @example
|
|
78
|
+
* Union with default in first option
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const field = z.union([z.string().default('hello'), z.number()]);
|
|
81
|
+
* const defaultValue = extractDefault(field);
|
|
82
|
+
* // Result: 'hello' (extracts from first union option)
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* Union with default in second option
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const field = z.union([z.string(), z.number().default(42)]);
|
|
89
|
+
* const defaultValue = extractDefault(field);
|
|
90
|
+
* // Result: undefined (only checks first option)
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
74
94
|
* Field without default
|
|
75
95
|
* ```typescript
|
|
76
96
|
* const field = z.string().optional();
|
|
@@ -169,6 +189,78 @@ type Unwrappable = {
|
|
|
169
189
|
* @since 0.1.0
|
|
170
190
|
*/
|
|
171
191
|
declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrappable;
|
|
192
|
+
/**
|
|
193
|
+
* Unwraps a ZodUnion type and returns the first field and all union options.
|
|
194
|
+
*
|
|
195
|
+
* This function extracts the individual type options from a union type.
|
|
196
|
+
* By default, it filters out `ZodNull` and `ZodUndefined` types, returning only
|
|
197
|
+
* the meaningful type options. You can disable this filtering to get all options.
|
|
198
|
+
*
|
|
199
|
+
* @template T - The Zod type to unwrap
|
|
200
|
+
* @param field - The Zod field (union or single type)
|
|
201
|
+
* @param options - Configuration options
|
|
202
|
+
* @param options.filterNullish - Whether to filter out null and undefined types (default: true)
|
|
203
|
+
* @returns Object with `field` (first option) and `union` (all options array)
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* Basic union unwrapping
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const field = z.union([z.string(), z.number()]);
|
|
209
|
+
* const result = unwrapUnion(field);
|
|
210
|
+
* // Result: { field: z.string(), union: [z.string(), z.number()] }
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* Union with null (filtered by default)
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const field = z.union([z.string(), z.null()]);
|
|
217
|
+
* const result = unwrapUnion(field);
|
|
218
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* Union with null (keep all options)
|
|
223
|
+
* ```typescript
|
|
224
|
+
* const field = z.union([z.string(), z.null()]);
|
|
225
|
+
* const result = unwrapUnion(field, { filterNullish: false });
|
|
226
|
+
* // Result: { field: z.string(), union: [z.string(), z.null()] }
|
|
227
|
+
* ```
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* Non-union type (returns single field)
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const field = z.string();
|
|
233
|
+
* const result = unwrapUnion(field);
|
|
234
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* Nullable as union
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const field = z.string().nullable(); // This is z.union([z.string(), z.null()])
|
|
241
|
+
* const result = unwrapUnion(field);
|
|
242
|
+
* // Result: { field: z.string(), union: [z.string()] } (null filtered out)
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* Using the first field for type checking
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const field = z.union([z.string(), z.number()]);
|
|
249
|
+
* const { field: firstField, union } = unwrapUnion(field);
|
|
250
|
+
* if (firstField instanceof z.ZodString) {
|
|
251
|
+
* console.log('First type is string');
|
|
252
|
+
* }
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @see {@link getPrimitiveType} for unwrapping wrapper types
|
|
256
|
+
* @since 0.1.0
|
|
257
|
+
*/
|
|
258
|
+
declare function unwrapUnion<T extends z.ZodTypeAny>(field: T, options?: {
|
|
259
|
+
filterNullish?: boolean;
|
|
260
|
+
}): {
|
|
261
|
+
field: z.ZodTypeAny;
|
|
262
|
+
union: z.ZodTypeAny[];
|
|
263
|
+
};
|
|
172
264
|
/**
|
|
173
265
|
* Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.
|
|
174
266
|
*
|
|
@@ -206,7 +298,7 @@ declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrapp
|
|
|
206
298
|
* @see {@link canUnwrap} for checking if a field can be unwrapped
|
|
207
299
|
* @since 0.1.0
|
|
208
300
|
*/
|
|
209
|
-
declare const getPrimitiveType: <T extends z.
|
|
301
|
+
declare const getPrimitiveType: <T extends z.ZodType>(field: T) => z.ZodTypeAny;
|
|
210
302
|
type StripZodDefault<T> = T extends z.ZodDefault<infer Inner> ? StripZodDefault<Inner> : T extends z.ZodOptional<infer Inner> ? z.ZodOptional<StripZodDefault<Inner>> : T extends z.ZodNullable<infer Inner> ? z.ZodNullable<StripZodDefault<Inner>> : T;
|
|
211
303
|
/**
|
|
212
304
|
* Removes default values from a Zod field while preserving other wrapper types.
|
|
@@ -243,72 +335,177 @@ type StripZodDefault<T> = T extends z.ZodDefault<infer Inner> ? StripZodDefault<
|
|
|
243
335
|
* // Result: z.string().nullable()
|
|
244
336
|
* ```
|
|
245
337
|
*
|
|
246
|
-
* @see {@link
|
|
338
|
+
* @see {@link requiresValidInput} for usage with requirement checking
|
|
247
339
|
* @since 0.1.0
|
|
248
340
|
*/
|
|
249
341
|
declare function removeDefault<T extends z.ZodType>(field: T): StripZodDefault<T>;
|
|
250
342
|
/**
|
|
251
|
-
*
|
|
343
|
+
* Determines if a field will show validation errors when the user submits empty or invalid input.
|
|
344
|
+
*
|
|
345
|
+
* This is useful for form UIs to indicate which fields require valid user input (e.g., showing
|
|
346
|
+
* asterisks, validation states). The key insight: **defaults are just initial values** - they
|
|
347
|
+
* don't prevent validation errors if the user clears the field.
|
|
252
348
|
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
349
|
+
* **Real-world example:**
|
|
350
|
+
* ```typescript
|
|
351
|
+
* // Marital status field with default but validation rules
|
|
352
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
353
|
+
*
|
|
354
|
+
* // Initial: field shows "single" (from default)
|
|
355
|
+
* // User deletes the value → field is now empty string
|
|
356
|
+
* // User submits form → validation fails because .min(1) rejects empty strings
|
|
357
|
+
* // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)
|
|
358
|
+
* ```
|
|
258
359
|
*
|
|
259
|
-
* **
|
|
260
|
-
*
|
|
360
|
+
* **How it works:**
|
|
361
|
+
* 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)
|
|
362
|
+
* 2. Tests if the underlying schema accepts empty/invalid input:
|
|
363
|
+
* - `undefined` (via `.optional()`)
|
|
364
|
+
* - `null` (via `.nullable()`)
|
|
365
|
+
* - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)
|
|
366
|
+
* - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)
|
|
367
|
+
* 3. Returns `true` if validation will fail, `false` if empty input is accepted
|
|
261
368
|
*
|
|
262
369
|
* @template T - The Zod type to check
|
|
263
|
-
* @param field - The Zod field to check
|
|
264
|
-
* @returns True if the field
|
|
370
|
+
* @param field - The Zod field to check
|
|
371
|
+
* @returns True if the field will show validation errors on empty/invalid input, false otherwise
|
|
265
372
|
*
|
|
266
373
|
* @example
|
|
267
|
-
*
|
|
374
|
+
* User name field - required, no default
|
|
268
375
|
* ```typescript
|
|
269
|
-
* const
|
|
270
|
-
*
|
|
376
|
+
* const userName = z.string().min(1);
|
|
377
|
+
* requiresValidInput(userName); // true - will error if user submits empty
|
|
271
378
|
* ```
|
|
272
379
|
*
|
|
273
380
|
* @example
|
|
274
|
-
*
|
|
381
|
+
* Marital status - required WITH default
|
|
275
382
|
* ```typescript
|
|
276
|
-
* const
|
|
277
|
-
*
|
|
383
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
384
|
+
* requiresValidInput(maritalStatus); // true - will error if user clears and submits
|
|
278
385
|
* ```
|
|
279
386
|
*
|
|
280
387
|
* @example
|
|
281
|
-
*
|
|
388
|
+
* Age with default - requires valid input
|
|
282
389
|
* ```typescript
|
|
283
|
-
* const
|
|
284
|
-
*
|
|
390
|
+
* const age = z.number().default(0);
|
|
391
|
+
* requiresValidInput(age); // true - numbers reject empty strings
|
|
285
392
|
* ```
|
|
286
393
|
*
|
|
287
394
|
* @example
|
|
288
|
-
*
|
|
395
|
+
* Optional bio field - doesn't require input
|
|
289
396
|
* ```typescript
|
|
290
|
-
* const
|
|
291
|
-
*
|
|
397
|
+
* const bio = z.string().optional();
|
|
398
|
+
* requiresValidInput(bio); // false - user can leave empty
|
|
292
399
|
* ```
|
|
293
400
|
*
|
|
294
401
|
* @example
|
|
295
|
-
* String with
|
|
402
|
+
* String with default but NO validation - doesn't require input
|
|
296
403
|
* ```typescript
|
|
297
|
-
* const
|
|
298
|
-
*
|
|
404
|
+
* const notes = z.string().default('N/A');
|
|
405
|
+
* requiresValidInput(notes); // false - plain z.string() accepts empty strings
|
|
299
406
|
* ```
|
|
300
407
|
*
|
|
301
408
|
* @example
|
|
302
|
-
* Nullable field
|
|
409
|
+
* Nullable field - doesn't require input
|
|
303
410
|
* ```typescript
|
|
304
|
-
* const
|
|
305
|
-
*
|
|
411
|
+
* const middleName = z.string().nullable();
|
|
412
|
+
* requiresValidInput(middleName); // false - user can leave null
|
|
306
413
|
* ```
|
|
307
414
|
*
|
|
308
415
|
* @see {@link removeDefault} for understanding how defaults are handled
|
|
309
416
|
* @see {@link getPrimitiveType} for understanding type unwrapping
|
|
310
417
|
* @since 0.1.0
|
|
311
418
|
*/
|
|
312
|
-
declare const
|
|
419
|
+
declare const requiresValidInput: <T extends z.ZodType>(field: T) => boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Union type of all Zod check definition types.
|
|
422
|
+
*
|
|
423
|
+
* Includes all validation check types supported by Zod v4:
|
|
424
|
+
* - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)
|
|
425
|
+
* - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)
|
|
426
|
+
* - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`
|
|
427
|
+
* - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)
|
|
428
|
+
* - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`
|
|
429
|
+
* - **Other checks**: `property`, `mime_type`, `overwrite`
|
|
430
|
+
*
|
|
431
|
+
* @since 0.4.0
|
|
432
|
+
*/
|
|
433
|
+
type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckMultipleOfDef | $ZodCheckNumberFormatDef | $ZodCheckBigIntFormatDef | $ZodCheckMaxSizeDef | $ZodCheckMinSizeDef | $ZodCheckSizeEqualsDef | $ZodCheckMaxLengthDef | $ZodCheckMinLengthDef | $ZodCheckLengthEqualsDef | $ZodCheckStringFormatDef | $ZodCheckRegexDef | $ZodCheckLowerCaseDef | $ZodCheckUpperCaseDef | $ZodCheckIncludesDef | $ZodCheckStartsWithDef | $ZodCheckEndsWithDef | $ZodCheckPropertyDef | $ZodCheckMimeTypeDef | $ZodCheckOverwriteDef;
|
|
434
|
+
/**
|
|
435
|
+
* Extracts all validation check definitions from a Zod schema field.
|
|
436
|
+
*
|
|
437
|
+
* This function analyzes a Zod field and returns all check definitions as defined
|
|
438
|
+
* by Zod's internal structure. Returns Zod's raw check definition objects directly,
|
|
439
|
+
* including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,
|
|
440
|
+
* `format`, `pattern`, etc.
|
|
441
|
+
*
|
|
442
|
+
* **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.
|
|
443
|
+
* For unions, checks only the first option (same as other schema utilities).
|
|
444
|
+
*
|
|
445
|
+
* **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},
|
|
446
|
+
* including length, size, numeric range, format validation, string patterns, and more.
|
|
447
|
+
*
|
|
448
|
+
* @template T - The Zod type to extract checks from
|
|
449
|
+
* @param field - The Zod field to analyze
|
|
450
|
+
* @returns Array of Zod check definition objects (see {@link ZodUnionCheck})
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* String with length constraints
|
|
454
|
+
* ```typescript
|
|
455
|
+
* const username = z.string().min(3).max(20);
|
|
456
|
+
* const checks = getFieldChecks(username);
|
|
457
|
+
* // [
|
|
458
|
+
* // { check: 'min_length', minimum: 3, when: [Function], ... },
|
|
459
|
+
* // { check: 'max_length', maximum: 20, when: [Function], ... }
|
|
460
|
+
* // ]
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* Number with range constraints
|
|
465
|
+
* ```typescript
|
|
466
|
+
* const age = z.number().min(18).max(120);
|
|
467
|
+
* const checks = getFieldChecks(age);
|
|
468
|
+
* // [
|
|
469
|
+
* // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },
|
|
470
|
+
* // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }
|
|
471
|
+
* // ]
|
|
472
|
+
* ```
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* Array with item count constraints
|
|
476
|
+
* ```typescript
|
|
477
|
+
* const tags = z.array(z.string()).min(1).max(5);
|
|
478
|
+
* const checks = getFieldChecks(tags);
|
|
479
|
+
* // [
|
|
480
|
+
* // { check: 'min_length', minimum: 1, ... },
|
|
481
|
+
* // { check: 'max_length', maximum: 5, ... }
|
|
482
|
+
* // ]
|
|
483
|
+
* ```
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* String with format validation
|
|
487
|
+
* ```typescript
|
|
488
|
+
* const email = z.string().email();
|
|
489
|
+
* const checks = getFieldChecks(email);
|
|
490
|
+
* // [
|
|
491
|
+
* // { check: 'string_format', format: 'email', ... }
|
|
492
|
+
* // ]
|
|
493
|
+
* ```
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* Unwrapping optional/nullable/default layers
|
|
497
|
+
* ```typescript
|
|
498
|
+
* const bio = z.string().min(10).max(500).optional();
|
|
499
|
+
* const checks = getFieldChecks(bio);
|
|
500
|
+
* // [
|
|
501
|
+
* // { check: 'min_length', minimum: 10, ... },
|
|
502
|
+
* // { check: 'max_length', maximum: 500, ... }
|
|
503
|
+
* // ]
|
|
504
|
+
* ```
|
|
505
|
+
*
|
|
506
|
+
* @see {@link ZodUnionCheck} for all supported check types
|
|
507
|
+
* @since 0.4.0
|
|
508
|
+
*/
|
|
509
|
+
declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
|
|
313
510
|
|
|
314
|
-
export { type Simplify,
|
|
511
|
+
export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, unwrapUnion };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
+
import { $ZodCheckLessThanDef, $ZodCheckGreaterThanDef, $ZodCheckMultipleOfDef, $ZodCheckNumberFormatDef, $ZodCheckBigIntFormatDef, $ZodCheckMaxSizeDef, $ZodCheckMinSizeDef, $ZodCheckSizeEqualsDef, $ZodCheckMaxLengthDef, $ZodCheckMinLengthDef, $ZodCheckLengthEqualsDef, $ZodCheckStringFormatDef, $ZodCheckRegexDef, $ZodCheckLowerCaseDef, $ZodCheckUpperCaseDef, $ZodCheckIncludesDef, $ZodCheckStartsWithDef, $ZodCheckEndsWithDef, $ZodCheckPropertyDef, $ZodCheckMimeTypeDef, $ZodCheckOverwriteDef } from 'zod/v4/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
|
|
@@ -45,11 +46,14 @@ type Simplify<T> = {
|
|
|
45
46
|
} & {};
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
|
-
* Extracts the default value from a Zod field, recursively unwrapping optional and
|
|
49
|
+
* Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.
|
|
49
50
|
*
|
|
50
|
-
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find
|
|
51
|
+
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`, `ZodUnion`) to find
|
|
51
52
|
* the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.
|
|
52
53
|
*
|
|
54
|
+
* **Union handling:** For union types, extracts the default from the first option. If the first option
|
|
55
|
+
* has no default, returns `undefined` (defaults in other union options are not checked).
|
|
56
|
+
*
|
|
53
57
|
* @template T - The Zod type to extract default from
|
|
54
58
|
* @param field - The Zod field to extract default from
|
|
55
59
|
* @returns The default value if present, undefined otherwise
|
|
@@ -71,6 +75,22 @@ type Simplify<T> = {
|
|
|
71
75
|
* ```
|
|
72
76
|
*
|
|
73
77
|
* @example
|
|
78
|
+
* Union with default in first option
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const field = z.union([z.string().default('hello'), z.number()]);
|
|
81
|
+
* const defaultValue = extractDefault(field);
|
|
82
|
+
* // Result: 'hello' (extracts from first union option)
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* Union with default in second option
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const field = z.union([z.string(), z.number().default(42)]);
|
|
89
|
+
* const defaultValue = extractDefault(field);
|
|
90
|
+
* // Result: undefined (only checks first option)
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
74
94
|
* Field without default
|
|
75
95
|
* ```typescript
|
|
76
96
|
* const field = z.string().optional();
|
|
@@ -169,6 +189,78 @@ type Unwrappable = {
|
|
|
169
189
|
* @since 0.1.0
|
|
170
190
|
*/
|
|
171
191
|
declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrappable;
|
|
192
|
+
/**
|
|
193
|
+
* Unwraps a ZodUnion type and returns the first field and all union options.
|
|
194
|
+
*
|
|
195
|
+
* This function extracts the individual type options from a union type.
|
|
196
|
+
* By default, it filters out `ZodNull` and `ZodUndefined` types, returning only
|
|
197
|
+
* the meaningful type options. You can disable this filtering to get all options.
|
|
198
|
+
*
|
|
199
|
+
* @template T - The Zod type to unwrap
|
|
200
|
+
* @param field - The Zod field (union or single type)
|
|
201
|
+
* @param options - Configuration options
|
|
202
|
+
* @param options.filterNullish - Whether to filter out null and undefined types (default: true)
|
|
203
|
+
* @returns Object with `field` (first option) and `union` (all options array)
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* Basic union unwrapping
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const field = z.union([z.string(), z.number()]);
|
|
209
|
+
* const result = unwrapUnion(field);
|
|
210
|
+
* // Result: { field: z.string(), union: [z.string(), z.number()] }
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* Union with null (filtered by default)
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const field = z.union([z.string(), z.null()]);
|
|
217
|
+
* const result = unwrapUnion(field);
|
|
218
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* Union with null (keep all options)
|
|
223
|
+
* ```typescript
|
|
224
|
+
* const field = z.union([z.string(), z.null()]);
|
|
225
|
+
* const result = unwrapUnion(field, { filterNullish: false });
|
|
226
|
+
* // Result: { field: z.string(), union: [z.string(), z.null()] }
|
|
227
|
+
* ```
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* Non-union type (returns single field)
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const field = z.string();
|
|
233
|
+
* const result = unwrapUnion(field);
|
|
234
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* Nullable as union
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const field = z.string().nullable(); // This is z.union([z.string(), z.null()])
|
|
241
|
+
* const result = unwrapUnion(field);
|
|
242
|
+
* // Result: { field: z.string(), union: [z.string()] } (null filtered out)
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* Using the first field for type checking
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const field = z.union([z.string(), z.number()]);
|
|
249
|
+
* const { field: firstField, union } = unwrapUnion(field);
|
|
250
|
+
* if (firstField instanceof z.ZodString) {
|
|
251
|
+
* console.log('First type is string');
|
|
252
|
+
* }
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @see {@link getPrimitiveType} for unwrapping wrapper types
|
|
256
|
+
* @since 0.1.0
|
|
257
|
+
*/
|
|
258
|
+
declare function unwrapUnion<T extends z.ZodTypeAny>(field: T, options?: {
|
|
259
|
+
filterNullish?: boolean;
|
|
260
|
+
}): {
|
|
261
|
+
field: z.ZodTypeAny;
|
|
262
|
+
union: z.ZodTypeAny[];
|
|
263
|
+
};
|
|
172
264
|
/**
|
|
173
265
|
* Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.
|
|
174
266
|
*
|
|
@@ -206,7 +298,7 @@ declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrapp
|
|
|
206
298
|
* @see {@link canUnwrap} for checking if a field can be unwrapped
|
|
207
299
|
* @since 0.1.0
|
|
208
300
|
*/
|
|
209
|
-
declare const getPrimitiveType: <T extends z.
|
|
301
|
+
declare const getPrimitiveType: <T extends z.ZodType>(field: T) => z.ZodTypeAny;
|
|
210
302
|
type StripZodDefault<T> = T extends z.ZodDefault<infer Inner> ? StripZodDefault<Inner> : T extends z.ZodOptional<infer Inner> ? z.ZodOptional<StripZodDefault<Inner>> : T extends z.ZodNullable<infer Inner> ? z.ZodNullable<StripZodDefault<Inner>> : T;
|
|
211
303
|
/**
|
|
212
304
|
* Removes default values from a Zod field while preserving other wrapper types.
|
|
@@ -243,72 +335,177 @@ type StripZodDefault<T> = T extends z.ZodDefault<infer Inner> ? StripZodDefault<
|
|
|
243
335
|
* // Result: z.string().nullable()
|
|
244
336
|
* ```
|
|
245
337
|
*
|
|
246
|
-
* @see {@link
|
|
338
|
+
* @see {@link requiresValidInput} for usage with requirement checking
|
|
247
339
|
* @since 0.1.0
|
|
248
340
|
*/
|
|
249
341
|
declare function removeDefault<T extends z.ZodType>(field: T): StripZodDefault<T>;
|
|
250
342
|
/**
|
|
251
|
-
*
|
|
343
|
+
* Determines if a field will show validation errors when the user submits empty or invalid input.
|
|
344
|
+
*
|
|
345
|
+
* This is useful for form UIs to indicate which fields require valid user input (e.g., showing
|
|
346
|
+
* asterisks, validation states). The key insight: **defaults are just initial values** - they
|
|
347
|
+
* don't prevent validation errors if the user clears the field.
|
|
252
348
|
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
349
|
+
* **Real-world example:**
|
|
350
|
+
* ```typescript
|
|
351
|
+
* // Marital status field with default but validation rules
|
|
352
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
353
|
+
*
|
|
354
|
+
* // Initial: field shows "single" (from default)
|
|
355
|
+
* // User deletes the value → field is now empty string
|
|
356
|
+
* // User submits form → validation fails because .min(1) rejects empty strings
|
|
357
|
+
* // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)
|
|
358
|
+
* ```
|
|
258
359
|
*
|
|
259
|
-
* **
|
|
260
|
-
*
|
|
360
|
+
* **How it works:**
|
|
361
|
+
* 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)
|
|
362
|
+
* 2. Tests if the underlying schema accepts empty/invalid input:
|
|
363
|
+
* - `undefined` (via `.optional()`)
|
|
364
|
+
* - `null` (via `.nullable()`)
|
|
365
|
+
* - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)
|
|
366
|
+
* - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)
|
|
367
|
+
* 3. Returns `true` if validation will fail, `false` if empty input is accepted
|
|
261
368
|
*
|
|
262
369
|
* @template T - The Zod type to check
|
|
263
|
-
* @param field - The Zod field to check
|
|
264
|
-
* @returns True if the field
|
|
370
|
+
* @param field - The Zod field to check
|
|
371
|
+
* @returns True if the field will show validation errors on empty/invalid input, false otherwise
|
|
265
372
|
*
|
|
266
373
|
* @example
|
|
267
|
-
*
|
|
374
|
+
* User name field - required, no default
|
|
268
375
|
* ```typescript
|
|
269
|
-
* const
|
|
270
|
-
*
|
|
376
|
+
* const userName = z.string().min(1);
|
|
377
|
+
* requiresValidInput(userName); // true - will error if user submits empty
|
|
271
378
|
* ```
|
|
272
379
|
*
|
|
273
380
|
* @example
|
|
274
|
-
*
|
|
381
|
+
* Marital status - required WITH default
|
|
275
382
|
* ```typescript
|
|
276
|
-
* const
|
|
277
|
-
*
|
|
383
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
384
|
+
* requiresValidInput(maritalStatus); // true - will error if user clears and submits
|
|
278
385
|
* ```
|
|
279
386
|
*
|
|
280
387
|
* @example
|
|
281
|
-
*
|
|
388
|
+
* Age with default - requires valid input
|
|
282
389
|
* ```typescript
|
|
283
|
-
* const
|
|
284
|
-
*
|
|
390
|
+
* const age = z.number().default(0);
|
|
391
|
+
* requiresValidInput(age); // true - numbers reject empty strings
|
|
285
392
|
* ```
|
|
286
393
|
*
|
|
287
394
|
* @example
|
|
288
|
-
*
|
|
395
|
+
* Optional bio field - doesn't require input
|
|
289
396
|
* ```typescript
|
|
290
|
-
* const
|
|
291
|
-
*
|
|
397
|
+
* const bio = z.string().optional();
|
|
398
|
+
* requiresValidInput(bio); // false - user can leave empty
|
|
292
399
|
* ```
|
|
293
400
|
*
|
|
294
401
|
* @example
|
|
295
|
-
* String with
|
|
402
|
+
* String with default but NO validation - doesn't require input
|
|
296
403
|
* ```typescript
|
|
297
|
-
* const
|
|
298
|
-
*
|
|
404
|
+
* const notes = z.string().default('N/A');
|
|
405
|
+
* requiresValidInput(notes); // false - plain z.string() accepts empty strings
|
|
299
406
|
* ```
|
|
300
407
|
*
|
|
301
408
|
* @example
|
|
302
|
-
* Nullable field
|
|
409
|
+
* Nullable field - doesn't require input
|
|
303
410
|
* ```typescript
|
|
304
|
-
* const
|
|
305
|
-
*
|
|
411
|
+
* const middleName = z.string().nullable();
|
|
412
|
+
* requiresValidInput(middleName); // false - user can leave null
|
|
306
413
|
* ```
|
|
307
414
|
*
|
|
308
415
|
* @see {@link removeDefault} for understanding how defaults are handled
|
|
309
416
|
* @see {@link getPrimitiveType} for understanding type unwrapping
|
|
310
417
|
* @since 0.1.0
|
|
311
418
|
*/
|
|
312
|
-
declare const
|
|
419
|
+
declare const requiresValidInput: <T extends z.ZodType>(field: T) => boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Union type of all Zod check definition types.
|
|
422
|
+
*
|
|
423
|
+
* Includes all validation check types supported by Zod v4:
|
|
424
|
+
* - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)
|
|
425
|
+
* - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)
|
|
426
|
+
* - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`
|
|
427
|
+
* - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)
|
|
428
|
+
* - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`
|
|
429
|
+
* - **Other checks**: `property`, `mime_type`, `overwrite`
|
|
430
|
+
*
|
|
431
|
+
* @since 0.4.0
|
|
432
|
+
*/
|
|
433
|
+
type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckMultipleOfDef | $ZodCheckNumberFormatDef | $ZodCheckBigIntFormatDef | $ZodCheckMaxSizeDef | $ZodCheckMinSizeDef | $ZodCheckSizeEqualsDef | $ZodCheckMaxLengthDef | $ZodCheckMinLengthDef | $ZodCheckLengthEqualsDef | $ZodCheckStringFormatDef | $ZodCheckRegexDef | $ZodCheckLowerCaseDef | $ZodCheckUpperCaseDef | $ZodCheckIncludesDef | $ZodCheckStartsWithDef | $ZodCheckEndsWithDef | $ZodCheckPropertyDef | $ZodCheckMimeTypeDef | $ZodCheckOverwriteDef;
|
|
434
|
+
/**
|
|
435
|
+
* Extracts all validation check definitions from a Zod schema field.
|
|
436
|
+
*
|
|
437
|
+
* This function analyzes a Zod field and returns all check definitions as defined
|
|
438
|
+
* by Zod's internal structure. Returns Zod's raw check definition objects directly,
|
|
439
|
+
* including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,
|
|
440
|
+
* `format`, `pattern`, etc.
|
|
441
|
+
*
|
|
442
|
+
* **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.
|
|
443
|
+
* For unions, checks only the first option (same as other schema utilities).
|
|
444
|
+
*
|
|
445
|
+
* **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},
|
|
446
|
+
* including length, size, numeric range, format validation, string patterns, and more.
|
|
447
|
+
*
|
|
448
|
+
* @template T - The Zod type to extract checks from
|
|
449
|
+
* @param field - The Zod field to analyze
|
|
450
|
+
* @returns Array of Zod check definition objects (see {@link ZodUnionCheck})
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* String with length constraints
|
|
454
|
+
* ```typescript
|
|
455
|
+
* const username = z.string().min(3).max(20);
|
|
456
|
+
* const checks = getFieldChecks(username);
|
|
457
|
+
* // [
|
|
458
|
+
* // { check: 'min_length', minimum: 3, when: [Function], ... },
|
|
459
|
+
* // { check: 'max_length', maximum: 20, when: [Function], ... }
|
|
460
|
+
* // ]
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* Number with range constraints
|
|
465
|
+
* ```typescript
|
|
466
|
+
* const age = z.number().min(18).max(120);
|
|
467
|
+
* const checks = getFieldChecks(age);
|
|
468
|
+
* // [
|
|
469
|
+
* // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },
|
|
470
|
+
* // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }
|
|
471
|
+
* // ]
|
|
472
|
+
* ```
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* Array with item count constraints
|
|
476
|
+
* ```typescript
|
|
477
|
+
* const tags = z.array(z.string()).min(1).max(5);
|
|
478
|
+
* const checks = getFieldChecks(tags);
|
|
479
|
+
* // [
|
|
480
|
+
* // { check: 'min_length', minimum: 1, ... },
|
|
481
|
+
* // { check: 'max_length', maximum: 5, ... }
|
|
482
|
+
* // ]
|
|
483
|
+
* ```
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* String with format validation
|
|
487
|
+
* ```typescript
|
|
488
|
+
* const email = z.string().email();
|
|
489
|
+
* const checks = getFieldChecks(email);
|
|
490
|
+
* // [
|
|
491
|
+
* // { check: 'string_format', format: 'email', ... }
|
|
492
|
+
* // ]
|
|
493
|
+
* ```
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* Unwrapping optional/nullable/default layers
|
|
497
|
+
* ```typescript
|
|
498
|
+
* const bio = z.string().min(10).max(500).optional();
|
|
499
|
+
* const checks = getFieldChecks(bio);
|
|
500
|
+
* // [
|
|
501
|
+
* // { check: 'min_length', minimum: 10, ... },
|
|
502
|
+
* // { check: 'max_length', maximum: 500, ... }
|
|
503
|
+
* // ]
|
|
504
|
+
* ```
|
|
505
|
+
*
|
|
506
|
+
* @see {@link ZodUnionCheck} for all supported check types
|
|
507
|
+
* @since 0.4.0
|
|
508
|
+
*/
|
|
509
|
+
declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
|
|
313
510
|
|
|
314
|
-
export { type Simplify,
|
|
511
|
+
export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, unwrapUnion };
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,23 @@ var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
|
26
26
|
function canUnwrap(field) {
|
|
27
27
|
return "unwrap" in field && typeof field.unwrap === "function";
|
|
28
28
|
}
|
|
29
|
+
function unwrapUnion(field, options = {}) {
|
|
30
|
+
const { filterNullish = true } = options;
|
|
31
|
+
if (field instanceof z__namespace.ZodUnion) {
|
|
32
|
+
const unionOptions = [...field.def.options];
|
|
33
|
+
const filteredOptions = filterNullish ? unionOptions.filter(
|
|
34
|
+
(option) => !(option instanceof z__namespace.ZodNull) && !(option instanceof z__namespace.ZodUndefined)
|
|
35
|
+
) : unionOptions;
|
|
36
|
+
return {
|
|
37
|
+
field: filteredOptions[0] || field,
|
|
38
|
+
union: filteredOptions
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
field,
|
|
43
|
+
union: [field]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
29
46
|
var getPrimitiveType = (field) => {
|
|
30
47
|
if (field instanceof z__namespace.ZodArray) {
|
|
31
48
|
return field;
|
|
@@ -33,6 +50,9 @@ var getPrimitiveType = (field) => {
|
|
|
33
50
|
if (canUnwrap(field)) {
|
|
34
51
|
return getPrimitiveType(field.unwrap());
|
|
35
52
|
}
|
|
53
|
+
if (field instanceof z__namespace.ZodUnion) {
|
|
54
|
+
return getPrimitiveType(unwrapUnion(field).field);
|
|
55
|
+
}
|
|
36
56
|
return field;
|
|
37
57
|
};
|
|
38
58
|
function removeDefault(field) {
|
|
@@ -50,21 +70,23 @@ function removeDefault(field) {
|
|
|
50
70
|
}
|
|
51
71
|
return field;
|
|
52
72
|
}
|
|
53
|
-
var
|
|
54
|
-
const undefinedResult = field.safeParse(void 0).success;
|
|
55
|
-
if (undefinedResult) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
73
|
+
var requiresValidInput = (field) => {
|
|
58
74
|
const defaultRemovedField = removeDefault(field);
|
|
59
75
|
if (!(defaultRemovedField instanceof z__namespace.ZodType)) {
|
|
60
76
|
return false;
|
|
61
77
|
}
|
|
78
|
+
const undefinedResult = defaultRemovedField.safeParse(void 0).success;
|
|
62
79
|
const nullResult = defaultRemovedField.safeParse(null).success;
|
|
63
80
|
const primitiveType = getPrimitiveType(defaultRemovedField);
|
|
64
81
|
const emptyStringResult = primitiveType.type === "string" && defaultRemovedField.safeParse("").success;
|
|
65
82
|
const emptyArrayResult = primitiveType.type === "array" && defaultRemovedField.safeParse([]).success;
|
|
66
83
|
return !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult;
|
|
67
84
|
};
|
|
85
|
+
function getFieldChecks(field) {
|
|
86
|
+
var _a;
|
|
87
|
+
const primitiveType = getPrimitiveType(field);
|
|
88
|
+
return ((_a = primitiveType.def.checks) == null ? void 0 : _a.map((check) => check._zod.def)) || [];
|
|
89
|
+
}
|
|
68
90
|
|
|
69
91
|
// src/defaults.ts
|
|
70
92
|
function extractDefault(field) {
|
|
@@ -74,6 +96,9 @@ function extractDefault(field) {
|
|
|
74
96
|
if (canUnwrap(field)) {
|
|
75
97
|
return extractDefault(field.unwrap());
|
|
76
98
|
}
|
|
99
|
+
if (field instanceof z__namespace.ZodUnion) {
|
|
100
|
+
return extractDefault(unwrapUnion(field).field);
|
|
101
|
+
}
|
|
77
102
|
return void 0;
|
|
78
103
|
}
|
|
79
104
|
function getSchemaDefaults(schema) {
|
|
@@ -90,10 +115,12 @@ function getSchemaDefaults(schema) {
|
|
|
90
115
|
}
|
|
91
116
|
|
|
92
117
|
exports.canUnwrap = canUnwrap;
|
|
93
|
-
exports.checkIfFieldIsRequired = checkIfFieldIsRequired;
|
|
94
118
|
exports.extractDefault = extractDefault;
|
|
119
|
+
exports.getFieldChecks = getFieldChecks;
|
|
95
120
|
exports.getPrimitiveType = getPrimitiveType;
|
|
96
121
|
exports.getSchemaDefaults = getSchemaDefaults;
|
|
97
122
|
exports.removeDefault = removeDefault;
|
|
123
|
+
exports.requiresValidInput = requiresValidInput;
|
|
124
|
+
exports.unwrapUnion = unwrapUnion;
|
|
98
125
|
//# sourceMappingURL=index.js.map
|
|
99
126
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schema.ts","../src/defaults.ts"],"names":["z","z2"],"mappings":"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","file":"index.js","sourcesContent":["import * as z from 'zod';\n\n/**\n * Type representing a Zod type that has an unwrap method\n */\ntype Unwrappable = { unwrap: () => z.ZodTypeAny };\n\n/**\n * Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).\n *\n * This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types\n * like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.\n *\n * @param field - The Zod field to check\n * @returns True if the field has an unwrap method, false otherwise\n *\n * @example\n * ```typescript\n * const optionalField = z.string().optional();\n * console.log(canUnwrap(optionalField)); // true\n *\n * const plainField = z.string();\n * console.log(canUnwrap(plainField)); // false\n * ```\n *\n * @since 0.1.0\n */\nexport function canUnwrap(\n field: z.ZodTypeAny,\n): field is z.ZodTypeAny & Unwrappable {\n return 'unwrap' in field && typeof field.unwrap === 'function';\n}\n\n/**\n * Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.\n *\n * This function removes wrapper layers (optional, nullable, default) to reveal the base type.\n * **Important:** It stops at array types without unwrapping them, treating arrays as primitives.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field to unwrap\n * @returns The unwrapped primitive Zod type\n *\n * @example\n * Unwrapping to string primitive\n * ```typescript\n * const field = z.string().optional().nullable();\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (unwrapped all wrappers)\n * ```\n *\n * @example\n * Stopping at array type\n * ```typescript\n * const field = z.array(z.string()).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.array(z.string()) (stops at array, doesn't unwrap it)\n * ```\n *\n * @example\n * Unwrapping defaults\n * ```typescript\n * const field = z.number().default(0).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.number()\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @since 0.1.0\n */\nexport const getPrimitiveType = <T extends z.ZodTypeAny>(\n field: T,\n): z.ZodTypeAny => {\n // Stop at arrays - don't unwrap them\n if (field instanceof z.ZodArray) {\n return field;\n }\n\n if (canUnwrap(field)) {\n return getPrimitiveType(field.unwrap());\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link checkIfFieldIsRequired} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Checks if a Zod field is truly required by testing multiple acceptance criteria.\n *\n * A field is considered **not required** if it accepts any of the following:\n * - `undefined` (via `.optional()` or `.default()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n *\n * **Note:** Fields with `.default()` are considered not required since they'll have a value\n * even if the user doesn't provide one.\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check for required status\n * @returns True if the field is required, false otherwise\n *\n * @example\n * Required field\n * ```typescript\n * const field = z.string().min(1);\n * console.log(checkIfFieldIsRequired(field)); // true\n * ```\n *\n * @example\n * Optional field (not required)\n * ```typescript\n * const field = z.string().optional();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * Field with default (not required)\n * ```typescript\n * const field = z.string().default('hello');\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * String without min length (not required - accepts empty string)\n * ```typescript\n * const field = z.string();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * String with nonempty (required)\n * ```typescript\n * const field = z.string().nonempty();\n * console.log(checkIfFieldIsRequired(field)); // true\n * ```\n *\n * @example\n * Nullable field (not required)\n * ```typescript\n * const field = z.number().nullable();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const checkIfFieldIsRequired = <T extends z.ZodType>(field: T) => {\n // First check the original field for undefined - this catches fields with defaults\n const undefinedResult = field.safeParse(undefined).success;\n if (undefinedResult) {\n return false;\n }\n\n const defaultRemovedField = removeDefault(field);\n\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n","import * as z from 'zod';\nimport { canUnwrap } from './schema';\nimport type { Simplify } from './types';\n\n/**\n * Extracts the default value from a Zod field, recursively unwrapping optional and nullable layers.\n *\n * This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find\n * the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.\n *\n * @template T - The Zod type to extract default from\n * @param field - The Zod field to extract default from\n * @returns The default value if present, undefined otherwise\n *\n * @example\n * Basic usage with default value\n * ```typescript\n * const field = z.string().default('hello');\n * const defaultValue = extractDefault(field);\n * // Result: 'hello'\n * ```\n *\n * @example\n * Unwrapping optional/nullable layers\n * ```typescript\n * const field = z.string().default('world').optional();\n * const defaultValue = extractDefault(field);\n * // Result: 'world' (unwraps optional to find default)\n * ```\n *\n * @example\n * Field without default\n * ```typescript\n * const field = z.string().optional();\n * const defaultValue = extractDefault(field);\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for extracting defaults from entire schemas\n * @since 0.1.0\n */\nexport function extractDefault<T extends z.ZodTypeAny>(\n field: T,\n): z.infer<T> | undefined {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.def.defaultValue as z.infer<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(field.unwrap()) as z.infer<T>;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema while skipping fields without defaults.\n *\n * This function recursively traverses the schema and collects all fields that have\n * explicit default values defined. Fields without defaults are excluded from the result.\n *\n * **Important:** Nested defaults are NOT extracted unless the parent object also has\n * an explicit `.default()`. This is by design to match Zod's default value behavior.\n *\n * @template T - The Zod object schema type\n * @param schema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with default values\n *\n * @example\n * Basic usage\n * ```typescript\n * const schema = z.object({\n * name: z.string().default('John'),\n * age: z.number(), // no default - will be skipped\n * email: z.string().email().optional(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { name: 'John' }\n * ```\n *\n * @example\n * Nested objects with defaults\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string().default('Guest')\n * }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()\n *\n * settings: z.object({\n * theme: z.string().default('light')\n * }), // ❌ NOT extracted - parent has no .default()\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { user: { name: 'Guest' } }\n * ```\n *\n * @example\n * Unwrapping optional/nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string().default('Untitled').optional(),\n * count: z.number().default(0).nullable(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<T extends z.ZodObject>(\n schema: T,\n): Simplify<Partial<z.infer<T>>> {\n const defaults: Record<string, unknown> = {};\n\n for (const key in schema.shape) {\n const field = schema.shape[key];\n if (!field) continue;\n\n // Check if this field has an explicit default value\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<T>>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts","../src/defaults.ts"],"names":["z","z2"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkDO,SAAS,UACd,KAAA,EACqC;AACrC,EAAA,OAAO,QAAA,IAAY,KAAA,IAAS,OAAO,KAAA,CAAM,MAAA,KAAW,UAAA;AACtD;AAoEO,SAAS,WAAA,CACd,KAAA,EACA,OAAA,GAAuC,EAAC,EACQ;AAChD,EAAA,MAAM,EAAE,aAAA,GAAgB,IAAA,EAAK,GAAI,OAAA;AAEjC,EAAA,IAAI,iBAAmBA,YAAA,CAAA,QAAA,EAAU;AAE/B,IAAA,MAAM,YAAA,GAAe,CAAC,GAAG,KAAA,CAAM,IAAI,OAAO,CAAA;AAE1C,IAAA,MAAM,eAAA,GAAkB,gBACpB,YAAA,CAAa,MAAA;AAAA,MACX,CAAC,MAAA,KACC,EAAE,MAAA,YAAoBA,YAAA,CAAA,OAAA,CAAA,IACtB,EAAE,MAAA,YAAoBA,YAAA,CAAA,YAAA;AAAA,KAC1B,GACA,YAAA;AAEJ,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,eAAA,CAAgB,CAAC,CAAA,IAAK,KAAA;AAAA,MAC7B,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAGA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,KAAA,EAAO,CAAC,KAAK;AAAA,GACf;AACF;AAuCO,IAAM,gBAAA,GAAmB,CAC9B,KAAA,KACiB;AAEjB,EAAA,IAAI,iBAAmBA,YAAA,CAAA,QAAA,EAAU;AAC/B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,IAAA,OAAO,gBAAA,CAAiB,KAAA,CAAM,MAAA,EAAQ,CAAA;AAAA,EACxC;AAEA,EAAA,IAAI,iBAAmBA,YAAA,CAAA,QAAA,EAAU;AAC/B,IAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAK,CAAA,CAAE,KAAK,CAAA;AAAA,EAClD;AAEA,EAAA,OAAO,KAAA;AACT;AAgDO,SAAS,cACd,KAAA,EACoB;AACpB,EAAA,IAAI,iBAAmBA,YAAA,CAAA,UAAA,EAAY;AAEjC,IAAA,OAAO,MAAM,MAAA,EAAO;AAAA,EACtB;AAEA,EAAA,IAAI,eAAe,KAAA,CAAM,GAAA,IAAO,KAAA,CAAM,GAAA,CAAI,qBAAuBA,YAAA,CAAA,OAAA,EAAS;AACxE,IAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,GAAA,CAAI,SAAS,CAAA;AAE/C,IAAA,IAAI,iBAAmBA,YAAA,CAAA,WAAA,EAAa;AAElC,MAAA,OAAO,MAAM,QAAA,EAAS;AAAA,IACxB;AACA,IAAA,IAAI,iBAAmBA,YAAA,CAAA,WAAA,EAAa;AAElC,MAAA,OAAO,MAAM,QAAA,EAAS;AAAA,IACxB;AAAA,EACF;AAGA,EAAA,OAAO,KAAA;AACT;AA+EO,IAAM,kBAAA,GAAqB,CAAsB,KAAA,KAAa;AACnE,EAAA,MAAM,mBAAA,GAAsB,cAAc,KAAK,CAAA;AAC/C,EAAA,IAAI,EAAE,+BAAiCA,YAAA,CAAA,OAAA,CAAA,EAAU;AAC/C,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,eAAA,GAAkB,mBAAA,CAAoB,SAAA,CAAU,MAAS,CAAA,CAAE,OAAA;AAGjE,EAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,SAAA,CAAU,IAAI,CAAA,CAAE,OAAA;AAEvD,EAAA,MAAM,aAAA,GAAgB,iBAAiB,mBAAmB,CAAA;AAE1D,EAAA,MAAM,oBACJ,aAAA,CAAc,IAAA,KAAS,YACvB,mBAAA,CAAoB,SAAA,CAAU,EAAE,CAAA,CAAE,OAAA;AAEpC,EAAA,MAAM,gBAAA,GACJ,cAAc,IAAA,KAAS,OAAA,IAAW,oBAAoB,SAAA,CAAU,EAAE,CAAA,CAAE,OAAA;AAEtE,EAAA,OACE,CAAC,eAAA,IAAmB,CAAC,UAAA,IAAc,CAAC,qBAAqB,CAAC,gBAAA;AAE9D;AAiHO,SAAS,eACd,KAAA,EACsB;AA/exB,EAAA,IAAA,EAAA;AAgfE,EAAA,MAAM,aAAA,GAAgB,iBAAiB,KAAK,CAAA;AAE5C,EAAA,OAAA,CAAA,CAAQ,EAAA,GAAA,aAAA,CAAc,GAAA,CAAI,MAAA,KAAlB,IAAA,GAAA,MAAA,GAAA,EAAA,CAA0B,GAAA,CAAI,CAAC,KAAA,KAAU,KAAA,CAAM,IAAA,CAAK,GAAA,CAAA,KAC1D,EAAC;AACL;;;ACxbO,SAAS,eACd,KAAA,EACwB;AACxB,EAAA,IAAI,iBAAmBC,YAAA,CAAA,UAAA,EAAY;AAEjC,IAAA,OAAO,MAAM,GAAA,CAAI,YAAA;AAAA,EACnB;AAEA,EAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AAEpB,IAAA,OAAO,cAAA,CAAe,KAAA,CAAM,MAAA,EAAQ,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,iBAAmBA,YAAA,CAAA,QAAA,EAAU;AAE/B,IAAA,OAAO,cAAA,CAAe,WAAA,CAAY,KAAK,CAAA,CAAE,KAAK,CAAA;AAAA,EAChD;AAEA,EAAA,OAAO,MAAA;AACT;AA4DO,SAAS,kBACd,MAAA,EAC+B;AAC/B,EAAA,MAAM,WAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,GAAA,IAAO,OAAO,KAAA,EAAO;AAC9B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA;AAC9B,IAAA,IAAI,CAAC,KAAA,EAAO;AAGZ,IAAA,MAAM,YAAA,GAAe,eAAe,KAAK,CAAA;AACzC,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,YAAA;AAAA,IAClB;AAAA,EACF;AAGA,EAAA,OAAO,QAAA;AACT","file":"index.js","sourcesContent":["import * as z from 'zod';\nimport type {\n $ZodCheckBigIntFormatDef,\n $ZodCheckEndsWithDef,\n $ZodCheckGreaterThanDef,\n $ZodCheckIncludesDef,\n $ZodCheckLengthEqualsDef,\n $ZodCheckLessThanDef,\n $ZodCheckLowerCaseDef,\n $ZodCheckMaxLengthDef,\n $ZodCheckMaxSizeDef,\n $ZodCheckMimeTypeDef,\n $ZodCheckMinLengthDef,\n $ZodCheckMinSizeDef,\n $ZodCheckMultipleOfDef,\n $ZodCheckNumberFormatDef,\n $ZodCheckOverwriteDef,\n $ZodCheckPropertyDef,\n $ZodCheckRegexDef,\n $ZodCheckSizeEqualsDef,\n $ZodCheckStartsWithDef,\n $ZodCheckStringFormatDef,\n $ZodCheckUpperCaseDef,\n} from 'zod/v4/core';\n\n/**\n * Type representing a Zod type that has an unwrap method\n */\ntype Unwrappable = { unwrap: () => z.ZodTypeAny };\n\n/**\n * Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).\n *\n * This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types\n * like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.\n *\n * @param field - The Zod field to check\n * @returns True if the field has an unwrap method, false otherwise\n *\n * @example\n * ```typescript\n * const optionalField = z.string().optional();\n * console.log(canUnwrap(optionalField)); // true\n *\n * const plainField = z.string();\n * console.log(canUnwrap(plainField)); // false\n * ```\n *\n * @since 0.1.0\n */\nexport function canUnwrap(\n field: z.ZodTypeAny,\n): field is z.ZodTypeAny & Unwrappable {\n return 'unwrap' in field && typeof field.unwrap === 'function';\n}\n\n/**\n * Unwraps a ZodUnion type and returns the first field and all union options.\n *\n * This function extracts the individual type options from a union type.\n * By default, it filters out `ZodNull` and `ZodUndefined` types, returning only\n * the meaningful type options. You can disable this filtering to get all options.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field (union or single type)\n * @param options - Configuration options\n * @param options.filterNullish - Whether to filter out null and undefined types (default: true)\n * @returns Object with `field` (first option) and `union` (all options array)\n *\n * @example\n * Basic union unwrapping\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string(), z.number()] }\n * ```\n *\n * @example\n * Union with null (filtered by default)\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] }\n * ```\n *\n * @example\n * Union with null (keep all options)\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const result = unwrapUnion(field, { filterNullish: false });\n * // Result: { field: z.string(), union: [z.string(), z.null()] }\n * ```\n *\n * @example\n * Non-union type (returns single field)\n * ```typescript\n * const field = z.string();\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] }\n * ```\n *\n * @example\n * Nullable as union\n * ```typescript\n * const field = z.string().nullable(); // This is z.union([z.string(), z.null()])\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] } (null filtered out)\n * ```\n *\n * @example\n * Using the first field for type checking\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const { field: firstField, union } = unwrapUnion(field);\n * if (firstField instanceof z.ZodString) {\n * console.log('First type is string');\n * }\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.1.0\n */\nexport function unwrapUnion<T extends z.ZodTypeAny>(\n field: T,\n options: { filterNullish?: boolean } = {},\n): { field: z.ZodTypeAny; union: z.ZodTypeAny[] } {\n const { filterNullish = true } = options;\n\n if (field instanceof z.ZodUnion) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const unionOptions = [...field.def.options] as z.ZodTypeAny[];\n\n const filteredOptions = filterNullish\n ? unionOptions.filter(\n (option) =>\n !(option instanceof z.ZodNull) &&\n !(option instanceof z.ZodUndefined),\n )\n : unionOptions;\n\n return {\n field: filteredOptions[0] || field,\n union: filteredOptions,\n };\n }\n\n // If it's not a union, return the field itself\n return {\n field,\n union: [field],\n };\n}\n\n/**\n * Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.\n *\n * This function removes wrapper layers (optional, nullable, default) to reveal the base type.\n * **Important:** It stops at array types without unwrapping them, treating arrays as primitives.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field to unwrap\n * @returns The unwrapped primitive Zod type\n *\n * @example\n * Unwrapping to string primitive\n * ```typescript\n * const field = z.string().optional().nullable();\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (unwrapped all wrappers)\n * ```\n *\n * @example\n * Stopping at array type\n * ```typescript\n * const field = z.array(z.string()).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.array(z.string()) (stops at array, doesn't unwrap it)\n * ```\n *\n * @example\n * Unwrapping defaults\n * ```typescript\n * const field = z.number().default(0).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.number()\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @since 0.1.0\n */\nexport const getPrimitiveType = <T extends z.ZodType>(\n field: T,\n): z.ZodTypeAny => {\n // Stop at arrays - don't unwrap them\n if (field instanceof z.ZodArray) {\n return field;\n }\n\n if (canUnwrap(field)) {\n return getPrimitiveType(field.unwrap());\n }\n\n if (field instanceof z.ZodUnion) {\n return getPrimitiveType(unwrapUnion(field).field);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { canUnwrap, unwrapUnion } from './schema';\nimport type { Simplify } from './types';\n\n/**\n * Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.\n *\n * This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`, `ZodUnion`) to find\n * the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.\n *\n * **Union handling:** For union types, extracts the default from the first option. If the first option\n * has no default, returns `undefined` (defaults in other union options are not checked).\n *\n * @template T - The Zod type to extract default from\n * @param field - The Zod field to extract default from\n * @returns The default value if present, undefined otherwise\n *\n * @example\n * Basic usage with default value\n * ```typescript\n * const field = z.string().default('hello');\n * const defaultValue = extractDefault(field);\n * // Result: 'hello'\n * ```\n *\n * @example\n * Unwrapping optional/nullable layers\n * ```typescript\n * const field = z.string().default('world').optional();\n * const defaultValue = extractDefault(field);\n * // Result: 'world' (unwraps optional to find default)\n * ```\n *\n * @example\n * Union with default in first option\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (extracts from first union option)\n * ```\n *\n * @example\n * Union with default in second option\n * ```typescript\n * const field = z.union([z.string(), z.number().default(42)]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (only checks first option)\n * ```\n *\n * @example\n * Field without default\n * ```typescript\n * const field = z.string().optional();\n * const defaultValue = extractDefault(field);\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for extracting defaults from entire schemas\n * @since 0.1.0\n */\nexport function extractDefault<T extends z.ZodTypeAny>(\n field: T,\n): z.infer<T> | undefined {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.def.defaultValue as z.infer<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(field.unwrap()) as z.infer<T>;\n }\n\n if (field instanceof z.ZodUnion) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapUnion(field).field) as z.infer<T>;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema while skipping fields without defaults.\n *\n * This function recursively traverses the schema and collects all fields that have\n * explicit default values defined. Fields without defaults are excluded from the result.\n *\n * **Important:** Nested defaults are NOT extracted unless the parent object also has\n * an explicit `.default()`. This is by design to match Zod's default value behavior.\n *\n * @template T - The Zod object schema type\n * @param schema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with default values\n *\n * @example\n * Basic usage\n * ```typescript\n * const schema = z.object({\n * name: z.string().default('John'),\n * age: z.number(), // no default - will be skipped\n * email: z.string().email().optional(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { name: 'John' }\n * ```\n *\n * @example\n * Nested objects with defaults\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string().default('Guest')\n * }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()\n *\n * settings: z.object({\n * theme: z.string().default('light')\n * }), // ❌ NOT extracted - parent has no .default()\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { user: { name: 'Guest' } }\n * ```\n *\n * @example\n * Unwrapping optional/nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string().default('Untitled').optional(),\n * count: z.number().default(0).nullable(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<T extends z.ZodObject>(\n schema: T,\n): Simplify<Partial<z.infer<T>>> {\n const defaults: Record<string, unknown> = {};\n\n for (const key in schema.shape) {\n const field = schema.shape[key];\n if (!field) continue;\n\n // Check if this field has an explicit default value\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<T>>;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,23 @@ import * as z from 'zod';
|
|
|
4
4
|
function canUnwrap(field) {
|
|
5
5
|
return "unwrap" in field && typeof field.unwrap === "function";
|
|
6
6
|
}
|
|
7
|
+
function unwrapUnion(field, options = {}) {
|
|
8
|
+
const { filterNullish = true } = options;
|
|
9
|
+
if (field instanceof z.ZodUnion) {
|
|
10
|
+
const unionOptions = [...field.def.options];
|
|
11
|
+
const filteredOptions = filterNullish ? unionOptions.filter(
|
|
12
|
+
(option) => !(option instanceof z.ZodNull) && !(option instanceof z.ZodUndefined)
|
|
13
|
+
) : unionOptions;
|
|
14
|
+
return {
|
|
15
|
+
field: filteredOptions[0] || field,
|
|
16
|
+
union: filteredOptions
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
field,
|
|
21
|
+
union: [field]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
7
24
|
var getPrimitiveType = (field) => {
|
|
8
25
|
if (field instanceof z.ZodArray) {
|
|
9
26
|
return field;
|
|
@@ -11,6 +28,9 @@ var getPrimitiveType = (field) => {
|
|
|
11
28
|
if (canUnwrap(field)) {
|
|
12
29
|
return getPrimitiveType(field.unwrap());
|
|
13
30
|
}
|
|
31
|
+
if (field instanceof z.ZodUnion) {
|
|
32
|
+
return getPrimitiveType(unwrapUnion(field).field);
|
|
33
|
+
}
|
|
14
34
|
return field;
|
|
15
35
|
};
|
|
16
36
|
function removeDefault(field) {
|
|
@@ -28,21 +48,23 @@ function removeDefault(field) {
|
|
|
28
48
|
}
|
|
29
49
|
return field;
|
|
30
50
|
}
|
|
31
|
-
var
|
|
32
|
-
const undefinedResult = field.safeParse(void 0).success;
|
|
33
|
-
if (undefinedResult) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
51
|
+
var requiresValidInput = (field) => {
|
|
36
52
|
const defaultRemovedField = removeDefault(field);
|
|
37
53
|
if (!(defaultRemovedField instanceof z.ZodType)) {
|
|
38
54
|
return false;
|
|
39
55
|
}
|
|
56
|
+
const undefinedResult = defaultRemovedField.safeParse(void 0).success;
|
|
40
57
|
const nullResult = defaultRemovedField.safeParse(null).success;
|
|
41
58
|
const primitiveType = getPrimitiveType(defaultRemovedField);
|
|
42
59
|
const emptyStringResult = primitiveType.type === "string" && defaultRemovedField.safeParse("").success;
|
|
43
60
|
const emptyArrayResult = primitiveType.type === "array" && defaultRemovedField.safeParse([]).success;
|
|
44
61
|
return !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult;
|
|
45
62
|
};
|
|
63
|
+
function getFieldChecks(field) {
|
|
64
|
+
var _a;
|
|
65
|
+
const primitiveType = getPrimitiveType(field);
|
|
66
|
+
return ((_a = primitiveType.def.checks) == null ? void 0 : _a.map((check) => check._zod.def)) || [];
|
|
67
|
+
}
|
|
46
68
|
|
|
47
69
|
// src/defaults.ts
|
|
48
70
|
function extractDefault(field) {
|
|
@@ -52,6 +74,9 @@ function extractDefault(field) {
|
|
|
52
74
|
if (canUnwrap(field)) {
|
|
53
75
|
return extractDefault(field.unwrap());
|
|
54
76
|
}
|
|
77
|
+
if (field instanceof z.ZodUnion) {
|
|
78
|
+
return extractDefault(unwrapUnion(field).field);
|
|
79
|
+
}
|
|
55
80
|
return void 0;
|
|
56
81
|
}
|
|
57
82
|
function getSchemaDefaults(schema) {
|
|
@@ -67,6 +92,6 @@ function getSchemaDefaults(schema) {
|
|
|
67
92
|
return defaults;
|
|
68
93
|
}
|
|
69
94
|
|
|
70
|
-
export { canUnwrap,
|
|
95
|
+
export { canUnwrap, extractDefault, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, unwrapUnion };
|
|
71
96
|
//# sourceMappingURL=index.mjs.map
|
|
72
97
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schema.ts","../src/defaults.ts"],"names":["z2"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as z from 'zod';\n\n/**\n * Type representing a Zod type that has an unwrap method\n */\ntype Unwrappable = { unwrap: () => z.ZodTypeAny };\n\n/**\n * Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).\n *\n * This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types\n * like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.\n *\n * @param field - The Zod field to check\n * @returns True if the field has an unwrap method, false otherwise\n *\n * @example\n * ```typescript\n * const optionalField = z.string().optional();\n * console.log(canUnwrap(optionalField)); // true\n *\n * const plainField = z.string();\n * console.log(canUnwrap(plainField)); // false\n * ```\n *\n * @since 0.1.0\n */\nexport function canUnwrap(\n field: z.ZodTypeAny,\n): field is z.ZodTypeAny & Unwrappable {\n return 'unwrap' in field && typeof field.unwrap === 'function';\n}\n\n/**\n * Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.\n *\n * This function removes wrapper layers (optional, nullable, default) to reveal the base type.\n * **Important:** It stops at array types without unwrapping them, treating arrays as primitives.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field to unwrap\n * @returns The unwrapped primitive Zod type\n *\n * @example\n * Unwrapping to string primitive\n * ```typescript\n * const field = z.string().optional().nullable();\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (unwrapped all wrappers)\n * ```\n *\n * @example\n * Stopping at array type\n * ```typescript\n * const field = z.array(z.string()).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.array(z.string()) (stops at array, doesn't unwrap it)\n * ```\n *\n * @example\n * Unwrapping defaults\n * ```typescript\n * const field = z.number().default(0).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.number()\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @since 0.1.0\n */\nexport const getPrimitiveType = <T extends z.ZodTypeAny>(\n field: T,\n): z.ZodTypeAny => {\n // Stop at arrays - don't unwrap them\n if (field instanceof z.ZodArray) {\n return field;\n }\n\n if (canUnwrap(field)) {\n return getPrimitiveType(field.unwrap());\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link checkIfFieldIsRequired} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Checks if a Zod field is truly required by testing multiple acceptance criteria.\n *\n * A field is considered **not required** if it accepts any of the following:\n * - `undefined` (via `.optional()` or `.default()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n *\n * **Note:** Fields with `.default()` are considered not required since they'll have a value\n * even if the user doesn't provide one.\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check for required status\n * @returns True if the field is required, false otherwise\n *\n * @example\n * Required field\n * ```typescript\n * const field = z.string().min(1);\n * console.log(checkIfFieldIsRequired(field)); // true\n * ```\n *\n * @example\n * Optional field (not required)\n * ```typescript\n * const field = z.string().optional();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * Field with default (not required)\n * ```typescript\n * const field = z.string().default('hello');\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * String without min length (not required - accepts empty string)\n * ```typescript\n * const field = z.string();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @example\n * String with nonempty (required)\n * ```typescript\n * const field = z.string().nonempty();\n * console.log(checkIfFieldIsRequired(field)); // true\n * ```\n *\n * @example\n * Nullable field (not required)\n * ```typescript\n * const field = z.number().nullable();\n * console.log(checkIfFieldIsRequired(field)); // false\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const checkIfFieldIsRequired = <T extends z.ZodType>(field: T) => {\n // First check the original field for undefined - this catches fields with defaults\n const undefinedResult = field.safeParse(undefined).success;\n if (undefinedResult) {\n return false;\n }\n\n const defaultRemovedField = removeDefault(field);\n\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n","import * as z from 'zod';\nimport { canUnwrap } from './schema';\nimport type { Simplify } from './types';\n\n/**\n * Extracts the default value from a Zod field, recursively unwrapping optional and nullable layers.\n *\n * This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find\n * the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.\n *\n * @template T - The Zod type to extract default from\n * @param field - The Zod field to extract default from\n * @returns The default value if present, undefined otherwise\n *\n * @example\n * Basic usage with default value\n * ```typescript\n * const field = z.string().default('hello');\n * const defaultValue = extractDefault(field);\n * // Result: 'hello'\n * ```\n *\n * @example\n * Unwrapping optional/nullable layers\n * ```typescript\n * const field = z.string().default('world').optional();\n * const defaultValue = extractDefault(field);\n * // Result: 'world' (unwraps optional to find default)\n * ```\n *\n * @example\n * Field without default\n * ```typescript\n * const field = z.string().optional();\n * const defaultValue = extractDefault(field);\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for extracting defaults from entire schemas\n * @since 0.1.0\n */\nexport function extractDefault<T extends z.ZodTypeAny>(\n field: T,\n): z.infer<T> | undefined {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.def.defaultValue as z.infer<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(field.unwrap()) as z.infer<T>;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema while skipping fields without defaults.\n *\n * This function recursively traverses the schema and collects all fields that have\n * explicit default values defined. Fields without defaults are excluded from the result.\n *\n * **Important:** Nested defaults are NOT extracted unless the parent object also has\n * an explicit `.default()`. This is by design to match Zod's default value behavior.\n *\n * @template T - The Zod object schema type\n * @param schema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with default values\n *\n * @example\n * Basic usage\n * ```typescript\n * const schema = z.object({\n * name: z.string().default('John'),\n * age: z.number(), // no default - will be skipped\n * email: z.string().email().optional(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { name: 'John' }\n * ```\n *\n * @example\n * Nested objects with defaults\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string().default('Guest')\n * }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()\n *\n * settings: z.object({\n * theme: z.string().default('light')\n * }), // ❌ NOT extracted - parent has no .default()\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { user: { name: 'Guest' } }\n * ```\n *\n * @example\n * Unwrapping optional/nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string().default('Untitled').optional(),\n * count: z.number().default(0).nullable(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<T extends z.ZodObject>(\n schema: T,\n): Simplify<Partial<z.infer<T>>> {\n const defaults: Record<string, unknown> = {};\n\n for (const key in schema.shape) {\n const field = schema.shape[key];\n if (!field) continue;\n\n // Check if this field has an explicit default value\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<T>>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts","../src/defaults.ts"],"names":["z2"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as z from 'zod';\nimport type {\n $ZodCheckBigIntFormatDef,\n $ZodCheckEndsWithDef,\n $ZodCheckGreaterThanDef,\n $ZodCheckIncludesDef,\n $ZodCheckLengthEqualsDef,\n $ZodCheckLessThanDef,\n $ZodCheckLowerCaseDef,\n $ZodCheckMaxLengthDef,\n $ZodCheckMaxSizeDef,\n $ZodCheckMimeTypeDef,\n $ZodCheckMinLengthDef,\n $ZodCheckMinSizeDef,\n $ZodCheckMultipleOfDef,\n $ZodCheckNumberFormatDef,\n $ZodCheckOverwriteDef,\n $ZodCheckPropertyDef,\n $ZodCheckRegexDef,\n $ZodCheckSizeEqualsDef,\n $ZodCheckStartsWithDef,\n $ZodCheckStringFormatDef,\n $ZodCheckUpperCaseDef,\n} from 'zod/v4/core';\n\n/**\n * Type representing a Zod type that has an unwrap method\n */\ntype Unwrappable = { unwrap: () => z.ZodTypeAny };\n\n/**\n * Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).\n *\n * This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types\n * like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.\n *\n * @param field - The Zod field to check\n * @returns True if the field has an unwrap method, false otherwise\n *\n * @example\n * ```typescript\n * const optionalField = z.string().optional();\n * console.log(canUnwrap(optionalField)); // true\n *\n * const plainField = z.string();\n * console.log(canUnwrap(plainField)); // false\n * ```\n *\n * @since 0.1.0\n */\nexport function canUnwrap(\n field: z.ZodTypeAny,\n): field is z.ZodTypeAny & Unwrappable {\n return 'unwrap' in field && typeof field.unwrap === 'function';\n}\n\n/**\n * Unwraps a ZodUnion type and returns the first field and all union options.\n *\n * This function extracts the individual type options from a union type.\n * By default, it filters out `ZodNull` and `ZodUndefined` types, returning only\n * the meaningful type options. You can disable this filtering to get all options.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field (union or single type)\n * @param options - Configuration options\n * @param options.filterNullish - Whether to filter out null and undefined types (default: true)\n * @returns Object with `field` (first option) and `union` (all options array)\n *\n * @example\n * Basic union unwrapping\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string(), z.number()] }\n * ```\n *\n * @example\n * Union with null (filtered by default)\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] }\n * ```\n *\n * @example\n * Union with null (keep all options)\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const result = unwrapUnion(field, { filterNullish: false });\n * // Result: { field: z.string(), union: [z.string(), z.null()] }\n * ```\n *\n * @example\n * Non-union type (returns single field)\n * ```typescript\n * const field = z.string();\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] }\n * ```\n *\n * @example\n * Nullable as union\n * ```typescript\n * const field = z.string().nullable(); // This is z.union([z.string(), z.null()])\n * const result = unwrapUnion(field);\n * // Result: { field: z.string(), union: [z.string()] } (null filtered out)\n * ```\n *\n * @example\n * Using the first field for type checking\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const { field: firstField, union } = unwrapUnion(field);\n * if (firstField instanceof z.ZodString) {\n * console.log('First type is string');\n * }\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.1.0\n */\nexport function unwrapUnion<T extends z.ZodTypeAny>(\n field: T,\n options: { filterNullish?: boolean } = {},\n): { field: z.ZodTypeAny; union: z.ZodTypeAny[] } {\n const { filterNullish = true } = options;\n\n if (field instanceof z.ZodUnion) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const unionOptions = [...field.def.options] as z.ZodTypeAny[];\n\n const filteredOptions = filterNullish\n ? unionOptions.filter(\n (option) =>\n !(option instanceof z.ZodNull) &&\n !(option instanceof z.ZodUndefined),\n )\n : unionOptions;\n\n return {\n field: filteredOptions[0] || field,\n union: filteredOptions,\n };\n }\n\n // If it's not a union, return the field itself\n return {\n field,\n union: [field],\n };\n}\n\n/**\n * Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.\n *\n * This function removes wrapper layers (optional, nullable, default) to reveal the base type.\n * **Important:** It stops at array types without unwrapping them, treating arrays as primitives.\n *\n * @template T - The Zod type to unwrap\n * @param field - The Zod field to unwrap\n * @returns The unwrapped primitive Zod type\n *\n * @example\n * Unwrapping to string primitive\n * ```typescript\n * const field = z.string().optional().nullable();\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (unwrapped all wrappers)\n * ```\n *\n * @example\n * Stopping at array type\n * ```typescript\n * const field = z.array(z.string()).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.array(z.string()) (stops at array, doesn't unwrap it)\n * ```\n *\n * @example\n * Unwrapping defaults\n * ```typescript\n * const field = z.number().default(0).optional();\n * const primitive = getPrimitiveType(field);\n * // Result: z.number()\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @since 0.1.0\n */\nexport const getPrimitiveType = <T extends z.ZodType>(\n field: T,\n): z.ZodTypeAny => {\n // Stop at arrays - don't unwrap them\n if (field instanceof z.ZodArray) {\n return field;\n }\n\n if (canUnwrap(field)) {\n return getPrimitiveType(field.unwrap());\n }\n\n if (field instanceof z.ZodUnion) {\n return getPrimitiveType(unwrapUnion(field).field);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { canUnwrap, unwrapUnion } from './schema';\nimport type { Simplify } from './types';\n\n/**\n * Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.\n *\n * This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`, `ZodUnion`) to find\n * the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.\n *\n * **Union handling:** For union types, extracts the default from the first option. If the first option\n * has no default, returns `undefined` (defaults in other union options are not checked).\n *\n * @template T - The Zod type to extract default from\n * @param field - The Zod field to extract default from\n * @returns The default value if present, undefined otherwise\n *\n * @example\n * Basic usage with default value\n * ```typescript\n * const field = z.string().default('hello');\n * const defaultValue = extractDefault(field);\n * // Result: 'hello'\n * ```\n *\n * @example\n * Unwrapping optional/nullable layers\n * ```typescript\n * const field = z.string().default('world').optional();\n * const defaultValue = extractDefault(field);\n * // Result: 'world' (unwraps optional to find default)\n * ```\n *\n * @example\n * Union with default in first option\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (extracts from first union option)\n * ```\n *\n * @example\n * Union with default in second option\n * ```typescript\n * const field = z.union([z.string(), z.number().default(42)]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (only checks first option)\n * ```\n *\n * @example\n * Field without default\n * ```typescript\n * const field = z.string().optional();\n * const defaultValue = extractDefault(field);\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for extracting defaults from entire schemas\n * @since 0.1.0\n */\nexport function extractDefault<T extends z.ZodTypeAny>(\n field: T,\n): z.infer<T> | undefined {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.def.defaultValue as z.infer<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(field.unwrap()) as z.infer<T>;\n }\n\n if (field instanceof z.ZodUnion) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapUnion(field).field) as z.infer<T>;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema while skipping fields without defaults.\n *\n * This function recursively traverses the schema and collects all fields that have\n * explicit default values defined. Fields without defaults are excluded from the result.\n *\n * **Important:** Nested defaults are NOT extracted unless the parent object also has\n * an explicit `.default()`. This is by design to match Zod's default value behavior.\n *\n * @template T - The Zod object schema type\n * @param schema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with default values\n *\n * @example\n * Basic usage\n * ```typescript\n * const schema = z.object({\n * name: z.string().default('John'),\n * age: z.number(), // no default - will be skipped\n * email: z.string().email().optional(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { name: 'John' }\n * ```\n *\n * @example\n * Nested objects with defaults\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string().default('Guest')\n * }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()\n *\n * settings: z.object({\n * theme: z.string().default('light')\n * }), // ❌ NOT extracted - parent has no .default()\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { user: { name: 'Guest' } }\n * ```\n *\n * @example\n * Unwrapping optional/nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string().default('Untitled').optional(),\n * count: z.number().default(0).nullable(),\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<T extends z.ZodObject>(\n schema: T,\n): Simplify<Partial<z.infer<T>>> {\n const defaults: Record<string, unknown> = {};\n\n for (const key in schema.shape) {\n const field = schema.shape[key];\n if (!field) continue;\n\n // Check if this field has an explicit default value\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<T>>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zod-utils/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Pure TypeScript utilities for Zod schema manipulation and default extraction",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"test": "vitest run",
|
|
29
29
|
"test:watch": "vitest",
|
|
30
30
|
"test:coverage": "vitest run --coverage",
|
|
31
|
+
"bench": "vitest bench --run",
|
|
32
|
+
"bench:watch": "vitest bench",
|
|
31
33
|
"prepublishOnly": "npm run build"
|
|
32
34
|
},
|
|
33
35
|
"keywords": [
|