@zod-utils/core 0.9.0 → 1.0.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 CHANGED
@@ -272,6 +272,157 @@ import { getFieldChecks, type ZodUnionCheck } from "@zod-utils/core";
272
272
 
273
273
  ---
274
274
 
275
+ ### `extractDiscriminatedSchema(schema, key, value)`
276
+
277
+ Extract a specific variant from a discriminated union schema based on the discriminator field and value.
278
+
279
+ ```typescript
280
+ import { extractDiscriminatedSchema } from "@zod-utils/core";
281
+ import { z } from "zod";
282
+
283
+ const userSchema = z.discriminatedUnion('mode', [
284
+ z.object({
285
+ mode: z.literal('create'),
286
+ name: z.string(),
287
+ age: z.number().optional(),
288
+ }),
289
+ z.object({
290
+ mode: z.literal('edit'),
291
+ id: z.number(),
292
+ name: z.string().optional(),
293
+ bio: z.string().optional(),
294
+ }),
295
+ ]);
296
+
297
+ // Extract the 'create' variant
298
+ const createSchema = extractDiscriminatedSchema({
299
+ schema: userSchema,
300
+ key: 'mode',
301
+ value: 'create',
302
+ });
303
+ // Returns: z.ZodObject with { mode, name, age }
304
+
305
+ // Extract the 'edit' variant
306
+ const editSchema = extractDiscriminatedSchema({
307
+ schema: userSchema,
308
+ key: 'mode',
309
+ value: 'edit',
310
+ });
311
+ // Returns: z.ZodObject with { mode, id, name, bio }
312
+ ```
313
+
314
+ **Use with discriminated unions:** This is essential when working with `z.discriminatedUnion()` schemas, as it extracts the correct variant schema based on the discriminator value.
315
+
316
+ ---
317
+
318
+ ### `extractFieldFromSchema(schema, fieldName, discriminator?)`
319
+
320
+ Extract a single field from a Zod object or discriminated union schema.
321
+
322
+ ```typescript
323
+ import { extractFieldFromSchema } from "@zod-utils/core";
324
+ import { z } from "zod";
325
+
326
+ // Simple object schema
327
+ const userSchema = z.object({
328
+ name: z.string(),
329
+ age: z.number(),
330
+ email: z.string().email(),
331
+ });
332
+
333
+ const nameField = extractFieldFromSchema({
334
+ schema: userSchema,
335
+ fieldName: 'name',
336
+ });
337
+ // Returns: ZodString
338
+
339
+ // Discriminated union schema
340
+ const formSchema = z.discriminatedUnion('mode', [
341
+ z.object({
342
+ mode: z.literal('create'),
343
+ name: z.string(),
344
+ age: z.number().optional(),
345
+ }),
346
+ z.object({
347
+ mode: z.literal('edit'),
348
+ id: z.number(),
349
+ name: z.string().optional(),
350
+ }),
351
+ ]);
352
+
353
+ // Extract field from specific variant
354
+ const idField = extractFieldFromSchema({
355
+ schema: formSchema,
356
+ fieldName: 'id',
357
+ discriminator: {
358
+ key: 'mode',
359
+ value: 'edit',
360
+ },
361
+ });
362
+ // Returns: ZodNumber
363
+
364
+ // Without discriminator on discriminated union, returns undefined
365
+ const fieldWithoutDiscriminator = extractFieldFromSchema({
366
+ schema: formSchema,
367
+ fieldName: 'name',
368
+ });
369
+ // Returns: undefined (need discriminator to know which variant)
370
+ ```
371
+
372
+ **Discriminated union support:** When extracting fields from discriminated unions, you must provide the `discriminator` option with `key` and `value` to specify which variant to use.
373
+
374
+ ---
375
+
376
+ ### `getSchemaDefaults(schema, discriminator?)`
377
+
378
+ **Updated:** Now supports discriminated union schemas with the `discriminator` option.
379
+
380
+ ```typescript
381
+ import { getSchemaDefaults } from "@zod-utils/core";
382
+ import { z } from "zod";
383
+
384
+ // Discriminated union with defaults
385
+ const formSchema = z.discriminatedUnion('mode', [
386
+ z.object({
387
+ mode: z.literal('create'),
388
+ name: z.string(),
389
+ age: z.number().default(18),
390
+ }),
391
+ z.object({
392
+ mode: z.literal('edit'),
393
+ id: z.number().default(1),
394
+ name: z.string().optional(),
395
+ bio: z.string().default('bio goes here'),
396
+ }),
397
+ ]);
398
+
399
+ // Get defaults for 'create' mode
400
+ const createDefaults = getSchemaDefaults(formSchema, {
401
+ discriminator: {
402
+ key: 'mode',
403
+ value: 'create',
404
+ },
405
+ });
406
+ // Returns: { age: 18 }
407
+
408
+ // Get defaults for 'edit' mode
409
+ const editDefaults = getSchemaDefaults(formSchema, {
410
+ discriminator: {
411
+ key: 'mode',
412
+ value: 'edit',
413
+ },
414
+ });
415
+ // Returns: { id: 1, bio: 'bio goes here' }
416
+
417
+ // Without discriminator, returns empty object
418
+ const noDefaults = getSchemaDefaults(formSchema);
419
+ // Returns: {}
420
+ ```
421
+
422
+ **Discriminator types:** The discriminator `value` can be a string, number, or boolean literal that matches the discriminator field type.
423
+
424
+ ---
425
+
275
426
  ## Type Utilities
276
427
 
