@zod-utils/core 0.1.0 → 0.4.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 +93 -80
- package/dist/index.d.mts +371 -49
- package/dist/index.d.ts +371 -49
- package/dist/index.js +47 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,79 +1,401 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
* @
|
|
22
|
-
*
|
|
4
|
+
* Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
|
|
5
|
+
*
|
|
6
|
+
* This utility type flattens intersections and complex type expressions into a single,
|
|
7
|
+
* readable object type. This is especially useful when working with mapped types,
|
|
8
|
+
* conditional types, or type intersections that produce hard-to-read IDE hints.
|
|
9
|
+
*
|
|
10
|
+
* @template T - The type to simplify
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* Simplifying intersection types
|
|
14
|
+
* ```typescript
|
|
15
|
+
* type A = { name: string };
|
|
16
|
+
* type B = { age: number };
|
|
17
|
+
* type C = A & B; // Shows as "A & B" in IDE
|
|
18
|
+
* type D = Simplify<A & B>; // Shows as "{ name: string; age: number }" in IDE
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* Simplifying Partial<> results
|
|
23
|
+
* ```typescript
|
|
24
|
+
* type User = { name: string; age: number; email: string };
|
|
25
|
+
* type PartialUser = Partial<User>; // Shows as "Partial<User>" in IDE
|
|
26
|
+
* type SimplifiedPartialUser = Simplify<Partial<User>>;
|
|
27
|
+
* // Shows as "{ name?: string; age?: number; email?: string }" in IDE
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* Usage with zod schema inference
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const schema = z.object({ id: z.string() })
|
|
34
|
+
* .merge(z.object({ name: z.string() }));
|
|
35
|
+
*
|
|
36
|
+
* type InferredType = z.infer<typeof schema>; // May show complex type
|
|
37
|
+
* type SimplifiedType = Simplify<z.infer<typeof schema>>;
|
|
38
|
+
* // Shows clear: { id: string; name: string }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @since 0.1.0
|
|
23
42
|
*/
|
|
24
|
-
|
|
43
|
+
type Simplify<T> = {
|
|
44
|
+
[K in keyof T]: T[K];
|
|
45
|
+
} & {};
|
|
25
46
|
|
|
26
47
|
/**
|
|
27
|
-
*
|
|
48
|
+
* Extracts the default value from a Zod field, recursively unwrapping optional and nullable layers.
|
|
49
|
+
*
|
|
50
|
+
* This function traverses through wrapper types (like `ZodOptional`, `ZodNullable`) to find
|
|
51
|
+
* the underlying `ZodDefault` and returns its default value. If no default is found, returns `undefined`.
|
|
52
|
+
*
|
|
53
|
+
* @template T - The Zod type to extract default from
|
|
28
54
|
* @param field - The Zod field to extract default from
|
|
29
55
|
* @returns The default value if present, undefined otherwise
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* Basic usage with default value
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const field = z.string().default('hello');
|
|
61
|
+
* const defaultValue = extractDefault(field);
|
|
62
|
+
* // Result: 'hello'
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* Unwrapping optional/nullable layers
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const field = z.string().default('world').optional();
|
|
69
|
+
* const defaultValue = extractDefault(field);
|
|
70
|
+
* // Result: 'world' (unwraps optional to find default)
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* Field without default
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const field = z.string().optional();
|
|
77
|
+
* const defaultValue = extractDefault(field);
|
|
78
|
+
* // Result: undefined
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @see {@link getSchemaDefaults} for extracting defaults from entire schemas
|
|
82
|
+
* @since 0.1.0
|
|
30
83
|
*/
|
|
31
|
-
declare function extractDefault(field:
|
|
32
|
-
/**
|
|
33
|
-
* Get the unwrapped type without going through defaults
|
|
34
|
-
* Useful for detecting nested objects/arrays while preserving defaults
|
|
35
|
-
* @param field - The Zod field to unwrap
|
|
36
|
-
* @returns The unwrapped type
|
|
37
|
-
*/
|
|
38
|
-
declare function getUnwrappedType(field: z.ZodTypeAny): z.ZodTypeAny;
|
|
84
|
+
declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> | undefined;
|
|
39
85
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
86
|
+
* Extracts default values from a Zod object schema while skipping fields without defaults.
|
|
87
|
+
*
|
|
88
|
+
* This function recursively traverses the schema and collects all fields that have
|
|
89
|
+
* explicit default values defined. Fields without defaults are excluded from the result.
|
|
90
|
+
*
|
|
91
|
+
* **Important:** Nested defaults are NOT extracted unless the parent object also has
|
|
92
|
+
* an explicit `.default()`. This is by design to match Zod's default value behavior.
|
|
93
|
+
*
|
|
94
|
+
* @template T - The Zod object schema type
|
|
42
95
|
* @param schema - The Zod object schema to extract defaults from
|
|
43
|
-
* @returns
|
|
96
|
+
* @returns A partial object containing only fields with default values
|
|
44
97
|
*
|
|
45
98
|
* @example
|
|
46
|
-
*
|
|
99
|
+
* Basic usage
|
|
100
|
+
* ```typescript
|
|
47
101
|
* const schema = z.object({
|
|
48
102
|
* name: z.string().default('John'),
|
|
49
|
-
* age: z.number(), // no default - skipped
|
|
103
|
+
* age: z.number(), // no default - will be skipped
|
|
104
|
+
* email: z.string().email().optional(),
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* const defaults = getSchemaDefaults(schema);
|
|
108
|
+
* // Result: { name: 'John' }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* Nested objects with defaults
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const schema = z.object({
|
|
115
|
+
* user: z.object({
|
|
116
|
+
* name: z.string().default('Guest')
|
|
117
|
+
* }).default({ name: 'Guest' }), // ✅ Extracted because parent has .default()
|
|
118
|
+
*
|
|
50
119
|
* settings: z.object({
|
|
51
120
|
* theme: z.string().default('light')
|
|
52
|
-
* })
|
|
121
|
+
* }), // ❌ NOT extracted - parent has no .default()
|
|
53
122
|
* });
|
|
54
123
|
*
|
|
55
|
-
* getSchemaDefaults(schema);
|
|
56
|
-
* //
|
|
124
|
+
* const defaults = getSchemaDefaults(schema);
|
|
125
|
+
* // Result: { user: { name: 'Guest' } }
|
|
57
126
|
* ```
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* Unwrapping optional/nullable fields
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const schema = z.object({
|
|
132
|
+
* title: z.string().default('Untitled').optional(),
|
|
133
|
+
* count: z.number().default(0).nullable(),
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* const defaults = getSchemaDefaults(schema);
|
|
137
|
+
* // Result: { title: 'Untitled', count: 0 }
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* @see {@link extractDefault} for extracting defaults from individual fields
|
|
141
|
+
* @since 0.1.0
|
|
58
142
|
*/
|
|
59
|
-
declare function getSchemaDefaults<T extends z.ZodObject
|
|
143
|
+
declare function getSchemaDefaults<T extends z.ZodObject>(schema: T): Simplify<Partial<z.infer<T>>>;
|
|
60
144
|
|
|
61
145
|
/**
|
|
62
|
-
*
|
|
146
|
+
* Type representing a Zod type that has an unwrap method
|
|
63
147
|
*/
|
|
64
|
-
type
|
|
148
|
+
type Unwrappable = {
|
|
149
|
+
unwrap: () => z.ZodTypeAny;
|
|
150
|
+
};
|
|
65
151
|
/**
|
|
66
|
-
*
|
|
152
|
+
* Type guard to check if a Zod field can be unwrapped (has wrapper types like optional, nullable, default).
|
|
153
|
+
*
|
|
154
|
+
* This checks whether a Zod type has an `unwrap()` method, which is present on wrapper types
|
|
155
|
+
* like `ZodOptional`, `ZodNullable`, `ZodDefault`, and others.
|
|
156
|
+
*
|
|
157
|
+
* @param field - The Zod field to check
|
|
158
|
+
* @returns True if the field has an unwrap method, false otherwise
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const optionalField = z.string().optional();
|
|
163
|
+
* console.log(canUnwrap(optionalField)); // true
|
|
164
|
+
*
|
|
165
|
+
* const plainField = z.string();
|
|
166
|
+
* console.log(canUnwrap(plainField)); // false
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @since 0.1.0
|
|
67
170
|
*/
|
|
68
|
-
|
|
69
|
-
[K in keyof T]: T[K];
|
|
70
|
-
} & {};
|
|
171
|
+
declare function canUnwrap(field: z.ZodTypeAny): field is z.ZodTypeAny & Unwrappable;
|
|
71
172
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
173
|
+
* Unwraps a ZodUnion type and returns the first field and all union options.
|
|
174
|
+
*
|
|
175
|
+
* This function extracts the individual type options from a union type.
|
|
176
|
+
* By default, it filters out `ZodNull` and `ZodUndefined` types, returning only
|
|
177
|
+
* the meaningful type options. You can disable this filtering to get all options.
|
|
178
|
+
*
|
|
179
|
+
* @template T - The Zod type to unwrap
|
|
180
|
+
* @param field - The Zod field (union or single type)
|
|
181
|
+
* @param options - Configuration options
|
|
182
|
+
* @param options.filterNullish - Whether to filter out null and undefined types (default: true)
|
|
183
|
+
* @returns Object with `field` (first option) and `union` (all options array)
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* Basic union unwrapping
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const field = z.union([z.string(), z.number()]);
|
|
189
|
+
* const result = unwrapUnion(field);
|
|
190
|
+
* // Result: { field: z.string(), union: [z.string(), z.number()] }
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* Union with null (filtered by default)
|
|
195
|
+
* ```typescript
|
|
196
|
+
* const field = z.union([z.string(), z.null()]);
|
|
197
|
+
* const result = unwrapUnion(field);
|
|
198
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* Union with null (keep all options)
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const field = z.union([z.string(), z.null()]);
|
|
205
|
+
* const result = unwrapUnion(field, { filterNullish: false });
|
|
206
|
+
* // Result: { field: z.string(), union: [z.string(), z.null()] }
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* Non-union type (returns single field)
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const field = z.string();
|
|
213
|
+
* const result = unwrapUnion(field);
|
|
214
|
+
* // Result: { field: z.string(), union: [z.string()] }
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* Nullable as union
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const field = z.string().nullable(); // This is z.union([z.string(), z.null()])
|
|
221
|
+
* const result = unwrapUnion(field);
|
|
222
|
+
* // Result: { field: z.string(), union: [z.string()] } (null filtered out)
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* Using the first field for type checking
|
|
227
|
+
* ```typescript
|
|
228
|
+
* const field = z.union([z.string(), z.number()]);
|
|
229
|
+
* const { field: firstField, union } = unwrapUnion(field);
|
|
230
|
+
* if (firstField instanceof z.ZodString) {
|
|
231
|
+
* console.log('First type is string');
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*
|
|
235
|
+
* @see {@link getPrimitiveType} for unwrapping wrapper types
|
|
236
|
+
* @since 0.1.0
|
|
74
237
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
238
|
+
declare function unwrapUnion<T extends z.ZodTypeAny>(field: T, options?: {
|
|
239
|
+
filterNullish?: boolean;
|
|
240
|
+
}): {
|
|
241
|
+
field: z.ZodTypeAny;
|
|
242
|
+
union: z.ZodTypeAny[];
|
|
77
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* Gets the underlying primitive type of a Zod field by recursively unwrapping wrapper types.
|
|
246
|
+
*
|
|
247
|
+
* This function removes wrapper layers (optional, nullable, default) to reveal the base type.
|
|
248
|
+
* **Important:** It stops at array types without unwrapping them, treating arrays as primitives.
|
|
249
|
+
*
|
|
250
|
+
* @template T - The Zod type to unwrap
|
|
251
|
+
* @param field - The Zod field to unwrap
|
|
252
|
+
* @returns The unwrapped primitive Zod type
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* Unwrapping to string primitive
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const field = z.string().optional().nullable();
|
|
258
|
+
* const primitive = getPrimitiveType(field);
|
|
259
|
+
* // Result: z.string() (unwrapped all wrappers)
|
|
260
|
+
* ```
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* Stopping at array type
|
|
264
|
+
* ```typescript
|
|
265
|
+
* const field = z.array(z.string()).optional();
|
|
266
|
+
* const primitive = getPrimitiveType(field);
|
|
267
|
+
* // Result: z.array(z.string()) (stops at array, doesn't unwrap it)
|
|
268
|
+
* ```
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* Unwrapping defaults
|
|
272
|
+
* ```typescript
|
|
273
|
+
* const field = z.number().default(0).optional();
|
|
274
|
+
* const primitive = getPrimitiveType(field);
|
|
275
|
+
* // Result: z.number()
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @see {@link canUnwrap} for checking if a field can be unwrapped
|
|
279
|
+
* @since 0.1.0
|
|
280
|
+
*/
|
|
281
|
+
declare const getPrimitiveType: <T extends z.ZodType>(field: T) => z.ZodTypeAny;
|
|
282
|
+
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;
|
|
283
|
+
/**
|
|
284
|
+
* Removes default values from a Zod field while preserving other wrapper types.
|
|
285
|
+
*
|
|
286
|
+
* This function recursively removes `ZodDefault` wrappers from a field, while maintaining
|
|
287
|
+
* `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check
|
|
288
|
+
* field requirements without considering default values.
|
|
289
|
+
*
|
|
290
|
+
* @template T - The Zod type to process
|
|
291
|
+
* @param field - The Zod field to remove defaults from
|
|
292
|
+
* @returns The field without defaults but with optional/nullable preserved
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* Removing simple default
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const field = z.string().default('hello');
|
|
298
|
+
* const withoutDefault = removeDefault(field);
|
|
299
|
+
* // Result: z.string()
|
|
300
|
+
* ```
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* Preserving optional wrapper
|
|
304
|
+
* ```typescript
|
|
305
|
+
* const field = z.string().default('hello').optional();
|
|
306
|
+
* const withoutDefault = removeDefault(field);
|
|
307
|
+
* // Result: z.string().optional()
|
|
308
|
+
* ```
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* Nested defaults
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const field = z.string().default('inner').nullable().default('outer');
|
|
314
|
+
* const withoutDefault = removeDefault(field);
|
|
315
|
+
* // Result: z.string().nullable()
|
|
316
|
+
* ```
|
|
317
|
+
*
|
|
318
|
+
* @see {@link requiresValidInput} for usage with requirement checking
|
|
319
|
+
* @since 0.1.0
|
|
320
|
+
*/
|
|
321
|
+
declare function removeDefault<T extends z.ZodType>(field: T): StripZodDefault<T>;
|
|
322
|
+
/**
|
|
323
|
+
* Determines if a field will show validation errors when the user submits empty or invalid input.
|
|
324
|
+
*
|
|
325
|
+
* This is useful for form UIs to indicate which fields require valid user input (e.g., showing
|
|
326
|
+
* asterisks, validation states). The key insight: **defaults are just initial values** - they
|
|
327
|
+
* don't prevent validation errors if the user clears the field.
|
|
328
|
+
*
|
|
329
|
+
* **Real-world example:**
|
|
330
|
+
* ```typescript
|
|
331
|
+
* // Marital status field with default but validation rules
|
|
332
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
333
|
+
*
|
|
334
|
+
* // Initial: field shows "single" (from default)
|
|
335
|
+
* // User deletes the value → field is now empty string
|
|
336
|
+
* // User submits form → validation fails because .min(1) rejects empty strings
|
|
337
|
+
* // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)
|
|
338
|
+
* ```
|
|
339
|
+
*
|
|
340
|
+
* **How it works:**
|
|
341
|
+
* 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)
|
|
342
|
+
* 2. Tests if the underlying schema accepts empty/invalid input:
|
|
343
|
+
* - `undefined` (via `.optional()`)
|
|
344
|
+
* - `null` (via `.nullable()`)
|
|
345
|
+
* - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)
|
|
346
|
+
* - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)
|
|
347
|
+
* 3. Returns `true` if validation will fail, `false` if empty input is accepted
|
|
348
|
+
*
|
|
349
|
+
* @template T - The Zod type to check
|
|
350
|
+
* @param field - The Zod field to check
|
|
351
|
+
* @returns True if the field will show validation errors on empty/invalid input, false otherwise
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* User name field - required, no default
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const userName = z.string().min(1);
|
|
357
|
+
* requiresValidInput(userName); // true - will error if user submits empty
|
|
358
|
+
* ```
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* Marital status - required WITH default
|
|
362
|
+
* ```typescript
|
|
363
|
+
* const maritalStatus = z.string().min(1).default('single');
|
|
364
|
+
* requiresValidInput(maritalStatus); // true - will error if user clears and submits
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* Age with default - requires valid input
|
|
369
|
+
* ```typescript
|
|
370
|
+
* const age = z.number().default(0);
|
|
371
|
+
* requiresValidInput(age); // true - numbers reject empty strings
|
|
372
|
+
* ```
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* Optional bio field - doesn't require input
|
|
376
|
+
* ```typescript
|
|
377
|
+
* const bio = z.string().optional();
|
|
378
|
+
* requiresValidInput(bio); // false - user can leave empty
|
|
379
|
+
* ```
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* String with default but NO validation - doesn't require input
|
|
383
|
+
* ```typescript
|
|
384
|
+
* const notes = z.string().default('N/A');
|
|
385
|
+
* requiresValidInput(notes); // false - plain z.string() accepts empty strings
|
|
386
|
+
* ```
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* Nullable field - doesn't require input
|
|
390
|
+
* ```typescript
|
|
391
|
+
* const middleName = z.string().nullable();
|
|
392
|
+
* requiresValidInput(middleName); // false - user can leave null
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* @see {@link removeDefault} for understanding how defaults are handled
|
|
396
|
+
* @see {@link getPrimitiveType} for understanding type unwrapping
|
|
397
|
+
* @since 0.1.0
|
|
398
|
+
*/
|
|
399
|
+
declare const requiresValidInput: <T extends z.ZodType>(field: T) => boolean;
|
|
78
400
|
|
|
79
|
-
export { type
|
|
401
|
+
export { type Simplify, canUnwrap, extractDefault, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, unwrapUnion };
|
package/dist/index.js
CHANGED
|
@@ -22,15 +22,36 @@ function _interopNamespace(e) {
|
|
|
22
22
|
|
|
23
23
|
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
24
24
|
|
|
25
|
-
// src/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// src/defaults.ts
|
|
26
|
+
function canUnwrap(field) {
|
|
27
|
+
return "unwrap" in field && typeof field.unwrap === "function";
|
|
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
|
+
}
|
|
46
|
+
var getPrimitiveType = (field) => {
|
|
47
|
+
if (field instanceof z__namespace.ZodArray) {
|
|
30
48
|
return field;
|
|
31
49
|
}
|
|
32
|
-
if (
|
|
33
|
-
return getPrimitiveType(field.unwrap()
|
|
50
|
+
if (canUnwrap(field)) {
|
|
51
|
+
return getPrimitiveType(field.unwrap());
|
|
52
|
+
}
|
|
53
|
+
if (field instanceof z__namespace.ZodUnion) {
|
|
54
|
+
return getPrimitiveType(unwrapUnion(field).field);
|
|
34
55
|
}
|
|
35
56
|
return field;
|
|
36
57
|
};
|
|
@@ -38,7 +59,7 @@ function removeDefault(field) {
|
|
|
38
59
|
if (field instanceof z__namespace.ZodDefault) {
|
|
39
60
|
return field.unwrap();
|
|
40
61
|
}
|
|
41
|
-
if ("innerType" in field.def) {
|
|
62
|
+
if ("innerType" in field.def && field.def.innerType instanceof z__namespace.ZodType) {
|
|
42
63
|
const inner = removeDefault(field.def.innerType);
|
|
43
64
|
if (field instanceof z__namespace.ZodOptional) {
|
|
44
65
|
return inner.optional();
|
|
@@ -49,58 +70,48 @@ function removeDefault(field) {
|
|
|
49
70
|
}
|
|
50
71
|
return field;
|
|
51
72
|
}
|
|
52
|
-
var
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
73
|
+
var requiresValidInput = (field) => {
|
|
74
|
+
const defaultRemovedField = removeDefault(field);
|
|
75
|
+
if (!(defaultRemovedField instanceof z__namespace.ZodType)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const undefinedResult = defaultRemovedField.safeParse(void 0).success;
|
|
79
|
+
const nullResult = defaultRemovedField.safeParse(null).success;
|
|
80
|
+
const primitiveType = getPrimitiveType(defaultRemovedField);
|
|
81
|
+
const emptyStringResult = primitiveType.type === "string" && defaultRemovedField.safeParse("").success;
|
|
82
|
+
const emptyArrayResult = primitiveType.type === "array" && defaultRemovedField.safeParse([]).success;
|
|
58
83
|
return !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult;
|
|
59
84
|
};
|
|
85
|
+
|
|
86
|
+
// src/defaults.ts
|
|
60
87
|
function extractDefault(field) {
|
|
61
88
|
if (field instanceof z__namespace.ZodDefault) {
|
|
62
|
-
|
|
63
|
-
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
89
|
+
return field.def.defaultValue;
|
|
64
90
|
}
|
|
65
|
-
if (
|
|
91
|
+
if (canUnwrap(field)) {
|
|
66
92
|
return extractDefault(field.unwrap());
|
|
67
93
|
}
|
|
68
94
|
return void 0;
|
|
69
95
|
}
|
|
70
|
-
function getUnwrappedType(field) {
|
|
71
|
-
if (field instanceof z__namespace.ZodDefault) {
|
|
72
|
-
return field;
|
|
73
|
-
}
|
|
74
|
-
if ("unwrap" in field && typeof field.unwrap === "function") {
|
|
75
|
-
return getUnwrappedType(field.unwrap());
|
|
76
|
-
}
|
|
77
|
-
return field;
|
|
78
|
-
}
|
|
79
96
|
function getSchemaDefaults(schema) {
|
|
80
97
|
const defaults = {};
|
|
81
98
|
for (const key in schema.shape) {
|
|
82
99
|
const field = schema.shape[key];
|
|
100
|
+
if (!field) continue;
|
|
83
101
|
const defaultValue = extractDefault(field);
|
|
84
102
|
if (defaultValue !== void 0) {
|
|
85
103
|
defaults[key] = defaultValue;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const unwrapped = getUnwrappedType(field);
|
|
89
|
-
if (unwrapped instanceof z__namespace.ZodObject) {
|
|
90
|
-
const nestedDefaults = getSchemaDefaults(unwrapped);
|
|
91
|
-
if (Object.keys(nestedDefaults).length > 0) {
|
|
92
|
-
defaults[key] = nestedDefaults;
|
|
93
|
-
}
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
return defaults;
|
|
97
107
|
}
|
|
98
108
|
|
|
99
|
-
exports.
|
|
109
|
+
exports.canUnwrap = canUnwrap;
|
|
100
110
|
exports.extractDefault = extractDefault;
|
|
101
111
|
exports.getPrimitiveType = getPrimitiveType;
|
|
102
112
|
exports.getSchemaDefaults = getSchemaDefaults;
|
|
103
|
-
exports.getUnwrappedType = getUnwrappedType;
|
|
104
113
|
exports.removeDefault = removeDefault;
|
|
114
|
+
exports.requiresValidInput = requiresValidInput;
|
|
115
|
+
exports.unwrapUnion = unwrapUnion;
|
|
105
116
|
//# sourceMappingURL=index.js.map
|
|
106
117
|
//# 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 * Get the primitive type of a Zod field by unwrapping optional/nullable wrappers\n * @param field - The Zod field to unwrap\n * @param options - Options for unwrapping\n * @param options.unwrapArrays - If true, continues unwrapping arrays. If false (default), stops at arrays\n * @returns The unwrapped primitive type\n */\nexport const getPrimitiveType = <T extends z.ZodTypeAny>(\n field: T,\n options?: {\n unwrapArrays?: boolean;\n },\n): z.ZodTypeAny => {\n const unwrapArrays = options?.unwrapArrays ?? false;\n\n if (!unwrapArrays && field.type === 'array') {\n return field;\n }\n\n if ('unwrap' in field && typeof field.unwrap === 'function') {\n return getPrimitiveType(field.unwrap(), options);\n }\n\n return field;\n};\n\n/**\n * Remove default values from a Zod field\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults\n */\nexport function removeDefault(field: z.ZodType): z.ZodType {\n if (field instanceof z.ZodDefault) {\n return field.unwrap() as z.ZodType;\n }\n\n if ('innerType' in field.def) {\n const inner = removeDefault(field.def.innerType as z.ZodType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n return inner.optional();\n }\n if (field instanceof z.ZodNullable) {\n return inner.nullable();\n }\n }\n\n return field;\n}\n\n/**\n * Check if a Zod field is required (not optional/nullable and doesn't accept empty values)\n * @param field - The Zod field to check\n * @returns True if the field is required\n */\nexport const checkIfFieldIsRequired = <T extends z.ZodTypeAny>(field: T) => {\n // Check with defaults intact - if a field has a default, it's not required\n const undefinedResult = field.safeParse(undefined).success;\n const nullResult = field.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(field);\n\n const emptyStringResult =\n primitiveType.type === 'string' && field.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && field.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n","import * as z from 'zod';\n\n/**\n * Extract the default value from a Zod field (recursively unwraps optional/nullable)\n * @param field - The Zod field to extract default from\n * @returns The default value if present, undefined otherwise\n */\nexport function extractDefault(field: z.ZodTypeAny): any {\n if (field instanceof z.ZodDefault) {\n const defaultValue = field._def.defaultValue;\n return typeof defaultValue === 'function' ? defaultValue() : defaultValue;\n }\n\n if ('unwrap' in field && typeof field.unwrap === 'function') {\n return extractDefault(field.unwrap());\n }\n\n return undefined;\n}\n\n/**\n * Get the unwrapped type without going through defaults\n * Useful for detecting nested objects/arrays while preserving defaults\n * @param field - The Zod field to unwrap\n * @returns The unwrapped type\n */\nexport function getUnwrappedType(field: z.ZodTypeAny): z.ZodTypeAny {\n if (field instanceof z.ZodDefault) {\n // Don't unwrap defaults - we want to preserve them\n return field;\n }\n\n if ('unwrap' in field && typeof field.unwrap === 'function') {\n return getUnwrappedType(field.unwrap());\n }\n\n return field;\n}\n\n/**\n * Extract all default values from a Zod object schema\n * Recursively handles nested objects and only returns fields with defaults\n * @param schema - The Zod object schema to extract defaults from\n * @returns Partial object with only fields that have defaults\n *\n * @example\n * ```ts\n * const schema = z.object({\n * name: z.string().default('John'),\n * age: z.number(), // no default - skipped\n * settings: z.object({\n * theme: z.string().default('light')\n * })\n * });\n *\n * getSchemaDefaults(schema);\n * // Returns: { name: 'John', settings: { theme: 'light' } }\n * ```\n */\nexport function getSchemaDefaults<T extends z.ZodObject<any>>(\n schema: T,\n): Partial<z.infer<T>> {\n const defaults: Record<string, any> = {};\n\n for (const key in schema.shape) {\n const field = schema.shape[key];\n\n // First, check if this field has an explicit default value\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n continue;\n }\n\n // If no explicit default, check if it's a nested object with defaults\n const unwrapped = getUnwrappedType(field);\n if (unwrapped instanceof z.ZodObject) {\n const nestedDefaults = getSchemaDefaults(unwrapped);\n if (Object.keys(nestedDefaults).length > 0) {\n defaults[key] = nestedDefaults;\n }\n }\n }\n\n return defaults as Partial<z.infer<T>>;\n}\n"]}
|
|
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 * 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","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"]}
|