277
428
  ### `Simplify<T>`
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
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
+ import { util, z as z$1 } from 'zod';
3
+ import { $InferUnionOutput, $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';
3
4
 
4
5
  /**
5
6
  * Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
@@ -176,13 +177,22 @@ declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> |
176
177
  * @see {@link extractDefault} for extracting defaults from individual fields
177
178
  * @since 0.1.0
178
179
  */
179
- declare function getSchemaDefaults<TSchema extends z.ZodObject | z.ZodDiscriminatedUnion<Array<z.ZodObject>>, TObj extends z.infer<TSchema>, TDiscriminatorField extends keyof TObj = keyof TObj>(schema: TSchema, options?: {
180
+ declare function getSchemaDefaults<TSchema extends z.ZodObject | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>(schema: TSchema, options?: {
180
181
  discriminator?: {
181
- field: TDiscriminatorField;
182
- value: TObj[TDiscriminatorField];
182
+ key: TDiscriminatorKey;
183
+ value: TDiscriminatorValue;
183
184
  };
184
185
  }): Simplify<Partial<z.infer<TSchema>>>;
185
186
 
187
+ declare function extractFieldFromSchema<TSchema extends z$1.ZodObject | z$1.ZodDiscriminatedUnion, TName extends keyof Extract<Required<z$1.infer<TSchema>>, Record<TDiscriminatorKey, TDiscriminatorValue>>, TDiscriminatorKey extends keyof z$1.infer<TSchema> & string, TDiscriminatorValue extends z$1.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, fieldName, discriminator, }: {
188
+ schema: TSchema;
189
+ fieldName: TName;
190
+ discriminator?: {
191
+ key: TDiscriminatorKey;
192
+ value: TDiscriminatorValue;
193
+ };
194
+ }): z$1.ZodType<unknown, unknown, z$1.core.$ZodTypeInternals<unknown, unknown>> | undefined;
195
+
186
196
  /**
187
197
  * Type representing a Zod type that has an unwrap method
188
198
  */
@@ -515,26 +525,70 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
515
525
  * @since 0.4.0
516
526
  */
517
527
  declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
528
+ /**
529
+ * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.
530
+ *
531
+ * This advanced TypeScript utility type walks through a union's options tuple at compile-time,
532
+ * checking each schema against the discriminator field and value, and returns the exact matching
533
+ * schema type (not a union of all options).
534
+ *
535
+ * **How it works:**
536
+ * 1. Extracts the options tuple from the union using `infer Options`
537
+ * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching
538
+ * 3. Checks if `First` is a ZodObject with the matching discriminator field and value
539
+ * 4. If match found, returns `First` (the exact schema type)
540
+ * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted
541
+ *
542
+ * **Type narrowing magic:**
543
+ * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`
544
+ * - Discriminator value: `'a'` (matches SchemaA)
545
+ * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)
546
+ *
547
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type
548
+ * @template TDiscriminatorKey - The discriminator field name (e.g., "type", "mode")
549
+ * @template TDiscriminatorValue - The specific discriminator value (e.g., "create", "edit")
550
+ * @returns The exact matching schema type, or `never` if no match found
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * const schema = z.discriminatedUnion('mode', [
555
+ * z.object({ mode: z.literal('create'), name: z.string() }),
556
+ * z.object({ mode: z.literal('edit'), id: z.number() }),
557
+ * ]);
558
+ *
559
+ * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })
560
+ * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;
561
+ * ```
562
+ */
563
+ type ExtractZodUnionMember<TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal> = TSchema extends z.ZodUnion<infer Options> ? Options extends readonly [
564
+ infer First extends z.ZodTypeAny,
565
+ ...infer Rest extends z.ZodTypeAny[]
566
+ ] ? First extends z.ZodObject<infer Shape> ? TDiscriminatorKey extends keyof Shape ? Shape[TDiscriminatorKey] extends z.ZodLiteral<TDiscriminatorValue> ? First : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : never : never : never;
518
567
  /**
519
568
  * Extracts a specific schema option from a discriminated union based on the discriminator field value.
520
569
  *
521
- * This function finds and returns the matching schema option from a `ZodDiscriminatedUnion` by
570
+ * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by
522
571
  * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to
523
572
  * extract defaults from the correct schema variant in a discriminated union.
524
573
  *
525
- * **How it works:**
526
- * 1. Iterates through all options in the discriminated union
527
- * 2. For each option, checks if the discriminator field matches the provided value
528
- * 3. Returns the first matching schema option, or `undefined` if no match found
574
+ * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the
575
+ * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at
576
+ * compile-time based on the discriminator value.
529
577
  *
530
- * @template TSchema - The discriminated union schema type
531
- * @template TObj - The inferred type of the schema
532
- * @template TDiscriminatorField - The discriminator field key type
578
+ * **How it works:**
579
+ * 1. Iterates through all options in the discriminated union at runtime
580
+ * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches
581
+ * 3. Returns the first matching schema with its exact type narrowed at compile-time
582
+ * 4. Returns `undefined` if no match found or if option is not a ZodObject
583
+ *
584
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type
585
+ * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)
586
+ * @template TDiscriminatorValue - The specific discriminator value to match (literal type)
533
587
  * @param params - Parameters object
534
588
  * @param params.schema - The discriminated union schema to search
535
- * @param params.discriminatorField - The discriminator field name (e.g., "mode", "type")
589
+ * @param params.discriminatorKey - The discriminator field name (e.g., "mode", "type")
536
590
  * @param params.discriminatorValue - The discriminator value to match (e.g., "create", "edit")
537
- * @returns The matching schema option, or `undefined` if not found
591
+ * @returns The exact matching schema option (with precise type), or `undefined` if not found
538
592
  *
539
593
  * @example
540
594
  * Basic discriminated union - create/edit mode
@@ -555,7 +609,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
555
609
  * // Extract the "create" schema
556
610
  * const createSchema = extractDiscriminatedSchema({
557
611
  * schema: userSchema,
558
- * discriminatorField: 'mode',
612
+ * discriminatorKey: 'mode',
559
613
  * discriminatorValue: 'create',
560
614
  * });
561
615
  * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })
@@ -563,7 +617,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
563
617
  * // Extract the "edit" schema
564
618
  * const editSchema = extractDiscriminatedSchema({
565
619
  * schema: userSchema,
566
- * discriminatorField: 'mode',
620
+ * discriminatorKey: 'mode',
567
621
  * discriminatorValue: 'edit',
568
622
  * });
569
623
  * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })
@@ -579,7 +633,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
579
633
  *
580
634
  * const clickSchema = extractDiscriminatedSchema({
581
635
  * schema: eventSchema,
582
- * discriminatorField: 'type',
636
+ * discriminatorKey: 'type',
583
637
  * discriminatorValue: 'click',
584
638
  * });
585
639
  * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })
@@ -594,19 +648,44 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
594
648
  *
595
649
  * const result = extractDiscriminatedSchema({
596
650
  * schema,
597
- * discriminatorField: 'mode',
651
+ * discriminatorKey: 'mode',
598
652
  * discriminatorValue: 'invalid', // doesn't match any option
599
653
  * });
600
654
  * // Result: undefined
601
655
  * ```
602
656
  *
657
+ * @example
658
+ * Type narrowing demonstration
659
+ * ```typescript
660
+ * const schema = z.discriminatedUnion('mode', [
661
+ * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
662
+ * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),
663
+ * ]);
664
+ *
665
+ * const createSchema = extractDiscriminatedSchema({
666
+ * schema,
667
+ * discriminatorKey: 'mode',
668
+ * discriminatorValue: 'create',
669
+ * });
670
+ *
671
+ * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })
672
+ * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined
673
+ *
674
+ * if (createSchema) {
675
+ * createSchema.shape.age; // ✅ TypeScript knows 'age' exists
676
+ * createSchema.shape.name; // ✅ TypeScript knows 'name' exists
677
+ * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema
678
+ * }
679
+ * ```
680
+ *
603
681
  * @see {@link getSchemaDefaults} for usage with discriminated unions
682
+ * @see {@link ExtractZodUnionMember} for the type-level extraction logic
604
683
  * @since 0.6.0
605
684
  */
606
- declare const extractDiscriminatedSchema: <TSchema extends z.ZodDiscriminatedUnion<Array<z.ZodObject>>, TObj extends z.infer<TSchema>, TDiscriminatorField extends keyof TObj = keyof TObj>({ schema, discriminatorField, discriminatorValue, }: {
685
+ declare const extractDiscriminatedSchema: <TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, key, value, }: {
607
686
  schema: TSchema;
608
- discriminatorField: TDiscriminatorField;
609
- discriminatorValue: TObj[TDiscriminatorField];
610
- }) => z.ZodObject<z.core.$ZodLooseShape, z.core.$strip> | undefined;
687
+ key: TDiscriminatorKey;
688
+ value: TDiscriminatorValue;
689
+ }) => ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined;
611
690
 
612
- export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, extractDiscriminatedSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
691
+ export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
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
+ import { util, z as z$1 } from 'zod';
3
+ import { $InferUnionOutput, $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';
3
4
 
4
5
  /**
5
6
  * Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
@@ -176,13 +177,22 @@ declare function extractDefault<T extends z.ZodTypeAny>(field: T): z.infer<T> |
176
177
  * @see {@link extractDefault} for extracting defaults from individual fields
177
178
  * @since 0.1.0
178
179
  */
179
- declare function getSchemaDefaults<TSchema extends z.ZodObject | z.ZodDiscriminatedUnion<Array<z.ZodObject>>, TObj extends z.infer<TSchema>, TDiscriminatorField extends keyof TObj = keyof TObj>(schema: TSchema, options?: {
180
+ declare function getSchemaDefaults<TSchema extends z.ZodObject | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>(schema: TSchema, options?: {
180
181
  discriminator?: {
181
- field: TDiscriminatorField;
182
- value: TObj[TDiscriminatorField];
182
+ key: TDiscriminatorKey;
183
+ value: TDiscriminatorValue;
183
184
  };
184
185
  }): Simplify<Partial<z.infer<TSchema>>>;
185
186
 
187
+ declare function extractFieldFromSchema<TSchema extends z$1.ZodObject | z$1.ZodDiscriminatedUnion, TName extends keyof Extract<Required<z$1.infer<TSchema>>, Record<TDiscriminatorKey, TDiscriminatorValue>>, TDiscriminatorKey extends keyof z$1.infer<TSchema> & string, TDiscriminatorValue extends z$1.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, fieldName, discriminator, }: {
188
+ schema: TSchema;
189
+ fieldName: TName;
190
+ discriminator?: {
191
+ key: TDiscriminatorKey;
192
+ value: TDiscriminatorValue;
193
+ };
194
+ }): z$1.ZodType<unknown, unknown, z$1.core.$ZodTypeInternals<unknown, unknown>> | undefined;
195
+
186
196
  /**
187
197
  * Type representing a Zod type that has an unwrap method
188
198
  */
@@ -515,26 +525,70 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
515
525
  * @since 0.4.0
516
526
  */
517
527
  declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
528
+ /**
529
+ * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.
530
+ *
531
+ * This advanced TypeScript utility type walks through a union's options tuple at compile-time,
532
+ * checking each schema against the discriminator field and value, and returns the exact matching
533
+ * schema type (not a union of all options).
534
+ *
535
+ * **How it works:**
536
+ * 1. Extracts the options tuple from the union using `infer Options`
537
+ * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching
538
+ * 3. Checks if `First` is a ZodObject with the matching discriminator field and value
539
+ * 4. If match found, returns `First` (the exact schema type)
540
+ * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted
541
+ *
542
+ * **Type narrowing magic:**
543
+ * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`
544
+ * - Discriminator value: `'a'` (matches SchemaA)
545
+ * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)
546
+ *
547
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type
548
+ * @template TDiscriminatorKey - The discriminator field name (e.g., "type", "mode")
549
+ * @template TDiscriminatorValue - The specific discriminator value (e.g., "create", "edit")
550
+ * @returns The exact matching schema type, or `never` if no match found
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * const schema = z.discriminatedUnion('mode', [
555
+ * z.object({ mode: z.literal('create'), name: z.string() }),
556
+ * z.object({ mode: z.literal('edit'), id: z.number() }),
557
+ * ]);
558
+ *
559
+ * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })
560
+ * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;
561
+ * ```
562
+ */
563
+ type ExtractZodUnionMember<TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal> = TSchema extends z.ZodUnion<infer Options> ? Options extends readonly [
564
+ infer First extends z.ZodTypeAny,
565
+ ...infer Rest extends z.ZodTypeAny[]
566
+ ] ? First extends z.ZodObject<infer Shape> ? TDiscriminatorKey extends keyof Shape ? Shape[TDiscriminatorKey] extends z.ZodLiteral<TDiscriminatorValue> ? First : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : Rest extends [] ? never : TDiscriminatorValue extends $InferUnionOutput<Rest[number]>[TDiscriminatorKey] ? ExtractZodUnionMember<z.ZodUnion<Rest>, TDiscriminatorKey, TDiscriminatorValue> : never : never : never : never;
518
567
  /**
519
568
  * Extracts a specific schema option from a discriminated union based on the discriminator field value.
520
569
  *
521
- * This function finds and returns the matching schema option from a `ZodDiscriminatedUnion` by
570
+ * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by
522
571
  * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to
523
572
  * extract defaults from the correct schema variant in a discriminated union.
524
573
  *
525
- * **How it works:**
526
- * 1. Iterates through all options in the discriminated union
527
- * 2. For each option, checks if the discriminator field matches the provided value
528
- * 3. Returns the first matching schema option, or `undefined` if no match found
574
+ * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the
575
+ * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at
576
+ * compile-time based on the discriminator value.
529
577
  *
530
- * @template TSchema - The discriminated union schema type
531
- * @template TObj - The inferred type of the schema
532
- * @template TDiscriminatorField - The discriminator field key type
578
+ * **How it works:**
579
+ * 1. Iterates through all options in the discriminated union at runtime
580
+ * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches
581
+ * 3. Returns the first matching schema with its exact type narrowed at compile-time
582
+ * 4. Returns `undefined` if no match found or if option is not a ZodObject
583
+ *
584
+ * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type
585
+ * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)
586
+ * @template TDiscriminatorValue - The specific discriminator value to match (literal type)
533
587
  * @param params - Parameters object
534
588
  * @param params.schema - The discriminated union schema to search
535
- * @param params.discriminatorField - The discriminator field name (e.g., "mode", "type")
589
+ * @param params.discriminatorKey - The discriminator field name (e.g., "mode", "type")
536
590
  * @param params.discriminatorValue - The discriminator value to match (e.g., "create", "edit")
537
- * @returns The matching schema option, or `undefined` if not found
591
+ * @returns The exact matching schema option (with precise type), or `undefined` if not found
538
592
  *
539
593
  * @example
540
594
  * Basic discriminated union - create/edit mode
@@ -555,7 +609,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
555
609
  * // Extract the "create" schema
556
610
  * const createSchema = extractDiscriminatedSchema({
557
611
  * schema: userSchema,
558
- * discriminatorField: 'mode',
612
+ * discriminatorKey: 'mode',
559
613
  * discriminatorValue: 'create',
560
614
  * });
561
615
  * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })
@@ -563,7 +617,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
563
617
  * // Extract the "edit" schema
564
618
  * const editSchema = extractDiscriminatedSchema({
565
619
  * schema: userSchema,
566
- * discriminatorField: 'mode',
620
+ * discriminatorKey: 'mode',
567
621
  * discriminatorValue: 'edit',
568
622
  * });
569
623
  * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })
@@ -579,7 +633,7 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
579
633
  *
580
634
  * const clickSchema = extractDiscriminatedSchema({
581
635
  * schema: eventSchema,
582
- * discriminatorField: 'type',
636
+ * discriminatorKey: 'type',
583
637
  * discriminatorValue: 'click',
584
638
  * });
585
639
  * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })
@@ -594,19 +648,44 @@ declare function getFieldChecks<T extends z.ZodTypeAny>(field: T): Array<ZodUnio
594
648
  *
595
649
  * const result = extractDiscriminatedSchema({
596
650
  * schema,
597
- * discriminatorField: 'mode',
651
+ * discriminatorKey: 'mode',
598
652
  * discriminatorValue: 'invalid', // doesn't match any option
599
653
  * });
600
654
  * // Result: undefined
601
655
  * ```
602
656
  *
657
+ * @example
658
+ * Type narrowing demonstration
659
+ * ```typescript
660
+ * const schema = z.discriminatedUnion('mode', [
661
+ * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
662
+ * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),
663
+ * ]);
664
+ *
665
+ * const createSchema = extractDiscriminatedSchema({
666
+ * schema,
667
+ * discriminatorKey: 'mode',
668
+ * discriminatorValue: 'create',
669
+ * });
670
+ *
671
+ * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })
672
+ * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined
673
+ *
674
+ * if (createSchema) {
675
+ * createSchema.shape.age; // ✅ TypeScript knows 'age' exists
676
+ * createSchema.shape.name; // ✅ TypeScript knows 'name' exists
677
+ * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema
678
+ * }
679
+ * ```
680
+ *
603
681
  * @see {@link getSchemaDefaults} for usage with discriminated unions
682
+ * @see {@link ExtractZodUnionMember} for the type-level extraction logic
604
683
  * @since 0.6.0
605
684
  */
606
- declare const extractDiscriminatedSchema: <TSchema extends z.ZodDiscriminatedUnion<Array<z.ZodObject>>, TObj extends z.infer<TSchema>, TDiscriminatorField extends keyof TObj = keyof TObj>({ schema, discriminatorField, discriminatorValue, }: {
685
+ declare const extractDiscriminatedSchema: <TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion, TDiscriminatorKey extends keyof z.infer<TSchema> & string, TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] & util.Literal>({ schema, key, value, }: {
607
686
  schema: TSchema;
608
- discriminatorField: TDiscriminatorField;
609
- discriminatorValue: TObj[TDiscriminatorField];
610
- }) => z.ZodObject<z.core.$ZodLooseShape, z.core.$strip> | undefined;
687
+ key: TDiscriminatorKey;
688
+ value: TDiscriminatorValue;
689
+ }) => ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined;
611
690
 
612
- export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, extractDiscriminatedSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
691
+ export { type Simplify, type ZodUnionCheck, canUnwrap, extractDefault, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
package/dist/index.js CHANGED
@@ -22,7 +22,22 @@ function _interopNamespace(e) {
22
22
 
23
23
  var z__namespace = /*#__PURE__*/_interopNamespace(z);
24
24
 
25
- // src/defaults.ts
25
+ var __defProp = Object.defineProperty;
26
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
27
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
28
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
29
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
30
+ var __spreadValues = (a, b) => {
31
+ for (var prop in b || (b = {}))
32
+ if (__hasOwnProp.call(b, prop))
33
+ __defNormalProp(a, prop, b[prop]);
34
+ if (__getOwnPropSymbols)
35
+ for (var prop of __getOwnPropSymbols(b)) {
36
+ if (__propIsEnum.call(b, prop))
37
+ __defNormalProp(a, prop, b[prop]);
38
+ }
39
+ return a;
40
+ };
26
41
  function canUnwrap(field) {
27
42
  return "unwrap" in field && typeof field.unwrap === "function";
28
43
  }
@@ -89,15 +104,20 @@ function getFieldChecks(field) {
89
104
  }
90
105
  var extractDiscriminatedSchema = ({
91
106
  schema,
92
- discriminatorField,
93
- discriminatorValue
107
+ key,
108
+ value
94
109
  }) => {
95
- return schema.options.find((option) => {
96
- const targetField = option.shape[String(discriminatorField)];
97
- if (!targetField) return false;
98
- const parseResult = targetField.safeParse(discriminatorValue);
99
- return parseResult.success;
100
- });
110
+ return schema.options.find(
111
+ (option) => {
112
+ if (option instanceof z__namespace.ZodObject) {
113
+ const targetField = option.shape[String(key)];
114
+ if (!targetField) return false;
115
+ const parseResult = targetField.safeParse(value);
116
+ return parseResult.success;
117
+ }
118
+ return false;
119
+ }
120
+ );
101
121
  };
102
122
 
103
123
  // src/defaults.ts
@@ -121,12 +141,9 @@ function getSchemaDefaults(schema, options) {
121
141
  let targetSchema;
122
142
  if (schema instanceof z__namespace.ZodDiscriminatedUnion) {
123
143
  if (options == null ? void 0 : options.discriminator) {
124
- const { field, value } = options.discriminator;
125
- targetSchema = extractDiscriminatedSchema({
126
- schema,
127
- discriminatorField: field,
128
- discriminatorValue: value
129
- });
144
+ targetSchema = extractDiscriminatedSchema(__spreadValues({
145
+ schema
146
+ }, options.discriminator));
130
147
  }
131
148
  } else {
132
149
  targetSchema = schema;
@@ -144,10 +161,30 @@ function getSchemaDefaults(schema, options) {
144
161
  }
145
162
  return defaults;
146
163
  }
164
+ function extractFieldFromSchema({
165
+ schema,
166
+ fieldName,
167
+ discriminator
168
+ }) {
169
+ let targetSchema;
170
+ if (schema instanceof z.z.ZodDiscriminatedUnion) {
171
+ if (discriminator) {
172
+ targetSchema = extractDiscriminatedSchema(__spreadValues({
173
+ schema
174
+ }, discriminator));
175
+ }
176
+ } else {
177
+ targetSchema = schema;
178
+ }
179
+ if (!targetSchema) return void 0;
180
+ const field = targetSchema.shape[String(fieldName)];
181
+ return field;
182
+ }
147
183
 
148
184
  exports.canUnwrap = canUnwrap;
149
185
  exports.extractDefault = extractDefault;
150
186
  exports.extractDiscriminatedSchema = extractDiscriminatedSchema;
187
+ exports.extractFieldFromSchema = extractFieldFromSchema;
151
188
  exports.getFieldChecks = getFieldChecks;
152
189
  exports.getPrimitiveType = getPrimitiveType;
153
190
  exports.getSchemaDefaults = getSchemaDefaults;
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';\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 * Attempts to strip nullish types from a union and return the single remaining type.\n *\n * This function filters out `ZodNull` and `ZodUndefined` from union types. If exactly\n * one type remains after filtering, it returns that unwrapped type. Otherwise, it returns\n * `false` to indicate the union couldn't be simplified to a single type.\n *\n * @param field - The Zod field to process\n * @returns The unwrapped type if only one remains, otherwise `false`\n *\n * @example\n * Union with only nullish types filtered - returns single type\n * ```typescript\n * const field = z.union([z.string(), z.null(), z.undefined()]);\n * const result = tryStripNullishOnly(field);\n * // Result: z.string() (unwrapped)\n * ```\n *\n * @example\n * Union with multiple non-nullish types - returns false\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = tryStripNullishOnly(field);\n * // Result: false (cannot simplify to single type)\n * ```\n *\n * @example\n * Non-union type - returns false\n * ```typescript\n * const field = z.string();\n * const result = tryStripNullishOnly(field);\n * // Result: false (not a union)\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.5.0\n */\nexport function tryStripNullishOnly(field: z.ZodTypeAny): z.ZodType | false {\n if (field instanceof z.ZodUnion) {\n const unionOptions = [...field.def.options];\n\n const filteredOptions = unionOptions.filter(\n (option): option is z.ZodType =>\n !(option instanceof z.ZodNull) && !(option instanceof z.ZodUndefined),\n );\n\n // If exactly one option remains, return it unwrapped\n const firstOption = filteredOptions[0];\n if (firstOption && filteredOptions.length === 1) {\n return firstOption;\n }\n }\n\n // Not a union, or couldn't simplify to single type\n return false;\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 * **Union handling:** For union types, strips nullish types (null/undefined) first. If only one\n * type remains after stripping, unwraps to that type. If multiple non-nullish types remain,\n * returns the union as-is (does not unwrap).\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 * @example\n * Union with only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.union([z.string(), z.number()]) (returned as-is)\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n return getPrimitiveType(unwrapped);\n }\n\n // Multiple non-nullish types or all nullish - return union as-is\n return 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\n/**\n * Extracts a specific schema option from a discriminated union based on the discriminator field value.\n *\n * This function finds and returns the matching schema option from a `ZodDiscriminatedUnion` by\n * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to\n * extract defaults from the correct schema variant in a discriminated union.\n *\n * **How it works:**\n * 1. Iterates through all options in the discriminated union\n * 2. For each option, checks if the discriminator field matches the provided value\n * 3. Returns the first matching schema option, or `undefined` if no match found\n *\n * @template TSchema - The discriminated union schema type\n * @template TObj - The inferred type of the schema\n * @template TDiscriminatorField - The discriminator field key type\n * @param params - Parameters object\n * @param params.schema - The discriminated union schema to search\n * @param params.discriminatorField - The discriminator field name (e.g., \"mode\", \"type\")\n * @param params.discriminatorValue - The discriminator value to match (e.g., \"create\", \"edit\")\n * @returns The matching schema option, or `undefined` if not found\n *\n * @example\n * Basic discriminated union - create/edit mode\n * ```typescript\n * const userSchema = z.discriminatedUnion('mode', [\n * z.object({\n * mode: z.literal('create'),\n * name: z.string(),\n * age: z.number().optional(),\n * }),\n * z.object({\n * mode: z.literal('edit'),\n * id: z.number(),\n * name: z.string().optional(),\n * }),\n * ]);\n *\n * // Extract the \"create\" schema\n * const createSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'create',\n * });\n * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })\n *\n * // Extract the \"edit\" schema\n * const editSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'edit',\n * });\n * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })\n * ```\n *\n * @example\n * Type-based discrimination\n * ```typescript\n * const eventSchema = z.discriminatedUnion('type', [\n * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),\n * z.object({ type: z.literal('keypress'), key: z.string() }),\n * ]);\n *\n * const clickSchema = extractDiscriminatedSchema({\n * schema: eventSchema,\n * discriminatorField: 'type',\n * discriminatorValue: 'click',\n * });\n * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })\n * ```\n *\n * @example\n * Invalid discriminator value\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * ]);\n *\n * const result = extractDiscriminatedSchema({\n * schema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'invalid', // doesn't match any option\n * });\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for usage with discriminated unions\n * @since 0.6.0\n */\nexport const extractDiscriminatedSchema = <\n TSchema extends z.ZodDiscriminatedUnion<Array<z.ZodObject>>,\n TObj extends z.infer<TSchema>,\n TDiscriminatorField extends keyof TObj = keyof TObj,\n>({\n schema,\n discriminatorField,\n discriminatorValue,\n}: {\n schema: TSchema;\n discriminatorField: TDiscriminatorField;\n discriminatorValue: TObj[TDiscriminatorField];\n}) => {\n return schema.options.find((option) => {\n const targetField = option.shape[String(discriminatorField)];\n if (!targetField) return false;\n\n const parseResult = targetField.safeParse(discriminatorValue);\n return parseResult.success;\n });\n};\n","import * as z from 'zod';\nimport {\n canUnwrap,\n extractDiscriminatedSchema,\n tryStripNullishOnly,\n} 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, strips nullish types (null/undefined) first. If only one type\n * remains after stripping, extracts the default from that type. If multiple non-nullish types remain,\n * returns `undefined` (does not extract from any option).\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 only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.null()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (multiple non-nullish types - no default extracted)\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 * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n // Successfully unwrapped to single type\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapped) as z.infer<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema, returning only fields with explicit `.default()`.\n *\n * This function traverses the schema and collects fields that have explicit default values.\n * 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 * **Component handling:** For form inputs without explicit defaults (like `z.string()` or `z.number()`),\n * use the `?? ''` pattern in your components: `<Input value={field.value ?? ''} />`\n *\n * @template TSchema - The Zod object schema type\n * @param targetSchema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with explicit default values\n *\n * @example\n * Basic usage - only explicit defaults\n * ```typescript\n * const schema = z.object({\n * name: z.string(), // no default → NOT included\n * age: z.number(), // no default → NOT included\n * role: z.string().default('user'), // explicit default → included\n * count: z.number().default(0), // explicit default → included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { role: 'user', count: 0 }\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 * name: z.string().optional(), // no default → NOT included\n * age: z.number().optional(), // no default → NOT included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @example\n * Component usage for fields without defaults\n * ```typescript\n * // For string/number fields without defaults, handle in components:\n * <Input value={field.value ?? ''} />\n * <Input type=\"number\" value={field.value ?? ''} />\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion<Array<z.ZodObject>>,\n TObj extends z.infer<TSchema>,\n TDiscriminatorField extends keyof TObj = keyof TObj,\n>(\n schema: TSchema,\n options?: {\n discriminator?: {\n field: TDiscriminatorField;\n value: TObj[TDiscriminatorField];\n };\n },\n): Simplify<Partial<z.infer<TSchema>>> {\n let targetSchema: z.ZodObject | undefined;\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n const { field, value } = options.discriminator;\n\n targetSchema = extractDiscriminatedSchema({\n schema,\n discriminatorField: field,\n discriminatorValue: value,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n const defaults: Record<string, unknown> = {};\n\n if (targetSchema) {\n for (const key in targetSchema.shape) {\n const field = targetSchema.shape[key];\n if (!field) continue;\n\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<TSchema>>;\n}\n"]}
1
+ {"version":3,"sources":["../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z","z2"],"mappings":"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","file":"index.js","sourcesContent":["import type { util } from 'zod';\nimport * as z from 'zod';\nimport type {\n $InferUnionOutput,\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 * Attempts to strip nullish types from a union and return the single remaining type.\n *\n * This function filters out `ZodNull` and `ZodUndefined` from union types. If exactly\n * one type remains after filtering, it returns that unwrapped type. Otherwise, it returns\n * `false` to indicate the union couldn't be simplified to a single type.\n *\n * @param field - The Zod field to process\n * @returns The unwrapped type if only one remains, otherwise `false`\n *\n * @example\n * Union with only nullish types filtered - returns single type\n * ```typescript\n * const field = z.union([z.string(), z.null(), z.undefined()]);\n * const result = tryStripNullishOnly(field);\n * // Result: z.string() (unwrapped)\n * ```\n *\n * @example\n * Union with multiple non-nullish types - returns false\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = tryStripNullishOnly(field);\n * // Result: false (cannot simplify to single type)\n * ```\n *\n * @example\n * Non-union type - returns false\n * ```typescript\n * const field = z.string();\n * const result = tryStripNullishOnly(field);\n * // Result: false (not a union)\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.5.0\n */\nexport function tryStripNullishOnly(field: z.ZodTypeAny): z.ZodType | false {\n if (field instanceof z.ZodUnion) {\n const unionOptions = [...field.def.options];\n\n const filteredOptions = unionOptions.filter(\n (option): option is z.ZodType =>\n !(option instanceof z.ZodNull) && !(option instanceof z.ZodUndefined),\n );\n\n // If exactly one option remains, return it unwrapped\n const firstOption = filteredOptions[0];\n if (firstOption && filteredOptions.length === 1) {\n return firstOption;\n }\n }\n\n // Not a union, or couldn't simplify to single type\n return false;\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 * **Union handling:** For union types, strips nullish types (null/undefined) first. If only one\n * type remains after stripping, unwraps to that type. If multiple non-nullish types remain,\n * returns the union as-is (does not unwrap).\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 * @example\n * Union with only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.union([z.string(), z.number()]) (returned as-is)\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n return getPrimitiveType(unwrapped);\n }\n\n // Multiple non-nullish types or all nullish - return union as-is\n return 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\n/**\n * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.\n *\n * This advanced TypeScript utility type walks through a union's options tuple at compile-time,\n * checking each schema against the discriminator field and value, and returns the exact matching\n * schema type (not a union of all options).\n *\n * **How it works:**\n * 1. Extracts the options tuple from the union using `infer Options`\n * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching\n * 3. Checks if `First` is a ZodObject with the matching discriminator field and value\n * 4. If match found, returns `First` (the exact schema type)\n * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted\n *\n * **Type narrowing magic:**\n * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`\n * - Discriminator value: `'a'` (matches SchemaA)\n * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)\n *\n * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type\n * @template TDiscriminatorKey - The discriminator field name (e.g., \"type\", \"mode\")\n * @template TDiscriminatorValue - The specific discriminator value (e.g., \"create\", \"edit\")\n * @returns The exact matching schema type, or `never` if no match found\n *\n * @example\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })\n * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;\n * ```\n */\ntype ExtractZodUnionMember<\n TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n> = TSchema extends z.ZodUnion<infer Options>\n ? Options extends readonly [\n infer First extends z.ZodTypeAny,\n ...infer Rest extends z.ZodTypeAny[],\n ]\n ? First extends z.ZodObject<infer Shape>\n ? TDiscriminatorKey extends keyof Shape\n ? Shape[TDiscriminatorKey] extends z.ZodLiteral<TDiscriminatorValue>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionOutput<\n Rest[number]\n >[TDiscriminatorKey]\n ? ExtractZodUnionMember<\n z.ZodUnion<Rest>,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n : never\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionOutput<\n Rest[number]\n >[TDiscriminatorKey]\n ? ExtractZodUnionMember<\n z.ZodUnion<Rest>,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n : never\n : never\n : never\n : never;\n\n/**\n * Extracts a specific schema option from a discriminated union based on the discriminator field value.\n *\n * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by\n * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to\n * extract defaults from the correct schema variant in a discriminated union.\n *\n * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the\n * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at\n * compile-time based on the discriminator value.\n *\n * **How it works:**\n * 1. Iterates through all options in the discriminated union at runtime\n * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches\n * 3. Returns the first matching schema with its exact type narrowed at compile-time\n * 4. Returns `undefined` if no match found or if option is not a ZodObject\n *\n * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type\n * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)\n * @template TDiscriminatorValue - The specific discriminator value to match (literal type)\n * @param params - Parameters object\n * @param params.schema - The discriminated union schema to search\n * @param params.discriminatorKey - The discriminator field name (e.g., \"mode\", \"type\")\n * @param params.discriminatorValue - The discriminator value to match (e.g., \"create\", \"edit\")\n * @returns The exact matching schema option (with precise type), or `undefined` if not found\n *\n * @example\n * Basic discriminated union - create/edit mode\n * ```typescript\n * const userSchema = z.discriminatedUnion('mode', [\n * z.object({\n * mode: z.literal('create'),\n * name: z.string(),\n * age: z.number().optional(),\n * }),\n * z.object({\n * mode: z.literal('edit'),\n * id: z.number(),\n * name: z.string().optional(),\n * }),\n * ]);\n *\n * // Extract the \"create\" schema\n * const createSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'create',\n * });\n * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })\n *\n * // Extract the \"edit\" schema\n * const editSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'edit',\n * });\n * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })\n * ```\n *\n * @example\n * Type-based discrimination\n * ```typescript\n * const eventSchema = z.discriminatedUnion('type', [\n * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),\n * z.object({ type: z.literal('keypress'), key: z.string() }),\n * ]);\n *\n * const clickSchema = extractDiscriminatedSchema({\n * schema: eventSchema,\n * discriminatorKey: 'type',\n * discriminatorValue: 'click',\n * });\n * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })\n * ```\n *\n * @example\n * Invalid discriminator value\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * ]);\n *\n * const result = extractDiscriminatedSchema({\n * schema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'invalid', // doesn't match any option\n * });\n * // Result: undefined\n * ```\n *\n * @example\n * Type narrowing demonstration\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),\n * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),\n * ]);\n *\n * const createSchema = extractDiscriminatedSchema({\n * schema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'create',\n * });\n *\n * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })\n * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined\n *\n * if (createSchema) {\n * createSchema.shape.age; // ✅ TypeScript knows 'age' exists\n * createSchema.shape.name; // ✅ TypeScript knows 'name' exists\n * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema\n * }\n * ```\n *\n * @see {@link getSchemaDefaults} for usage with discriminated unions\n * @see {@link ExtractZodUnionMember} for the type-level extraction logic\n * @since 0.6.0\n */\nexport const extractDiscriminatedSchema = <\n TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n}) => {\n return schema.options.find(\n (\n option,\n ): option is ExtractZodUnionMember<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n > => {\n if (option instanceof z.ZodObject) {\n const targetField = option.shape[String(key)];\n if (!targetField) return false;\n\n const parseResult = targetField.safeParse(value);\n return parseResult.success;\n }\n return false;\n },\n );\n};\n","import type { util } from 'zod';\nimport * as z from 'zod';\nimport {\n canUnwrap,\n extractDiscriminatedSchema,\n tryStripNullishOnly,\n} 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, strips nullish types (null/undefined) first. If only one type\n * remains after stripping, extracts the default from that type. If multiple non-nullish types remain,\n * returns `undefined` (does not extract from any option).\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 only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.null()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (multiple non-nullish types - no default extracted)\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 * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n // Successfully unwrapped to single type\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapped) as z.infer<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema, returning only fields with explicit `.default()`.\n *\n * This function traverses the schema and collects fields that have explicit default values.\n * 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 * **Component handling:** For form inputs without explicit defaults (like `z.string()` or `z.number()`),\n * use the `?? ''` pattern in your components: `<Input value={field.value ?? ''} />`\n *\n * @template TSchema - The Zod object schema type\n * @param targetSchema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with explicit default values\n *\n * @example\n * Basic usage - only explicit defaults\n * ```typescript\n * const schema = z.object({\n * name: z.string(), // no default → NOT included\n * age: z.number(), // no default → NOT included\n * role: z.string().default('user'), // explicit default → included\n * count: z.number().default(0), // explicit default → included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { role: 'user', count: 0 }\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 * name: z.string().optional(), // no default → NOT included\n * age: z.number().optional(), // no default → NOT included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @example\n * Component usage for fields without defaults\n * ```typescript\n * // For string/number fields without defaults, handle in components:\n * <Input value={field.value ?? ''} />\n * <Input type=\"number\" value={field.value ?? ''} />\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>(\n schema: TSchema,\n options?: {\n discriminator?: {\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n };\n },\n): Simplify<Partial<z.infer<TSchema>>> {\n let targetSchema: z.ZodObject | undefined;\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema,\n ...options.discriminator,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n const defaults: Record<string, unknown> = {};\n\n if (targetSchema) {\n for (const key in targetSchema.shape) {\n const field = targetSchema.shape[key];\n if (!field) continue;\n\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<TSchema>>;\n}\n","import { type util, z } from 'zod';\nimport { extractDiscriminatedSchema } from './schema';\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion,\n TName extends keyof Extract<\n Required<z.infer<TSchema>>,\n Record<TDiscriminatorKey, TDiscriminatorValue>\n >,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>({\n schema,\n fieldName,\n discriminator,\n}: {\n schema: TSchema;\n fieldName: TName;\n discriminator?: {\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n };\n}) {\n let targetSchema: z.ZodObject | undefined;\n\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema,\n ...discriminator,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n if (!targetSchema) return undefined;\n\n const field: z.ZodType = targetSchema.shape[String(fieldName)];\n\n return field;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,22 @@
1
1
  import * as z from 'zod';
2
+ import { z as z$1 } from 'zod';
2
3
 
3
- // src/defaults.ts
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
4
20
  function canUnwrap(field) {
5
21
  return "unwrap" in field && typeof field.unwrap === "function";
6
22
  }
@@ -67,15 +83,20 @@ function getFieldChecks(field) {
67
83
  }
68
84
  var extractDiscriminatedSchema = ({
69
85
  schema,
70
- discriminatorField,
71
- discriminatorValue
86
+ key,
87
+ value
72
88
  }) => {
73
- return schema.options.find((option) => {
74
- const targetField = option.shape[String(discriminatorField)];
75
- if (!targetField) return false;
76
- const parseResult = targetField.safeParse(discriminatorValue);
77
- return parseResult.success;
78
- });
89
+ return schema.options.find(
90
+ (option) => {
91
+ if (option instanceof z.ZodObject) {
92
+ const targetField = option.shape[String(key)];
93
+ if (!targetField) return false;
94
+ const parseResult = targetField.safeParse(value);
95
+ return parseResult.success;
96
+ }
97
+ return false;
98
+ }
99
+ );
79
100
  };
80
101
 
81
102
  // src/defaults.ts
@@ -99,12 +120,9 @@ function getSchemaDefaults(schema, options) {
99
120
  let targetSchema;
100
121
  if (schema instanceof z.ZodDiscriminatedUnion) {
101
122
  if (options == null ? void 0 : options.discriminator) {
102
- const { field, value } = options.discriminator;
103
- targetSchema = extractDiscriminatedSchema({
104
- schema,
105
- discriminatorField: field,
106
- discriminatorValue: value
107
- });
123
+ targetSchema = extractDiscriminatedSchema(__spreadValues({
124
+ schema
125
+ }, options.discriminator));
108
126
  }
109
127
  } else {
110
128
  targetSchema = schema;
@@ -122,7 +140,26 @@ function getSchemaDefaults(schema, options) {
122
140
  }
123
141
  return defaults;
124
142
  }
143
+ function extractFieldFromSchema({
144
+ schema,
145
+ fieldName,
146
+ discriminator
147
+ }) {
148
+ let targetSchema;
149
+ if (schema instanceof z$1.ZodDiscriminatedUnion) {
150
+ if (discriminator) {
151
+ targetSchema = extractDiscriminatedSchema(__spreadValues({
152
+ schema
153
+ }, discriminator));
154
+ }
155
+ } else {
156
+ targetSchema = schema;
157
+ }
158
+ if (!targetSchema) return void 0;
159
+ const field = targetSchema.shape[String(fieldName)];
160
+ return field;
161
+ }
125
162
 
126
- export { canUnwrap, extractDefault, extractDiscriminatedSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
163
+ export { canUnwrap, extractDefault, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
127
164
  //# sourceMappingURL=index.mjs.map
128
165
  //# sourceMappingURL=index.mjs.map
@@ -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';\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 * Attempts to strip nullish types from a union and return the single remaining type.\n *\n * This function filters out `ZodNull` and `ZodUndefined` from union types. If exactly\n * one type remains after filtering, it returns that unwrapped type. Otherwise, it returns\n * `false` to indicate the union couldn't be simplified to a single type.\n *\n * @param field - The Zod field to process\n * @returns The unwrapped type if only one remains, otherwise `false`\n *\n * @example\n * Union with only nullish types filtered - returns single type\n * ```typescript\n * const field = z.union([z.string(), z.null(), z.undefined()]);\n * const result = tryStripNullishOnly(field);\n * // Result: z.string() (unwrapped)\n * ```\n *\n * @example\n * Union with multiple non-nullish types - returns false\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = tryStripNullishOnly(field);\n * // Result: false (cannot simplify to single type)\n * ```\n *\n * @example\n * Non-union type - returns false\n * ```typescript\n * const field = z.string();\n * const result = tryStripNullishOnly(field);\n * // Result: false (not a union)\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.5.0\n */\nexport function tryStripNullishOnly(field: z.ZodTypeAny): z.ZodType | false {\n if (field instanceof z.ZodUnion) {\n const unionOptions = [...field.def.options];\n\n const filteredOptions = unionOptions.filter(\n (option): option is z.ZodType =>\n !(option instanceof z.ZodNull) && !(option instanceof z.ZodUndefined),\n );\n\n // If exactly one option remains, return it unwrapped\n const firstOption = filteredOptions[0];\n if (firstOption && filteredOptions.length === 1) {\n return firstOption;\n }\n }\n\n // Not a union, or couldn't simplify to single type\n return false;\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 * **Union handling:** For union types, strips nullish types (null/undefined) first. If only one\n * type remains after stripping, unwraps to that type. If multiple non-nullish types remain,\n * returns the union as-is (does not unwrap).\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 * @example\n * Union with only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.union([z.string(), z.number()]) (returned as-is)\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n return getPrimitiveType(unwrapped);\n }\n\n // Multiple non-nullish types or all nullish - return union as-is\n return 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\n/**\n * Extracts a specific schema option from a discriminated union based on the discriminator field value.\n *\n * This function finds and returns the matching schema option from a `ZodDiscriminatedUnion` by\n * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to\n * extract defaults from the correct schema variant in a discriminated union.\n *\n * **How it works:**\n * 1. Iterates through all options in the discriminated union\n * 2. For each option, checks if the discriminator field matches the provided value\n * 3. Returns the first matching schema option, or `undefined` if no match found\n *\n * @template TSchema - The discriminated union schema type\n * @template TObj - The inferred type of the schema\n * @template TDiscriminatorField - The discriminator field key type\n * @param params - Parameters object\n * @param params.schema - The discriminated union schema to search\n * @param params.discriminatorField - The discriminator field name (e.g., \"mode\", \"type\")\n * @param params.discriminatorValue - The discriminator value to match (e.g., \"create\", \"edit\")\n * @returns The matching schema option, or `undefined` if not found\n *\n * @example\n * Basic discriminated union - create/edit mode\n * ```typescript\n * const userSchema = z.discriminatedUnion('mode', [\n * z.object({\n * mode: z.literal('create'),\n * name: z.string(),\n * age: z.number().optional(),\n * }),\n * z.object({\n * mode: z.literal('edit'),\n * id: z.number(),\n * name: z.string().optional(),\n * }),\n * ]);\n *\n * // Extract the \"create\" schema\n * const createSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'create',\n * });\n * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })\n *\n * // Extract the \"edit\" schema\n * const editSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'edit',\n * });\n * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })\n * ```\n *\n * @example\n * Type-based discrimination\n * ```typescript\n * const eventSchema = z.discriminatedUnion('type', [\n * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),\n * z.object({ type: z.literal('keypress'), key: z.string() }),\n * ]);\n *\n * const clickSchema = extractDiscriminatedSchema({\n * schema: eventSchema,\n * discriminatorField: 'type',\n * discriminatorValue: 'click',\n * });\n * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })\n * ```\n *\n * @example\n * Invalid discriminator value\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * ]);\n *\n * const result = extractDiscriminatedSchema({\n * schema,\n * discriminatorField: 'mode',\n * discriminatorValue: 'invalid', // doesn't match any option\n * });\n * // Result: undefined\n * ```\n *\n * @see {@link getSchemaDefaults} for usage with discriminated unions\n * @since 0.6.0\n */\nexport const extractDiscriminatedSchema = <\n TSchema extends z.ZodDiscriminatedUnion<Array<z.ZodObject>>,\n TObj extends z.infer<TSchema>,\n TDiscriminatorField extends keyof TObj = keyof TObj,\n>({\n schema,\n discriminatorField,\n discriminatorValue,\n}: {\n schema: TSchema;\n discriminatorField: TDiscriminatorField;\n discriminatorValue: TObj[TDiscriminatorField];\n}) => {\n return schema.options.find((option) => {\n const targetField = option.shape[String(discriminatorField)];\n if (!targetField) return false;\n\n const parseResult = targetField.safeParse(discriminatorValue);\n return parseResult.success;\n });\n};\n","import * as z from 'zod';\nimport {\n canUnwrap,\n extractDiscriminatedSchema,\n tryStripNullishOnly,\n} 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, strips nullish types (null/undefined) first. If only one type\n * remains after stripping, extracts the default from that type. If multiple non-nullish types remain,\n * returns `undefined` (does not extract from any option).\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 only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.null()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (multiple non-nullish types - no default extracted)\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 * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n // Successfully unwrapped to single type\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapped) as z.infer<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema, returning only fields with explicit `.default()`.\n *\n * This function traverses the schema and collects fields that have explicit default values.\n * 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 * **Component handling:** For form inputs without explicit defaults (like `z.string()` or `z.number()`),\n * use the `?? ''` pattern in your components: `<Input value={field.value ?? ''} />`\n *\n * @template TSchema - The Zod object schema type\n * @param targetSchema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with explicit default values\n *\n * @example\n * Basic usage - only explicit defaults\n * ```typescript\n * const schema = z.object({\n * name: z.string(), // no default → NOT included\n * age: z.number(), // no default → NOT included\n * role: z.string().default('user'), // explicit default → included\n * count: z.number().default(0), // explicit default → included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { role: 'user', count: 0 }\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 * name: z.string().optional(), // no default → NOT included\n * age: z.number().optional(), // no default → NOT included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @example\n * Component usage for fields without defaults\n * ```typescript\n * // For string/number fields without defaults, handle in components:\n * <Input value={field.value ?? ''} />\n * <Input type=\"number\" value={field.value ?? ''} />\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion<Array<z.ZodObject>>,\n TObj extends z.infer<TSchema>,\n TDiscriminatorField extends keyof TObj = keyof TObj,\n>(\n schema: TSchema,\n options?: {\n discriminator?: {\n field: TDiscriminatorField;\n value: TObj[TDiscriminatorField];\n };\n },\n): Simplify<Partial<z.infer<TSchema>>> {\n let targetSchema: z.ZodObject | undefined;\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n const { field, value } = options.discriminator;\n\n targetSchema = extractDiscriminatedSchema({\n schema,\n discriminatorField: field,\n discriminatorValue: value,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n const defaults: Record<string, unknown> = {};\n\n if (targetSchema) {\n for (const key in targetSchema.shape) {\n const field = targetSchema.shape[key];\n if (!field) continue;\n\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<TSchema>>;\n}\n"]}
1
+ {"version":3,"sources":["../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z2","z"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { util } from 'zod';\nimport * as z from 'zod';\nimport type {\n $InferUnionOutput,\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 * Attempts to strip nullish types from a union and return the single remaining type.\n *\n * This function filters out `ZodNull` and `ZodUndefined` from union types. If exactly\n * one type remains after filtering, it returns that unwrapped type. Otherwise, it returns\n * `false` to indicate the union couldn't be simplified to a single type.\n *\n * @param field - The Zod field to process\n * @returns The unwrapped type if only one remains, otherwise `false`\n *\n * @example\n * Union with only nullish types filtered - returns single type\n * ```typescript\n * const field = z.union([z.string(), z.null(), z.undefined()]);\n * const result = tryStripNullishOnly(field);\n * // Result: z.string() (unwrapped)\n * ```\n *\n * @example\n * Union with multiple non-nullish types - returns false\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const result = tryStripNullishOnly(field);\n * // Result: false (cannot simplify to single type)\n * ```\n *\n * @example\n * Non-union type - returns false\n * ```typescript\n * const field = z.string();\n * const result = tryStripNullishOnly(field);\n * // Result: false (not a union)\n * ```\n *\n * @see {@link getPrimitiveType} for unwrapping wrapper types\n * @since 0.5.0\n */\nexport function tryStripNullishOnly(field: z.ZodTypeAny): z.ZodType | false {\n if (field instanceof z.ZodUnion) {\n const unionOptions = [...field.def.options];\n\n const filteredOptions = unionOptions.filter(\n (option): option is z.ZodType =>\n !(option instanceof z.ZodNull) && !(option instanceof z.ZodUndefined),\n );\n\n // If exactly one option remains, return it unwrapped\n const firstOption = filteredOptions[0];\n if (firstOption && filteredOptions.length === 1) {\n return firstOption;\n }\n }\n\n // Not a union, or couldn't simplify to single type\n return false;\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 * **Union handling:** For union types, strips nullish types (null/undefined) first. If only one\n * type remains after stripping, unwraps to that type. If multiple non-nullish types remain,\n * returns the union as-is (does not unwrap).\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 * @example\n * Union with only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string(), z.null()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.string() (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string(), z.number()]);\n * const primitive = getPrimitiveType(field);\n * // Result: z.union([z.string(), z.number()]) (returned as-is)\n * ```\n *\n * @see {@link canUnwrap} for checking if a field can be unwrapped\n * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n return getPrimitiveType(unwrapped);\n }\n\n // Multiple non-nullish types or all nullish - return union as-is\n return 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\n/**\n * Recursively extracts the exact schema type from a discriminated union based on the discriminator value.\n *\n * This advanced TypeScript utility type walks through a union's options tuple at compile-time,\n * checking each schema against the discriminator field and value, and returns the exact matching\n * schema type (not a union of all options).\n *\n * **How it works:**\n * 1. Extracts the options tuple from the union using `infer Options`\n * 2. Destructure into head (`First`) and tail (`Rest`) using tuple pattern matching\n * 3. Checks if `First` is a ZodObject with the matching discriminator field and value\n * 4. If match found, returns `First` (the exact schema type)\n * 5. If no match, recursively processes `Rest` until a match is found or list is exhausted\n *\n * **Type narrowing magic:**\n * - Input: `z.discriminatedUnion('type', [SchemaA, SchemaB, SchemaC])`\n * - Discriminator value: `'a'` (matches SchemaA)\n * - Output: `SchemaA` (exact type, not `SchemaA | SchemaB | SchemaC`)\n *\n * @template TSchema - The ZodUnion or ZodDiscriminatedUnion type\n * @template TDiscriminatorKey - The discriminator field name (e.g., \"type\", \"mode\")\n * @template TDiscriminatorValue - The specific discriminator value (e.g., \"create\", \"edit\")\n * @returns The exact matching schema type, or `never` if no match found\n *\n * @example\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * // Exact type: z.object({ mode: z.literal('create'), name: z.string() })\n * type CreateSchema = ExtractZodUnionMember<typeof schema, 'mode', 'create'>;\n * ```\n */\ntype ExtractZodUnionMember<\n TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n> = TSchema extends z.ZodUnion<infer Options>\n ? Options extends readonly [\n infer First extends z.ZodTypeAny,\n ...infer Rest extends z.ZodTypeAny[],\n ]\n ? First extends z.ZodObject<infer Shape>\n ? TDiscriminatorKey extends keyof Shape\n ? Shape[TDiscriminatorKey] extends z.ZodLiteral<TDiscriminatorValue>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionOutput<\n Rest[number]\n >[TDiscriminatorKey]\n ? ExtractZodUnionMember<\n z.ZodUnion<Rest>,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n : never\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionOutput<\n Rest[number]\n >[TDiscriminatorKey]\n ? ExtractZodUnionMember<\n z.ZodUnion<Rest>,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n : never\n : never\n : never\n : never;\n\n/**\n * Extracts a specific schema option from a discriminated union based on the discriminator field value.\n *\n * This function finds and returns the **exact matching schema** from a `ZodDiscriminatedUnion` by\n * comparing the discriminator field value. It's used internally by {@link getSchemaDefaults} to\n * extract defaults from the correct schema variant in a discriminated union.\n *\n * **Key feature:** Returns the **exact schema type**, not a union of all options, thanks to the\n * {@link ExtractZodUnionMember} recursive type utility. This enables precise type narrowing at\n * compile-time based on the discriminator value.\n *\n * **How it works:**\n * 1. Iterates through all options in the discriminated union at runtime\n * 2. For each option, validates it's a ZodObject and checks if the discriminator field matches\n * 3. Returns the first matching schema with its exact type narrowed at compile-time\n * 4. Returns `undefined` if no match found or if option is not a ZodObject\n *\n * @template TSchema - The ZodUnion or ZodDiscriminatedUnion schema type\n * @template TDiscriminatorKey - The discriminator field name (string key of the inferred union type)\n * @template TDiscriminatorValue - The specific discriminator value to match (literal type)\n * @param params - Parameters object\n * @param params.schema - The discriminated union schema to search\n * @param params.discriminatorKey - The discriminator field name (e.g., \"mode\", \"type\")\n * @param params.discriminatorValue - The discriminator value to match (e.g., \"create\", \"edit\")\n * @returns The exact matching schema option (with precise type), or `undefined` if not found\n *\n * @example\n * Basic discriminated union - create/edit mode\n * ```typescript\n * const userSchema = z.discriminatedUnion('mode', [\n * z.object({\n * mode: z.literal('create'),\n * name: z.string(),\n * age: z.number().optional(),\n * }),\n * z.object({\n * mode: z.literal('edit'),\n * id: z.number(),\n * name: z.string().optional(),\n * }),\n * ]);\n *\n * // Extract the \"create\" schema\n * const createSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'create',\n * });\n * // Result: z.object({ mode: z.literal('create'), name: z.string(), age: z.number().optional() })\n *\n * // Extract the \"edit\" schema\n * const editSchema = extractDiscriminatedSchema({\n * schema: userSchema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'edit',\n * });\n * // Result: z.object({ mode: z.literal('edit'), id: z.number(), name: z.string().optional() })\n * ```\n *\n * @example\n * Type-based discrimination\n * ```typescript\n * const eventSchema = z.discriminatedUnion('type', [\n * z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),\n * z.object({ type: z.literal('keypress'), key: z.string() }),\n * ]);\n *\n * const clickSchema = extractDiscriminatedSchema({\n * schema: eventSchema,\n * discriminatorKey: 'type',\n * discriminatorValue: 'click',\n * });\n * // Result: z.object({ type: z.literal('click'), x: z.number(), y: z.number() })\n * ```\n *\n * @example\n * Invalid discriminator value\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string() }),\n * ]);\n *\n * const result = extractDiscriminatedSchema({\n * schema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'invalid', // doesn't match any option\n * });\n * // Result: undefined\n * ```\n *\n * @example\n * Type narrowing demonstration\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),\n * z.object({ mode: z.literal('edit'), id: z.number(), bio: z.string() }),\n * ]);\n *\n * const createSchema = extractDiscriminatedSchema({\n * schema,\n * discriminatorKey: 'mode',\n * discriminatorValue: 'create',\n * });\n *\n * // Type is EXACTLY: z.object({ mode: z.literal('create'), name: z.string(), age: z.number() })\n * // NOT: z.object({ mode: ..., ... }) | z.object({ mode: ..., ... }) | undefined\n *\n * if (createSchema) {\n * createSchema.shape.age; // ✅ TypeScript knows 'age' exists\n * createSchema.shape.name; // ✅ TypeScript knows 'name' exists\n * // createSchema.shape.id; // ❌ TypeScript error: 'id' doesn't exist on 'create' schema\n * }\n * ```\n *\n * @see {@link getSchemaDefaults} for usage with discriminated unions\n * @see {@link ExtractZodUnionMember} for the type-level extraction logic\n * @since 0.6.0\n */\nexport const extractDiscriminatedSchema = <\n TSchema extends z.ZodUnion | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n}) => {\n return schema.options.find(\n (\n option,\n ): option is ExtractZodUnionMember<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n > => {\n if (option instanceof z.ZodObject) {\n const targetField = option.shape[String(key)];\n if (!targetField) return false;\n\n const parseResult = targetField.safeParse(value);\n return parseResult.success;\n }\n return false;\n },\n );\n};\n","import type { util } from 'zod';\nimport * as z from 'zod';\nimport {\n canUnwrap,\n extractDiscriminatedSchema,\n tryStripNullishOnly,\n} 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, strips nullish types (null/undefined) first. If only one type\n * remains after stripping, extracts the default from that type. If multiple non-nullish types remain,\n * returns `undefined` (does not extract from any option).\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 only nullish types stripped to single type\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.null()]);\n * const defaultValue = extractDefault(field);\n * // Result: 'hello' (null stripped, leaving only string)\n * ```\n *\n * @example\n * Union with multiple non-nullish types\n * ```typescript\n * const field = z.union([z.string().default('hello'), z.number()]);\n * const defaultValue = extractDefault(field);\n * // Result: undefined (multiple non-nullish types - no default extracted)\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 * @see {@link tryStripNullishOnly} for union nullish stripping logic\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 const unwrapped = tryStripNullishOnly(field);\n if (unwrapped !== false) {\n // Successfully unwrapped to single type\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefault(unwrapped) as z.infer<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n return undefined;\n}\n\n/**\n * Extracts default values from a Zod object schema, returning only fields with explicit `.default()`.\n *\n * This function traverses the schema and collects fields that have explicit default values.\n * 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 * **Component handling:** For form inputs without explicit defaults (like `z.string()` or `z.number()`),\n * use the `?? ''` pattern in your components: `<Input value={field.value ?? ''} />`\n *\n * @template TSchema - The Zod object schema type\n * @param targetSchema - The Zod object schema to extract defaults from\n * @returns A partial object containing only fields with explicit default values\n *\n * @example\n * Basic usage - only explicit defaults\n * ```typescript\n * const schema = z.object({\n * name: z.string(), // no default → NOT included\n * age: z.number(), // no default → NOT included\n * role: z.string().default('user'), // explicit default → included\n * count: z.number().default(0), // explicit default → included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { role: 'user', count: 0 }\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 * name: z.string().optional(), // no default → NOT included\n * age: z.number().optional(), // no default → NOT included\n * });\n *\n * const defaults = getSchemaDefaults(schema);\n * // Result: { title: 'Untitled', count: 0 }\n * ```\n *\n * @example\n * Component usage for fields without defaults\n * ```typescript\n * // For string/number fields without defaults, handle in components:\n * <Input value={field.value ?? ''} />\n * <Input type=\"number\" value={field.value ?? ''} />\n * ```\n *\n * @see {@link extractDefault} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>(\n schema: TSchema,\n options?: {\n discriminator?: {\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n };\n },\n): Simplify<Partial<z.infer<TSchema>>> {\n let targetSchema: z.ZodObject | undefined;\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema,\n ...options.discriminator,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n const defaults: Record<string, unknown> = {};\n\n if (targetSchema) {\n for (const key in targetSchema.shape) {\n const field = targetSchema.shape[key];\n if (!field) continue;\n\n const defaultValue = extractDefault(field);\n if (defaultValue !== undefined) {\n defaults[key] = defaultValue;\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return defaults as Partial<z.infer<TSchema>>;\n}\n","import { type util, z } from 'zod';\nimport { extractDiscriminatedSchema } from './schema';\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodObject | z.ZodDiscriminatedUnion,\n TName extends keyof Extract<\n Required<z.infer<TSchema>>,\n Record<TDiscriminatorKey, TDiscriminatorValue>\n >,\n TDiscriminatorKey extends keyof z.infer<TSchema> & string,\n TDiscriminatorValue extends z.infer<TSchema>[TDiscriminatorKey] &\n util.Literal,\n>({\n schema,\n fieldName,\n discriminator,\n}: {\n schema: TSchema;\n fieldName: TName;\n discriminator?: {\n key: TDiscriminatorKey;\n value: TDiscriminatorValue;\n };\n}) {\n let targetSchema: z.ZodObject | undefined;\n\n if (schema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema,\n ...discriminator,\n });\n }\n } else {\n targetSchema = schema;\n }\n\n if (!targetSchema) return undefined;\n\n const field: z.ZodType = targetSchema.shape[String(fieldName)];\n\n return field;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zod-utils/core",
3
- "version": "0.9.0",
3
+ "version": "1.0.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",