@zod-utils/core 2.0.2 → 3.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
@@ -541,55 +541,41 @@ type EditInput = DiscriminatedInput<typeof schema, "mode", "edit">;
541
541
 
542
542
  ---
543
543
 
544
- ### `ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>`
544
+ ### `ValidPaths<TSchema, TDiscriminatorKey?, TDiscriminatorValue?, TFilterType?, TStrict?>`
545
545
 
546
- Generates all valid dot-notation paths for a schema. For discriminated unions, narrows to the specific variant first.
546
+ Generates valid dot-notation paths for a schema, with optional type filtering and discriminated union support.
547
547
 
548
- ```typescript
549
- import type { ValidPaths } from "@zod-utils/core";
550
- import { z } from "zod";
551
-
552
- const schema = z.discriminatedUnion("mode", [
553
- z.object({ mode: z.literal("create"), name: z.string() }),
554
- z.object({ mode: z.literal("edit"), id: z.number() }),
555
- ]);
556
-
557
- type CreatePaths = ValidPaths<typeof schema, "mode", "create">;
558
- // "mode" | "name"
559
-
560
- type EditPaths = ValidPaths<typeof schema, "mode", "edit">;
561
- // "mode" | "id"
562
- ```
563
-
564
- ---
548
+ **Parameters:**
549
+ - `TSchema` - The Zod schema type
550
+ - `TDiscriminatorKey` - Discriminator key for discriminated unions (default: `never`)
551
+ - `TDiscriminatorValue` - Discriminator value to filter variant (default: `never`)
552
+ - `TFilterType` - Type to filter paths by (default: `unknown` = all paths)
553
+ - `TStrict` - Strict mode for type matching (default: `true`)
565
554
 
566
- ### `ValidPathsOfType<TSchema, TValueConstraint, TDiscriminatorKey?, TDiscriminatorValue?>`
567
-
568
- Extracts field paths from a schema where the field value type matches a constraint. Filters schema keys to only those whose input type (with nullish stripped) extends the given `TValueConstraint`.
555
+ **Basic usage:**
569
556
 
570
557
  ```typescript
571
- import type { ValidPathsOfType } from "@zod-utils/core";
558
+ import type { ValidPaths } from "@zod-utils/core";
572
559
  import { z } from "zod";
573
560
 
574
561
  const schema = z.object({
575
562
  name: z.string(),
576
563
  age: z.number(),
577
564
  email: z.string().optional(),
578
- count: z.number().nullable(),
579
565
  active: z.boolean(),
580
566
  });
581
567
 
582
- // Get all string field paths
583
- type StringPaths = ValidPathsOfType<typeof schema, string>;
584
- // "name" | "email"
568
+ // Get all paths (no filtering)
569
+ type AllPaths = ValidPaths<typeof schema>;
570
+ // "name" | "age" | "email" | "active"
585
571
 
586
- // Get all number field paths
587
- type NumberPaths = ValidPathsOfType<typeof schema, number>;
588
- // "age" | "count"
572
+ // Filter by type - only string fields
573
+ type StringPaths = ValidPaths<typeof schema, never, never, string>;
574
+ // "name"
589
575
 
590
- // Get all boolean field paths
591
- type BooleanPaths = ValidPathsOfType<typeof schema, boolean>;
592
- // "active"
576
+ // Non-strict mode - includes optional string fields
577
+ type StringPathsNonStrict = ValidPaths<typeof schema, never, never, string, false>;
578
+ // "name" | "email"
593
579
  ```
594
580
 
595
581
  **With discriminated unions:**
@@ -600,39 +586,131 @@ const formSchema = z.discriminatedUnion("mode", [
600
586
  z.object({ mode: z.literal("edit"), id: z.number(), title: z.string() }),
601
587
  ]);
602
588
 
603
- // Get number paths for 'edit' variant only
604
- type EditNumberPaths = ValidPathsOfType<
605
- typeof formSchema,
606
- number,
607
- "mode",
608
- "edit"
609
- >;
610
- // "id"
589
+ // Get all paths for 'create' variant
590
+ type CreatePaths = ValidPaths<typeof formSchema, "mode", "create">;
591
+ // "mode" | "name" | "age"
611
592
 
612
- // Get string paths for 'create' variant
613
- type CreateStringPaths = ValidPathsOfType<
614
- typeof formSchema,
615
- string,
616
- "mode",
617
- "create"
618
- >;
619
- // "name"
593
+ // Get number paths for 'edit' variant
594
+ type EditNumberPaths = ValidPaths<typeof formSchema, "mode", "edit", number>;
595
+ // "id"
620
596
  ```
621
597
 
622
- **Array and object filtering:**
598
+ **Strict vs Non-Strict mode:**
623
599
 
624
600
  ```typescript
625
601
  const schema = z.object({
626
- tags: z.array(z.string()),
627
- scores: z.array(z.number()),
628
- profile: z.object({ bio: z.string() }),
602
+ required: z.string(),
603
+ optional: z.string().optional(),
604
+ nullable: z.string().nullable(),
629
605
  });
630
606
 
631
- type StringArrayPaths = ValidPathsOfType<typeof schema, string[]>;
632
- // "tags"
607
+ // Strict mode (default) - exact type matching
608
+ type StrictPaths = ValidPaths<typeof schema, never, never, string>;
609
+ // "required" - only exact string
610
+
611
+ // Non-strict mode - includes subtypes
612
+ type NonStrictPaths = ValidPaths<typeof schema, never, never, string, false>;
613
+ // "required" | "optional" | "nullable"
614
+ ```
615
+
616
+ ---
617
+
618
+ ### `Paths<T, FilterType?, Strict?>`
619
+
620
+ Low-level type utility for generating dot-notation paths from any type (not schema-specific).
621
+
622
+ ```typescript
623
+ import type { Paths } from "@zod-utils/core";
624
+
625
+ type User = {
626
+ name: string;
627
+ age: number;
628
+ profile: { bio: string };
629
+ };
630
+
631
+ // All paths
632
+ type AllPaths = Paths<User>;
633
+ // "name" | "age" | "profile" | "profile.bio"
634
+
635
+ // Filtered by string
636
+ type StringPaths = Paths<User, string>;
637
+ // "name" | "profile.bio"
638
+ ```
639
+
640
+ ---
641
+
642
+ ### `FieldSelector<TSchema, TName, TDiscriminatorKey?, TDiscriminatorValue?, TFilterType?, TStrict?>`
643
+
644
+ Utility type for creating typed parameter objects that include schema, field name, and optional discriminator. Useful for building type-safe form field components.
645
+
646
+ ```typescript
647
+ import type { FieldSelector } from "@zod-utils/core";
648
+ import { z } from "zod";
649
+
650
+ // Regular schema - no discriminator required
651
+ const userSchema = z.object({ name: z.string(), age: z.number() });
652
+ type UserParams = FieldSelector<typeof userSchema, "name">;
653
+ // { schema: typeof userSchema; name: "name" }
654
+
655
+ // Discriminated union - requires discriminator
656
+ const formSchema = z.discriminatedUnion("mode", [
657
+ z.object({ mode: z.literal("create"), name: z.string() }),
658
+ z.object({ mode: z.literal("edit"), id: z.number() }),
659
+ ]);
660
+ type FormParams = FieldSelector<typeof formSchema, "name", "mode", "create">;
661
+ // { schema: typeof formSchema; name: "name"; discriminator: { key: "mode"; value: "create" } }
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Migration Guide
667
+
668
+ ### Migrating to v3.0.0
669
+
670
+ #### `ValidPathsOfType` removed → Use `ValidPaths` with type filtering
671
+
672
+ The `ValidPathsOfType` type has been removed and consolidated into `ValidPaths` with a new `TFilterType` parameter.
673
+
674
+ **Before (v2.x):**
675
+ ```typescript
676
+ import type { ValidPathsOfType } from "@zod-utils/core";
677
+
678
+ // Get string field paths
679
+ type StringPaths = ValidPathsOfType<typeof schema, string>;
680
+
681
+ // With discriminated union
682
+ type EditNumberPaths = ValidPathsOfType<typeof schema, number, "mode", "edit">;
683
+ ```
684
+
685
+ **After (v3.x):**
686
+ ```typescript
687
+ import type { ValidPaths } from "@zod-utils/core";
688
+
689
+ // Get string field paths - use 4th type parameter
690
+ type StringPaths = ValidPaths<typeof schema, never, never, string>;
691
+
692
+ // With discriminated union - TFilterType is now 4th parameter
693
+ type EditNumberPaths = ValidPaths<typeof schema, "mode", "edit", number>;
694
+ ```
695
+
696
+ #### `Paths<T>` signature changed
697
+
698
+ The `Paths` type now accepts optional `FilterType` and `Strict` parameters.
699
+
700
+ **Before (v2.x):**
701
+ ```typescript
702
+ type AllPaths = Paths<User>; // Still works the same
703
+ ```
704
+
705
+ **After (v3.x):**
706
+ ```typescript
707
+ type AllPaths = Paths<User>; // Works the same (backward compatible)
708
+
709
+ // New: Filter by type
710
+ type StringPaths = Paths<User, string>;
633
711
 
634
- type ProfilePaths = ValidPathsOfType<typeof schema, { bio: string }>;
635
- // "profile"
712
+ // New: Non-strict mode
713
+ type StringPaths = Paths<User, string, false>;
636
714
  ```
637
715
 
638
716
  ---
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as z from 'zod';
2
2
  import { z as z$1, util } from 'zod';
3
- import { $InferUnionInput, $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
+ import { SomeType, $InferUnionInput, $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';
4
4
 
5
5
  /**
6
6
  * Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
@@ -40,7 +40,6 @@ import { $InferUnionInput, $ZodCheckLessThanDef, $ZodCheckGreaterThanDef, $ZodCh
40
40
  * // Shows clear: { id: string; name: string }
41
41
  * ```
42
42
  *
43
- * @since 0.1.0
44
43
  */
45
44
  type Simplify<T> = {
46
45
  [K in keyof T]: T[K];
@@ -77,27 +76,27 @@ type Discriminator<TSchema extends z$1.ZodType, TDiscriminatorKey extends Discri
77
76
  key: TDiscriminatorKey;
78
77
  value: TDiscriminatorValue;
79
78
  };
80
- type Primitive = string | number | boolean | null | undefined | symbol | bigint;
81
- type NonZeroDigit = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
82
- type PathsHint<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.0` | PathsHint<E, `${Prefix}.0`> : T extends object ? {
83
- [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsHint<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
84
- }[keyof T & string] : never;
85
- type NonZeroIndex = `${NonZeroDigit}` | `${NonZeroDigit}${number}`;
86
- type PathsLoose<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.${NonZeroIndex}` | PathsLoose<E, `${Prefix}.${NonZeroIndex}`> : T extends object ? {
87
- [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsLoose<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
88
- }[keyof T & string] : never;
89
- /**
90
- * Generates all valid dot-notation paths for a given type.
91
- * Supports nested objects and arrays with numeric indices.
92
- *
93
- * @example
94
- * ```typescript
95
- * type User = { name: string; address: { city: string } };
96
- * type UserPaths = Paths<User>;
97
- * // "name" | "address" | "address.city"
98
- * ```
99
- */
100
- type Paths<T> = PathsHint<T> | PathsLoose<T>;
79
+ interface FileList {
80
+ readonly length: number;
81
+ item(index: number): File | null;
82
+ [index: number]: File;
83
+ }
84
+ type Primitive = string | number | boolean | bigint | symbol | undefined | null;
85
+ type BrowserNativeObject = Date | FileList | File;
86
+ type ArrayKey = number;
87
+ type IsTuple<T extends ReadonlyArray<unknown>> = number extends T['length'] ? false : true;
88
+ type TupleKeys<T extends ReadonlyArray<unknown>> = Exclude<keyof T, keyof unknown[]>;
89
+ type IsEqual<T1, T2> = T1 extends T2 ? (<G>() => G extends T1 ? 1 : 2) extends <G>() => G extends T2 ? 1 : 2 ? true : false : false;
90
+ type AnyIsEqual<T1, T2> = T1 extends T2 ? IsEqual<T1, T2> extends true ? true : never : never;
91
+ type CheckFilter<V, FilterType, Strict extends boolean> = Strict extends true ? [V] extends [FilterType] ? true : false : V extends FilterType ? true : never;
92
+ type ArrayPaths = '${number}' | `${number}`;
93
+ type PathImpl<K extends string | number, V, TraversedTypes, FilterType = unknown, Strict extends boolean = true> = [V] extends [Primitive | BrowserNativeObject] ? CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never : true extends AnyIsEqual<TraversedTypes, V> ? CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never : K extends number ? (CheckFilter<V, FilterType, Strict> extends true ? ArrayPaths : never) | `${ArrayPaths}.${PathInternal<V, TraversedTypes | V, FilterType, Strict>}` : (CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never) | `${K}.${PathInternal<V, TraversedTypes | V, FilterType, Strict>}`;
94
+ type PathInternal<T, TraversedTypes = T, FilterType = unknown, Strict extends boolean = true> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
95
+ [K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes, FilterType, Strict>;
96
+ }[TupleKeys<T>] : PathImpl<ArrayKey, V, TraversedTypes, FilterType, Strict> : {
97
+ [K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes, FilterType, Strict>;
98
+ }[keyof T];
99
+ type Paths<T, FilterType = unknown, Strict extends boolean = true> = PathInternal<T, T, FilterType, Strict>;
101
100
  /**
102
101
  * Extracts fields common to all variants in a union type.
103
102
  *
@@ -113,6 +112,15 @@ type Paths<T> = PathsHint<T> | PathsLoose<T>;
113
112
  * ```
114
113
  */
115
114
  type CommonFields<T> = Pick<T, keyof T>;
115
+ /**
116
+ * Recursively unwraps Zod wrapper types to get the core schema type.
117
+ * Handles: ZodPipe (transform), ZodOptional, ZodNullable, ZodDefault
118
+ */
119
+ type UnwrapZodType<T> = T extends z$1.ZodPipe<infer In, SomeType> ? UnwrapZodType<In> : T extends z$1.ZodOptional<infer Inner> ? UnwrapZodType<Inner> : T extends z$1.ZodNullable<infer Inner> ? UnwrapZodType<Inner> : T extends z$1.ZodDefault<infer Inner> ? UnwrapZodType<Inner> : T;
120
+ /**
121
+ * Checks if the core (unwrapped) type is a ZodDiscriminatedUnion.
122
+ */
123
+ type IsDiscriminatedUnion<T> = UnwrapZodType<T> extends z$1.ZodDiscriminatedUnion ? true : false;
116
124
  /**
117
125
  * Extracts the input type from a discriminated union variant.
118
126
  *
@@ -137,9 +145,10 @@ type CommonFields<T> = Pick<T, keyof T>;
137
145
  * // { mode: 'edit'; id: number }
138
146
  * ```
139
147
  *
140
- * @since 0.5.0
141
148
  */
142
- type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>> = CommonFields<Extract<Required<z$1.input<TSchema>>, TDiscriminatorKey extends never ? z$1.input<TSchema> : TDiscriminatorValue extends never ? z$1.input<TSchema> : Simplify<Record<TDiscriminatorKey, TDiscriminatorValue>>>>;
149
+ type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never> = IsDiscriminatedUnion<TSchema> extends true ? Simplify<CommonFields<Extract<z$1.input<TSchema>, [
150
+ TDiscriminatorKey
151
+ ] extends [never] ? z$1.input<TSchema> : [TDiscriminatorValue] extends [never] ? z$1.input<TSchema> : Simplify<Record<TDiscriminatorKey, TDiscriminatorValue> | Partial<Record<TDiscriminatorKey, TDiscriminatorValue>>>>>> : z$1.input<TSchema>;
143
152
  /**
144
153
  * Generates valid dot-notation paths for fields in a discriminated union variant.
145
154
  *
@@ -160,50 +169,25 @@ type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends D
160
169
  * // "mode" | "id"
161
170
  * ```
162
171
  */
163
- type ValidPaths<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>> = Paths<DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>>;
164
- /**
165
- * Extracts field paths from a schema where the field value type matches a constraint.
166
- *
167
- * Filters schema keys to only those whose input type (with nullish stripped via `NonNullable`)
168
- * extends the given `TValueConstraint`. For discriminated unions, first narrows to the
169
- * specific variant before filtering.
170
- *
171
- * @template TSchema - The Zod schema type
172
- * @template TValueConstraint - The value type to filter by
173
- * @template TDiscriminatorKey - The discriminator key (for discriminated unions)
174
- * @template TDiscriminatorValue - The discriminator value (for discriminated unions)
175
- *
176
- * @example
177
- * Get all string field paths
178
- * ```typescript
179
- * const schema = z.object({
180
- * name: z.string(),
181
- * age: z.number(),
182
- * email: z.string().optional(),
183
- * });
184
- *
185
- * type StringPaths = ValidPathsOfType<typeof schema, string>;
186
- * // "name" | "email"
187
- * ```
188
- *
189
- * @example
190
- * With discriminated union - filter by type within a variant
191
- * ```typescript
192
- * const schema = z.discriminatedUnion('mode', [
193
- * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
194
- * z.object({ mode: z.literal('edit'), id: z.number(), name: z.string() }),
195
- * ]);
196
- *
197
- * type EditNumberPaths = ValidPathsOfType<typeof schema, number, 'mode', 'edit'>;
198
- * // "id" - only number fields in 'edit' variant
199
- * ```
200
- *
201
- * @see {@link ValidPaths} for discriminated union path filtering
202
- * @since 0.5.0
203
- */
204
- type ValidPathsOfType<TSchema extends z$1.ZodType, TValueConstraint, TDiscriminatorKey extends DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = DiscriminatorValue<TSchema, TDiscriminatorKey>, TVariant = DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>> = NonNullable<Extract<{
205
- [K in keyof TVariant]: NonNullable<TVariant[K]> extends TValueConstraint ? K : never;
206
- }[keyof TVariant], string> & ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>>;
172
+ type ValidPaths<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = Paths<DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>, TFilterType, TStrict>;
173
+ type InnerFieldSelector<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = TSchema extends z$1.ZodPipe<infer In> ? In extends z$1.ZodDiscriminatedUnion ? {
174
+ schema: TSchema;
175
+ name: TPath;
176
+ discriminator: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
177
+ } : {
178
+ schema: TSchema;
179
+ name: TPath;
180
+ discriminator?: never;
181
+ } : TSchema extends z$1.ZodDiscriminatedUnion ? {
182
+ schema: TSchema;
183
+ name: TPath;
184
+ discriminator: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
185
+ } : {
186
+ schema: TSchema;
187
+ name: TPath;
188
+ discriminator?: never;
189
+ };
190
+ type FieldSelector<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = InnerFieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>;
207
191
 
208
192
  /**
209
193
  * Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.
@@ -509,11 +493,7 @@ type NavigateZod<T, Path extends string[]> = Path extends [
509
493
  ...infer Rest extends string[]
510
494
  ] ? Unwrap<T> extends z$1.ZodObject<infer Shape> ? First extends keyof Shape ? Rest extends [] ? Shape[First] : NavigateZod<Shape[First], Rest> : never : Unwrap<T> extends z$1.ZodArray<infer Element> ? IsNumeric<First> extends true ? Rest extends [] ? Element : NavigateZod<Element, Rest> : never : never : T;
511
495
  type ExtractZodByPath<Schema, Path extends string> = NavigateZod<Schema, Split<Path>>;
512
- declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>>({ schema, name, discriminator, }: {
513
- schema: TSchema;
514
- name: TPath;
515
- discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
516
- }): (ExtractZodByPath<TSchema, TPath> & z$1.ZodType) | undefined;
496
+ declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, TFilterType = unknown, TStrict extends boolean = true>({ schema, name, discriminator, }: FieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>): (ExtractZodByPath<TSchema, TPath> & z$1.ZodType) | undefined;
517
497
  /**
518
498
  * Extends a Zod field with a transformation while preserving its metadata.
519
499
  *
@@ -534,6 +514,28 @@ declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath exten
534
514
  * ```
535
515
  */
536
516
  declare function extendWithMeta<T extends z$1.ZodType, R extends z$1.ZodType>(field: T, transform: (f: T) => R): R;
517
+ /**
518
+ * Merges factory props with component props into a FieldSelector.
519
+ * Encapsulates type assertion so callers don't need eslint-disable.
520
+ *
521
+ * @param factoryProps - Props from factory function (contains schema)
522
+ * @param componentProps - Props from component call (contains name, discriminator)
523
+ * @returns Properly typed FieldSelector
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const selectorProps = mergeFieldSelectorProps(
528
+ * { schema: mySchema },
529
+ * { name: 'fieldName', discriminator: { key: 'mode', value: 'create' } }
530
+ * );
531
+ * ```
532
+ */
533
+ declare function mergeFieldSelectorProps<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, TFilterType = unknown, TStrict extends boolean = true>(factoryProps: {
534
+ schema: TSchema;
535
+ }, componentProps: {
536
+ name: TPath;
537
+ discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
538
+ }): FieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>;
537
539
 
538
540
  /**
539
541
  * Type representing a Zod type that has an unwrap method
@@ -868,4 +870,4 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
868
870
  */
869
871
  declare function getFieldChecks<T extends z$1.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
870
872
 
871
- export { type CommonFields, type DiscriminatedInput, type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type ExtractZodByPath, type Paths, type Simplify, type ValidPaths, type ValidPathsOfType, type ZodUnionCheck, canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
873
+ export { type CommonFields, type DiscriminatedInput, type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type ExtractZodByPath, type FieldSelector, type InnerFieldSelector, type IsDiscriminatedUnion, type PathImpl, type PathInternal, type Paths, type Simplify, type UnwrapZodType, type ValidPaths, type ZodUnionCheck, canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, mergeFieldSelectorProps, removeDefault, requiresValidInput, tryStripNullishOnly };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as z from 'zod';
2
2
  import { z as z$1, util } from 'zod';
3
- import { $InferUnionInput, $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
+ import { SomeType, $InferUnionInput, $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';
4
4
 
5
5
  /**
6
6
  * Simplifies complex TypeScript types for better IDE hover tooltips and error messages.
@@ -40,7 +40,6 @@ import { $InferUnionInput, $ZodCheckLessThanDef, $ZodCheckGreaterThanDef, $ZodCh
40
40
  * // Shows clear: { id: string; name: string }
41
41
  * ```
42
42
  *
43
- * @since 0.1.0
44
43
  */
45
44
  type Simplify<T> = {
46
45
  [K in keyof T]: T[K];
@@ -77,27 +76,27 @@ type Discriminator<TSchema extends z$1.ZodType, TDiscriminatorKey extends Discri
77
76
  key: TDiscriminatorKey;
78
77
  value: TDiscriminatorValue;
79
78
  };
80
- type Primitive = string | number | boolean | null | undefined | symbol | bigint;
81
- type NonZeroDigit = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
82
- type PathsHint<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.0` | PathsHint<E, `${Prefix}.0`> : T extends object ? {
83
- [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsHint<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
84
- }[keyof T & string] : never;
85
- type NonZeroIndex = `${NonZeroDigit}` | `${NonZeroDigit}${number}`;
86
- type PathsLoose<T, Prefix extends string = ''> = T extends Primitive ? never : T extends (infer E)[] ? (Prefix extends '' ? never : Prefix) | `${Prefix}.${NonZeroIndex}` | PathsLoose<E, `${Prefix}.${NonZeroIndex}`> : T extends object ? {
87
- [K in keyof T & string]: (Prefix extends '' ? K : `${Prefix}.${K}`) | PathsLoose<T[K], Prefix extends '' ? K : `${Prefix}.${K}`>;
88
- }[keyof T & string] : never;
89
- /**
90
- * Generates all valid dot-notation paths for a given type.
91
- * Supports nested objects and arrays with numeric indices.
92
- *
93
- * @example
94
- * ```typescript
95
- * type User = { name: string; address: { city: string } };
96
- * type UserPaths = Paths<User>;
97
- * // "name" | "address" | "address.city"
98
- * ```
99
- */
100
- type Paths<T> = PathsHint<T> | PathsLoose<T>;
79
+ interface FileList {
80
+ readonly length: number;
81
+ item(index: number): File | null;
82
+ [index: number]: File;
83
+ }
84
+ type Primitive = string | number | boolean | bigint | symbol | undefined | null;
85
+ type BrowserNativeObject = Date | FileList | File;
86
+ type ArrayKey = number;
87
+ type IsTuple<T extends ReadonlyArray<unknown>> = number extends T['length'] ? false : true;
88
+ type TupleKeys<T extends ReadonlyArray<unknown>> = Exclude<keyof T, keyof unknown[]>;
89
+ type IsEqual<T1, T2> = T1 extends T2 ? (<G>() => G extends T1 ? 1 : 2) extends <G>() => G extends T2 ? 1 : 2 ? true : false : false;
90
+ type AnyIsEqual<T1, T2> = T1 extends T2 ? IsEqual<T1, T2> extends true ? true : never : never;
91
+ type CheckFilter<V, FilterType, Strict extends boolean> = Strict extends true ? [V] extends [FilterType] ? true : false : V extends FilterType ? true : never;
92
+ type ArrayPaths = '${number}' | `${number}`;
93
+ type PathImpl<K extends string | number, V, TraversedTypes, FilterType = unknown, Strict extends boolean = true> = [V] extends [Primitive | BrowserNativeObject] ? CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never : true extends AnyIsEqual<TraversedTypes, V> ? CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never : K extends number ? (CheckFilter<V, FilterType, Strict> extends true ? ArrayPaths : never) | `${ArrayPaths}.${PathInternal<V, TraversedTypes | V, FilterType, Strict>}` : (CheckFilter<V, FilterType, Strict> extends true ? `${K}` : never) | `${K}.${PathInternal<V, TraversedTypes | V, FilterType, Strict>}`;
94
+ type PathInternal<T, TraversedTypes = T, FilterType = unknown, Strict extends boolean = true> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
95
+ [K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes, FilterType, Strict>;
96
+ }[TupleKeys<T>] : PathImpl<ArrayKey, V, TraversedTypes, FilterType, Strict> : {
97
+ [K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes, FilterType, Strict>;
98
+ }[keyof T];
99
+ type Paths<T, FilterType = unknown, Strict extends boolean = true> = PathInternal<T, T, FilterType, Strict>;
101
100
  /**
102
101
  * Extracts fields common to all variants in a union type.
103
102
  *
@@ -113,6 +112,15 @@ type Paths<T> = PathsHint<T> | PathsLoose<T>;
113
112
  * ```
114
113
  */
115
114
  type CommonFields<T> = Pick<T, keyof T>;
115
+ /**
116
+ * Recursively unwraps Zod wrapper types to get the core schema type.
117
+ * Handles: ZodPipe (transform), ZodOptional, ZodNullable, ZodDefault
118
+ */
119
+ type UnwrapZodType<T> = T extends z$1.ZodPipe<infer In, SomeType> ? UnwrapZodType<In> : T extends z$1.ZodOptional<infer Inner> ? UnwrapZodType<Inner> : T extends z$1.ZodNullable<infer Inner> ? UnwrapZodType<Inner> : T extends z$1.ZodDefault<infer Inner> ? UnwrapZodType<Inner> : T;
120
+ /**
121
+ * Checks if the core (unwrapped) type is a ZodDiscriminatedUnion.
122
+ */
123
+ type IsDiscriminatedUnion<T> = UnwrapZodType<T> extends z$1.ZodDiscriminatedUnion ? true : false;
116
124
  /**
117
125
  * Extracts the input type from a discriminated union variant.
118
126
  *
@@ -137,9 +145,10 @@ type CommonFields<T> = Pick<T, keyof T>;
137
145
  * // { mode: 'edit'; id: number }
138
146
  * ```
139
147
  *
140
- * @since 0.5.0
141
148
  */
142
- type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>> = CommonFields<Extract<Required<z$1.input<TSchema>>, TDiscriminatorKey extends never ? z$1.input<TSchema> : TDiscriminatorValue extends never ? z$1.input<TSchema> : Simplify<Record<TDiscriminatorKey, TDiscriminatorValue>>>>;
149
+ type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never> = IsDiscriminatedUnion<TSchema> extends true ? Simplify<CommonFields<Extract<z$1.input<TSchema>, [
150
+ TDiscriminatorKey
151
+ ] extends [never] ? z$1.input<TSchema> : [TDiscriminatorValue] extends [never] ? z$1.input<TSchema> : Simplify<Record<TDiscriminatorKey, TDiscriminatorValue> | Partial<Record<TDiscriminatorKey, TDiscriminatorValue>>>>>> : z$1.input<TSchema>;
143
152
  /**
144
153
  * Generates valid dot-notation paths for fields in a discriminated union variant.
145
154
  *
@@ -160,50 +169,25 @@ type DiscriminatedInput<TSchema extends z$1.ZodType, TDiscriminatorKey extends D
160
169
  * // "mode" | "id"
161
170
  * ```
162
171
  */
163
- type ValidPaths<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>> = Paths<DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>>;
164
- /**
165
- * Extracts field paths from a schema where the field value type matches a constraint.
166
- *
167
- * Filters schema keys to only those whose input type (with nullish stripped via `NonNullable`)
168
- * extends the given `TValueConstraint`. For discriminated unions, first narrows to the
169
- * specific variant before filtering.
170
- *
171
- * @template TSchema - The Zod schema type
172
- * @template TValueConstraint - The value type to filter by
173
- * @template TDiscriminatorKey - The discriminator key (for discriminated unions)
174
- * @template TDiscriminatorValue - The discriminator value (for discriminated unions)
175
- *
176
- * @example
177
- * Get all string field paths
178
- * ```typescript
179
- * const schema = z.object({
180
- * name: z.string(),
181
- * age: z.number(),
182
- * email: z.string().optional(),
183
- * });
184
- *
185
- * type StringPaths = ValidPathsOfType<typeof schema, string>;
186
- * // "name" | "email"
187
- * ```
188
- *
189
- * @example
190
- * With discriminated union - filter by type within a variant
191
- * ```typescript
192
- * const schema = z.discriminatedUnion('mode', [
193
- * z.object({ mode: z.literal('create'), name: z.string(), age: z.number() }),
194
- * z.object({ mode: z.literal('edit'), id: z.number(), name: z.string() }),
195
- * ]);
196
- *
197
- * type EditNumberPaths = ValidPathsOfType<typeof schema, number, 'mode', 'edit'>;
198
- * // "id" - only number fields in 'edit' variant
199
- * ```
200
- *
201
- * @see {@link ValidPaths} for discriminated union path filtering
202
- * @since 0.5.0
203
- */
204
- type ValidPathsOfType<TSchema extends z$1.ZodType, TValueConstraint, TDiscriminatorKey extends DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = DiscriminatorValue<TSchema, TDiscriminatorKey>, TVariant = DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>> = NonNullable<Extract<{
205
- [K in keyof TVariant]: NonNullable<TVariant[K]> extends TValueConstraint ? K : never;
206
- }[keyof TVariant], string> & ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>>;
172
+ type ValidPaths<TSchema extends z$1.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = Paths<DiscriminatedInput<TSchema, TDiscriminatorKey, TDiscriminatorValue>, TFilterType, TStrict>;
173
+ type InnerFieldSelector<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = TSchema extends z$1.ZodPipe<infer In> ? In extends z$1.ZodDiscriminatedUnion ? {
174
+ schema: TSchema;
175
+ name: TPath;
176
+ discriminator: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
177
+ } : {
178
+ schema: TSchema;
179
+ name: TPath;
180
+ discriminator?: never;
181
+ } : TSchema extends z$1.ZodDiscriminatedUnion ? {
182
+ schema: TSchema;
183
+ name: TPath;
184
+ discriminator: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
185
+ } : {
186
+ schema: TSchema;
187
+ name: TPath;
188
+ discriminator?: never;
189
+ };
190
+ type FieldSelector<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true> = InnerFieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>;
207
191
 
208
192
  /**
209
193
  * Extracts the default value from a Zod field, recursively unwrapping optional, nullable, and union layers.
@@ -509,11 +493,7 @@ type NavigateZod<T, Path extends string[]> = Path extends [
509
493
  ...infer Rest extends string[]
510
494
  ] ? Unwrap<T> extends z$1.ZodObject<infer Shape> ? First extends keyof Shape ? Rest extends [] ? Shape[First] : NavigateZod<Shape[First], Rest> : never : Unwrap<T> extends z$1.ZodArray<infer Element> ? IsNumeric<First> extends true ? Rest extends [] ? Element : NavigateZod<Element, Rest> : never : never : T;
511
495
  type ExtractZodByPath<Schema, Path extends string> = NavigateZod<Schema, Split<Path>>;
512
- declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>>({ schema, name, discriminator, }: {
513
- schema: TSchema;
514
- name: TPath;
515
- discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
516
- }): (ExtractZodByPath<TSchema, TPath> & z$1.ZodType) | undefined;
496
+ declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, TFilterType = unknown, TStrict extends boolean = true>({ schema, name, discriminator, }: FieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>): (ExtractZodByPath<TSchema, TPath> & z$1.ZodType) | undefined;
517
497
  /**
518
498
  * Extends a Zod field with a transformation while preserving its metadata.
519
499
  *
@@ -534,6 +514,28 @@ declare function extractFieldFromSchema<TSchema extends z$1.ZodType, TPath exten
534
514
  * ```
535
515
  */
536
516
  declare function extendWithMeta<T extends z$1.ZodType, R extends z$1.ZodType>(field: T, transform: (f: T) => R): R;
517
+ /**
518
+ * Merges factory props with component props into a FieldSelector.
519
+ * Encapsulates type assertion so callers don't need eslint-disable.
520
+ *
521
+ * @param factoryProps - Props from factory function (contains schema)
522
+ * @param componentProps - Props from component call (contains name, discriminator)
523
+ * @returns Properly typed FieldSelector
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const selectorProps = mergeFieldSelectorProps(
528
+ * { schema: mySchema },
529
+ * { name: 'fieldName', discriminator: { key: 'mode', value: 'create' } }
530
+ * );
531
+ * ```
532
+ */
533
+ declare function mergeFieldSelectorProps<TSchema extends z$1.ZodType, TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>, TDiscriminatorKey extends DiscriminatorKey<TSchema>, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>, TFilterType = unknown, TStrict extends boolean = true>(factoryProps: {
534
+ schema: TSchema;
535
+ }, componentProps: {
536
+ name: TPath;
537
+ discriminator?: Discriminator<TSchema, TDiscriminatorKey, TDiscriminatorValue>;
538
+ }): FieldSelector<TSchema, TPath, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict>;
537
539
 
538
540
  /**
539
541
  * Type representing a Zod type that has an unwrap method
@@ -868,4 +870,4 @@ type ZodUnionCheck = $ZodCheckLessThanDef | $ZodCheckGreaterThanDef | $ZodCheckM
868
870
  */
869
871
  declare function getFieldChecks<T extends z$1.ZodTypeAny>(field: T): Array<ZodUnionCheck>;
870
872
 
871
- export { type CommonFields, type DiscriminatedInput, type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type ExtractZodByPath, type Paths, type Simplify, type ValidPaths, type ValidPathsOfType, type ZodUnionCheck, canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
873
+ export { type CommonFields, type DiscriminatedInput, type Discriminator, type DiscriminatorKey, type DiscriminatorValue, type ExtractZodByPath, type FieldSelector, type InnerFieldSelector, type IsDiscriminatedUnion, type PathImpl, type PathInternal, type Paths, type Simplify, type UnwrapZodType, type ValidPaths, type ZodUnionCheck, canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, mergeFieldSelectorProps, removeDefault, requiresValidInput, tryStripNullishOnly };
package/dist/index.js CHANGED
@@ -212,6 +212,9 @@ function extendWithMeta(field, transform) {
212
212
  }
213
213
  return transformedField.meta(__spreadValues({}, meta));
214
214
  }
215
+ function mergeFieldSelectorProps(factoryProps, componentProps) {
216
+ return __spreadValues(__spreadValues({}, factoryProps), componentProps);
217
+ }
215
218
 
216
219
  exports.canUnwrap = canUnwrap;
217
220
  exports.extendWithMeta = extendWithMeta;
@@ -221,6 +224,7 @@ exports.extractFieldFromSchema = extractFieldFromSchema;
221
224
  exports.getFieldChecks = getFieldChecks;
222
225
  exports.getPrimitiveType = getPrimitiveType;
223
226
  exports.getSchemaDefaults = getSchemaDefaults;
227
+ exports.mergeFieldSelectorProps = mergeFieldSelectorProps;
224
228
  exports.removeDefault = removeDefault;
225
229
  exports.requiresValidInput = requiresValidInput;
226
230
  exports.tryStripNullishOnly = tryStripNullishOnly;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/discriminatedSchema.ts","../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z","z3"],"mappings":"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","file":"index.js","sourcesContent":["import { type util, z } from 'zod';\nimport type { $InferUnionInput } from 'zod/v4/core';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n} from './types';\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 DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends z.input<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\n | z.ZodLiteral<TDiscriminatorValue>\n | z.ZodDefault<z.ZodLiteral<TDiscriminatorValue>>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionInput<\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 $InferUnionInput<\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.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n ReturnType extends TSchema extends z.ZodDiscriminatedUnion\n ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue>\n : never,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n} & Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n>): ReturnType => {\n if (!(schema instanceof z.ZodDiscriminatedUnion)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return undefined as ReturnType;\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema.options.find((option) => {\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 }) as ReturnType;\n};\n","import { 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 if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n return getPrimitiveType(field.def.in);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { canUnwrap, getPrimitiveType, tryStripNullishOnly } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n Simplify,\n} 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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 extractDefaultValue<T extends z.ZodType>(\n field: T,\n): z.input<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.input<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.unwrap()) as z.input<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 extractDefaultValue(unwrapped) as z.input<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.def.in) as z.input<T>;\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 extractDefaultValue} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n schema: TSchema,\n options?: {\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): Simplify<Partial<z.input<TSchema>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n let targetSchema: z.ZodObject | undefined;\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...options.discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n targetSchema = primitiveSchema;\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 = extractDefaultValue(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.input<TSchema>>;\n}\n","import { z } from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { getPrimitiveType } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n ValidPaths,\n} from './types';\n\n// Split 'a.b.c' into ['a', 'b', 'c']\ntype Split<S extends string> = S extends `${infer Head}.${infer Tail}`\n ? [Head, ...Split<Tail>]\n : [S];\n\n// Check if string is numeric\ntype IsNumeric<S extends string> = S extends `${number}` ? true : false;\n\n// Unwrap ZodOptional, ZodNullable, ZodDefault\ntype Unwrap<T> = T extends z.ZodOptional<infer U>\n ? Unwrap<U>\n : T extends z.ZodNullable<infer U>\n ? Unwrap<U>\n : T extends z.ZodDefault<infer U>\n ? Unwrap<U>\n : T;\n\n// Navigate Zod schema by path array\ntype NavigateZod<T, Path extends string[]> = Path extends [\n infer First extends string,\n ...infer Rest extends string[],\n]\n ? Unwrap<T> extends z.ZodObject<infer Shape>\n ? First extends keyof Shape\n ? Rest extends []\n ? Shape[First]\n : NavigateZod<Shape[First], Rest>\n : never\n : Unwrap<T> extends z.ZodArray<infer Element>\n ? IsNumeric<First> extends true\n ? Rest extends []\n ? Element\n : NavigateZod<Element, Rest>\n : never\n : never\n : T;\n\nexport type ExtractZodByPath<Schema, Path extends string> = NavigateZod<\n Schema,\n Split<Path>\n>;\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n name,\n discriminator,\n}: {\n schema: TSchema;\n name: TPath;\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}): (ExtractZodByPath<TSchema, TPath> & z.ZodType) | undefined {\n let currentSchema: z.ZodType | undefined;\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n currentSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n currentSchema = primitiveSchema;\n }\n\n if (!currentSchema) return undefined;\n\n // Split name into segments (e.g., \"contact.email\" -> [\"contact\", \"email\"])\n const segments = String(name).split('.');\n\n for (const segment of segments) {\n if (!currentSchema) return undefined;\n\n const unwrapped: z.ZodType = getPrimitiveType(currentSchema);\n\n if (unwrapped instanceof z.ZodObject) {\n currentSchema = unwrapped.shape[segment];\n } else if (unwrapped instanceof z.ZodArray) {\n // Arrays are keyed by integers only\n if (/^\\d+$/.test(segment) && unwrapped.element instanceof z.ZodType) {\n currentSchema = unwrapped.element;\n } else {\n return undefined;\n }\n } else {\n return undefined;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return currentSchema as\n | (ExtractZodByPath<TSchema, TPath> & z.ZodType)\n | undefined;\n}\n\n/**\n * Extends a Zod field with a transformation while preserving its metadata.\n *\n * This is useful when you want to add validations or transformations to a field\n * but keep the original metadata (like translationKey) intact.\n *\n * @param field - The original Zod field\n * @param transform - A function that transforms the field\n * @returns The transformed field with preserved metadata\n *\n * @example\n * ```typescript\n * const baseField = z.string().meta({ translationKey: 'user.field.name' });\n *\n * // Add min/max validation while keeping the translationKey\n * const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));\n * extendedField.meta(); // { translationKey: 'user.field.name' }\n * ```\n */\nexport function extendWithMeta<T extends z.ZodType, R extends z.ZodType>(\n field: T,\n transform: (f: T) => R,\n): R {\n const transformedField = transform(field);\n const meta = field.meta();\n if (!meta) {\n return transformedField;\n }\n return transformedField.meta({ ...meta });\n}\n"]}
1
+ {"version":3,"sources":["../src/discriminatedSchema.ts","../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z","z3"],"mappings":"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","file":"index.js","sourcesContent":["import { type util, z } from 'zod';\nimport type { $InferUnionInput } from 'zod/v4/core';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n} from './types';\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 DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends z.input<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\n | z.ZodLiteral<TDiscriminatorValue>\n | z.ZodDefault<z.ZodLiteral<TDiscriminatorValue>>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionInput<\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 $InferUnionInput<\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.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n ReturnType extends TSchema extends z.ZodDiscriminatedUnion\n ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue>\n : never,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n} & Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n>): ReturnType => {\n if (!(schema instanceof z.ZodDiscriminatedUnion)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return undefined as ReturnType;\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema.options.find((option) => {\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 }) as ReturnType;\n};\n","import { 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 if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n return getPrimitiveType(field.def.in);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { canUnwrap, getPrimitiveType, tryStripNullishOnly } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n Simplify,\n} 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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 extractDefaultValue<T extends z.ZodType>(\n field: T,\n): z.input<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.input<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.unwrap()) as z.input<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 extractDefaultValue(unwrapped) as z.input<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.def.in) as z.input<T>;\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 extractDefaultValue} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n schema: TSchema,\n options?: {\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): Simplify<Partial<z.input<TSchema>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n let targetSchema: z.ZodObject | undefined;\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...options.discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n targetSchema = primitiveSchema;\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 = extractDefaultValue(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.input<TSchema>>;\n}\n","import { z } from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { getPrimitiveType } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n FieldSelector,\n ValidPaths,\n} from './types';\n\n// Split 'a.b.c' into ['a', 'b', 'c']\ntype Split<S extends string> = S extends `${infer Head}.${infer Tail}`\n ? [Head, ...Split<Tail>]\n : [S];\n\n// Check if string is numeric\ntype IsNumeric<S extends string> = S extends `${number}` ? true : false;\n\n// Unwrap ZodOptional, ZodNullable, ZodDefault\ntype Unwrap<T> = T extends z.ZodOptional<infer U>\n ? Unwrap<U>\n : T extends z.ZodNullable<infer U>\n ? Unwrap<U>\n : T extends z.ZodDefault<infer U>\n ? Unwrap<U>\n : T;\n\n// Navigate Zod schema by path array\ntype NavigateZod<T, Path extends string[]> = Path extends [\n infer First extends string,\n ...infer Rest extends string[],\n]\n ? Unwrap<T> extends z.ZodObject<infer Shape>\n ? First extends keyof Shape\n ? Rest extends []\n ? Shape[First]\n : NavigateZod<Shape[First], Rest>\n : never\n : Unwrap<T> extends z.ZodArray<infer Element>\n ? IsNumeric<First> extends true\n ? Rest extends []\n ? Element\n : NavigateZod<Element, Rest>\n : never\n : never\n : T;\n\nexport type ExtractZodByPath<Schema, Path extends string> = NavigateZod<\n Schema,\n Split<Path>\n>;\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>({\n schema,\n name,\n discriminator,\n}: FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n>): (ExtractZodByPath<TSchema, TPath> & z.ZodType) | undefined {\n let currentSchema: z.ZodType | undefined;\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n currentSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n currentSchema = primitiveSchema;\n }\n\n if (!currentSchema) return undefined;\n\n // Split name into segments (e.g., \"contact.email\" -> [\"contact\", \"email\"])\n const segments = String(name).split('.');\n\n for (const segment of segments) {\n if (!currentSchema) return undefined;\n\n const unwrapped: z.ZodType = getPrimitiveType(currentSchema);\n\n if (unwrapped instanceof z.ZodObject) {\n currentSchema = unwrapped.shape[segment];\n } else if (unwrapped instanceof z.ZodArray) {\n // Arrays are keyed by integers only\n if (/^\\d+$/.test(segment) && unwrapped.element instanceof z.ZodType) {\n currentSchema = unwrapped.element;\n } else {\n return undefined;\n }\n } else {\n return undefined;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return currentSchema as\n | (ExtractZodByPath<TSchema, TPath> & z.ZodType)\n | undefined;\n}\n\n/**\n * Extends a Zod field with a transformation while preserving its metadata.\n *\n * This is useful when you want to add validations or transformations to a field\n * but keep the original metadata (like translationKey) intact.\n *\n * @param field - The original Zod field\n * @param transform - A function that transforms the field\n * @returns The transformed field with preserved metadata\n *\n * @example\n * ```typescript\n * const baseField = z.string().meta({ translationKey: 'user.field.name' });\n *\n * // Add min/max validation while keeping the translationKey\n * const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));\n * extendedField.meta(); // { translationKey: 'user.field.name' }\n * ```\n */\nexport function extendWithMeta<T extends z.ZodType, R extends z.ZodType>(\n field: T,\n transform: (f: T) => R,\n): R {\n const transformedField = transform(field);\n const meta = field.meta();\n if (!meta) {\n return transformedField;\n }\n return transformedField.meta({ ...meta });\n}\n\n/**\n * Merges factory props with component props into a FieldSelector.\n * Encapsulates type assertion so callers don't need eslint-disable.\n *\n * @param factoryProps - Props from factory function (contains schema)\n * @param componentProps - Props from component call (contains name, discriminator)\n * @returns Properly typed FieldSelector\n *\n * @example\n * ```typescript\n * const selectorProps = mergeFieldSelectorProps(\n * { schema: mySchema },\n * { name: 'fieldName', discriminator: { key: 'mode', value: 'create' } }\n * );\n * ```\n */\nexport function mergeFieldSelectorProps<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n factoryProps: { schema: TSchema },\n componentProps: {\n name: TPath;\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return { ...factoryProps, ...componentProps } as FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -191,7 +191,10 @@ function extendWithMeta(field, transform) {
191
191
  }
192
192
  return transformedField.meta(__spreadValues({}, meta));
193
193
  }
194
+ function mergeFieldSelectorProps(factoryProps, componentProps) {
195
+ return __spreadValues(__spreadValues({}, factoryProps), componentProps);
196
+ }
194
197
 
195
- export { canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, removeDefault, requiresValidInput, tryStripNullishOnly };
198
+ export { canUnwrap, extendWithMeta, extractDefaultValue, extractDiscriminatedSchema, extractFieldFromSchema, getFieldChecks, getPrimitiveType, getSchemaDefaults, mergeFieldSelectorProps, removeDefault, requiresValidInput, tryStripNullishOnly };
196
199
  //# sourceMappingURL=index.mjs.map
197
200
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/discriminatedSchema.ts","../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z"],"mappings":"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","file":"index.mjs","sourcesContent":["import { type util, z } from 'zod';\nimport type { $InferUnionInput } from 'zod/v4/core';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n} from './types';\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 DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends z.input<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\n | z.ZodLiteral<TDiscriminatorValue>\n | z.ZodDefault<z.ZodLiteral<TDiscriminatorValue>>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionInput<\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 $InferUnionInput<\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.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n ReturnType extends TSchema extends z.ZodDiscriminatedUnion\n ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue>\n : never,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n} & Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n>): ReturnType => {\n if (!(schema instanceof z.ZodDiscriminatedUnion)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return undefined as ReturnType;\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema.options.find((option) => {\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 }) as ReturnType;\n};\n","import { 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 if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n return getPrimitiveType(field.def.in);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { canUnwrap, getPrimitiveType, tryStripNullishOnly } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n Simplify,\n} 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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 extractDefaultValue<T extends z.ZodType>(\n field: T,\n): z.input<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.input<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.unwrap()) as z.input<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 extractDefaultValue(unwrapped) as z.input<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.def.in) as z.input<T>;\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 extractDefaultValue} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n schema: TSchema,\n options?: {\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): Simplify<Partial<z.input<TSchema>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n let targetSchema: z.ZodObject | undefined;\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...options.discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n targetSchema = primitiveSchema;\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 = extractDefaultValue(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.input<TSchema>>;\n}\n","import { z } from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { getPrimitiveType } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n ValidPaths,\n} from './types';\n\n// Split 'a.b.c' into ['a', 'b', 'c']\ntype Split<S extends string> = S extends `${infer Head}.${infer Tail}`\n ? [Head, ...Split<Tail>]\n : [S];\n\n// Check if string is numeric\ntype IsNumeric<S extends string> = S extends `${number}` ? true : false;\n\n// Unwrap ZodOptional, ZodNullable, ZodDefault\ntype Unwrap<T> = T extends z.ZodOptional<infer U>\n ? Unwrap<U>\n : T extends z.ZodNullable<infer U>\n ? Unwrap<U>\n : T extends z.ZodDefault<infer U>\n ? Unwrap<U>\n : T;\n\n// Navigate Zod schema by path array\ntype NavigateZod<T, Path extends string[]> = Path extends [\n infer First extends string,\n ...infer Rest extends string[],\n]\n ? Unwrap<T> extends z.ZodObject<infer Shape>\n ? First extends keyof Shape\n ? Rest extends []\n ? Shape[First]\n : NavigateZod<Shape[First], Rest>\n : never\n : Unwrap<T> extends z.ZodArray<infer Element>\n ? IsNumeric<First> extends true\n ? Rest extends []\n ? Element\n : NavigateZod<Element, Rest>\n : never\n : never\n : T;\n\nexport type ExtractZodByPath<Schema, Path extends string> = NavigateZod<\n Schema,\n Split<Path>\n>;\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<TSchema, TDiscriminatorKey, TDiscriminatorValue>,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n name,\n discriminator,\n}: {\n schema: TSchema;\n name: TPath;\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}): (ExtractZodByPath<TSchema, TPath> & z.ZodType) | undefined {\n let currentSchema: z.ZodType | undefined;\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n currentSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n currentSchema = primitiveSchema;\n }\n\n if (!currentSchema) return undefined;\n\n // Split name into segments (e.g., \"contact.email\" -> [\"contact\", \"email\"])\n const segments = String(name).split('.');\n\n for (const segment of segments) {\n if (!currentSchema) return undefined;\n\n const unwrapped: z.ZodType = getPrimitiveType(currentSchema);\n\n if (unwrapped instanceof z.ZodObject) {\n currentSchema = unwrapped.shape[segment];\n } else if (unwrapped instanceof z.ZodArray) {\n // Arrays are keyed by integers only\n if (/^\\d+$/.test(segment) && unwrapped.element instanceof z.ZodType) {\n currentSchema = unwrapped.element;\n } else {\n return undefined;\n }\n } else {\n return undefined;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return currentSchema as\n | (ExtractZodByPath<TSchema, TPath> & z.ZodType)\n | undefined;\n}\n\n/**\n * Extends a Zod field with a transformation while preserving its metadata.\n *\n * This is useful when you want to add validations or transformations to a field\n * but keep the original metadata (like translationKey) intact.\n *\n * @param field - The original Zod field\n * @param transform - A function that transforms the field\n * @returns The transformed field with preserved metadata\n *\n * @example\n * ```typescript\n * const baseField = z.string().meta({ translationKey: 'user.field.name' });\n *\n * // Add min/max validation while keeping the translationKey\n * const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));\n * extendedField.meta(); // { translationKey: 'user.field.name' }\n * ```\n */\nexport function extendWithMeta<T extends z.ZodType, R extends z.ZodType>(\n field: T,\n transform: (f: T) => R,\n): R {\n const transformedField = transform(field);\n const meta = field.meta();\n if (!meta) {\n return transformedField;\n }\n return transformedField.meta({ ...meta });\n}\n"]}
1
+ {"version":3,"sources":["../src/discriminatedSchema.ts","../src/schema.ts","../src/defaults.ts","../src/field.ts"],"names":["z"],"mappings":"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","file":"index.mjs","sourcesContent":["import { type util, z } from 'zod';\nimport type { $InferUnionInput } from 'zod/v4/core';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n} from './types';\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 DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends z.input<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\n | z.ZodLiteral<TDiscriminatorValue>\n | z.ZodDefault<z.ZodLiteral<TDiscriminatorValue>>\n ? First\n : Rest extends []\n ? never\n : TDiscriminatorValue extends $InferUnionInput<\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 $InferUnionInput<\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.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n ReturnType extends TSchema extends z.ZodDiscriminatedUnion\n ? ExtractZodUnionMember<TSchema, TDiscriminatorKey, TDiscriminatorValue>\n : never,\n>({\n schema,\n key,\n value,\n}: {\n schema: TSchema;\n} & Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n>): ReturnType => {\n if (!(schema instanceof z.ZodDiscriminatedUnion)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return undefined as ReturnType;\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema.options.find((option) => {\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 }) as ReturnType;\n};\n","import { 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 if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n return getPrimitiveType(field.def.in);\n }\n\n return field;\n};\n\ntype StripZodDefault<T> = T extends z.ZodDefault<infer Inner>\n ? StripZodDefault<Inner>\n : T extends z.ZodOptional<infer Inner>\n ? z.ZodOptional<StripZodDefault<Inner>>\n : T extends z.ZodNullable<infer Inner>\n ? z.ZodNullable<StripZodDefault<Inner>>\n : T;\n\n/**\n * Removes default values from a Zod field while preserving other wrapper types.\n *\n * This function recursively removes `ZodDefault` wrappers from a field, while maintaining\n * `optional()` and `nullable()` wrappers. Useful for scenarios where you want to check\n * field requirements without considering default values.\n *\n * @template T - The Zod type to process\n * @param field - The Zod field to remove defaults from\n * @returns The field without defaults but with optional/nullable preserved\n *\n * @example\n * Removing simple default\n * ```typescript\n * const field = z.string().default('hello');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string()\n * ```\n *\n * @example\n * Preserving optional wrapper\n * ```typescript\n * const field = z.string().default('hello').optional();\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().optional()\n * ```\n *\n * @example\n * Nested defaults\n * ```typescript\n * const field = z.string().default('inner').nullable().default('outer');\n * const withoutDefault = removeDefault(field);\n * // Result: z.string().nullable()\n * ```\n *\n * @see {@link requiresValidInput} for usage with requirement checking\n * @since 0.1.0\n */\nexport function removeDefault<T extends z.ZodType>(\n field: T,\n): StripZodDefault<T> {\n if (field instanceof z.ZodDefault) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field.unwrap() as StripZodDefault<T>;\n }\n\n if ('innerType' in field.def && field.def.innerType instanceof z.ZodType) {\n const inner = removeDefault(field.def.innerType);\n // Reconstruct the wrapper with the modified inner type\n if (field instanceof z.ZodOptional) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.optional() as unknown as StripZodDefault<T>;\n }\n if (field instanceof z.ZodNullable) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return inner.nullable() as unknown as StripZodDefault<T>;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return field as StripZodDefault<T>;\n}\n\n/**\n * Determines if a field will show validation errors when the user submits empty or invalid input.\n *\n * This is useful for form UIs to indicate which fields require valid user input (e.g., showing\n * asterisks, validation states). The key insight: **defaults are just initial values** - they\n * don't prevent validation errors if the user clears the field.\n *\n * **Real-world example:**\n * ```typescript\n * // Marital status field with default but validation rules\n * const maritalStatus = z.string().min(1).default('single');\n *\n * // Initial: field shows \"single\" (from default)\n * // User deletes the value → field is now empty string\n * // User submits form → validation fails because .min(1) rejects empty strings\n * // requiresValidInput(maritalStatus) → true (shows * indicator, validation error)\n * ```\n *\n * **How it works:**\n * 1. Removes `.default()` wrappers (defaults are initial values, not validation rules)\n * 2. Tests if the underlying schema accepts empty/invalid input:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty string (plain `z.string()` without `.min(1)` or `.nonempty()`)\n * - Empty array (plain `z.array()` without `.min(1)` or `.nonempty()`)\n * 3. Returns `true` if validation will fail, `false` if empty input is accepted\n *\n * @template T - The Zod type to check\n * @param field - The Zod field to check\n * @returns True if the field will show validation errors on empty/invalid input, false otherwise\n *\n * @example\n * User name field - required, no default\n * ```typescript\n * const userName = z.string().min(1);\n * requiresValidInput(userName); // true - will error if user submits empty\n * ```\n *\n * @example\n * Marital status - required WITH default\n * ```typescript\n * const maritalStatus = z.string().min(1).default('single');\n * requiresValidInput(maritalStatus); // true - will error if user clears and submits\n * ```\n *\n * @example\n * Age with default - requires valid input\n * ```typescript\n * const age = z.number().default(0);\n * requiresValidInput(age); // true - numbers reject empty strings\n * ```\n *\n * @example\n * Optional bio field - doesn't require input\n * ```typescript\n * const bio = z.string().optional();\n * requiresValidInput(bio); // false - user can leave empty\n * ```\n *\n * @example\n * String with default but NO validation - doesn't require input\n * ```typescript\n * const notes = z.string().default('N/A');\n * requiresValidInput(notes); // false - plain z.string() accepts empty strings\n * ```\n *\n * @example\n * Nullable field - doesn't require input\n * ```typescript\n * const middleName = z.string().nullable();\n * requiresValidInput(middleName); // false - user can leave null\n * ```\n *\n * @see {@link removeDefault} for understanding how defaults are handled\n * @see {@link getPrimitiveType} for understanding type unwrapping\n * @since 0.1.0\n */\nexport const requiresValidInput = <T extends z.ZodType>(field: T) => {\n const defaultRemovedField = removeDefault(field);\n if (!(defaultRemovedField instanceof z.ZodType)) {\n return false;\n }\n\n const undefinedResult = defaultRemovedField.safeParse(undefined).success;\n\n // Check if field accepts null (nullable)\n const nullResult = defaultRemovedField.safeParse(null).success;\n\n const primitiveType = getPrimitiveType(defaultRemovedField);\n\n const emptyStringResult =\n primitiveType.type === 'string' &&\n defaultRemovedField.safeParse('').success;\n\n const emptyArrayResult =\n primitiveType.type === 'array' && defaultRemovedField.safeParse([]).success;\n\n return (\n !undefinedResult && !nullResult && !emptyStringResult && !emptyArrayResult\n );\n};\n\n/**\n * Union type of all Zod check definition types.\n *\n * Includes all validation check types supported by Zod v4:\n * - **Length checks**: `min_length`, `max_length`, `length_equals` (strings, arrays)\n * - **Size checks**: `min_size`, `max_size`, `size_equals` (files, sets, maps)\n * - **Numeric checks**: `greater_than`, `less_than`, `multiple_of`\n * - **Format checks**: `number_format` (int32, float64, etc.), `bigint_format`, `string_format` (email, url, uuid, etc.)\n * - **String pattern checks**: `regex`, `lowercase`, `uppercase`, `includes`, `starts_with`, `ends_with`\n * - **Other checks**: `property`, `mime_type`, `overwrite`\n *\n * @since 0.4.0\n */\nexport type ZodUnionCheck =\n | $ZodCheckLessThanDef\n | $ZodCheckGreaterThanDef\n | $ZodCheckMultipleOfDef\n | $ZodCheckNumberFormatDef\n | $ZodCheckBigIntFormatDef\n | $ZodCheckMaxSizeDef\n | $ZodCheckMinSizeDef\n | $ZodCheckSizeEqualsDef\n | $ZodCheckMaxLengthDef\n | $ZodCheckMinLengthDef\n | $ZodCheckLengthEqualsDef\n | $ZodCheckStringFormatDef\n | $ZodCheckRegexDef\n | $ZodCheckLowerCaseDef\n | $ZodCheckUpperCaseDef\n | $ZodCheckIncludesDef\n | $ZodCheckStartsWithDef\n | $ZodCheckEndsWithDef\n | $ZodCheckPropertyDef\n | $ZodCheckMimeTypeDef\n | $ZodCheckOverwriteDef;\n\n/**\n * Extracts all validation check definitions from a Zod schema field.\n *\n * This function analyzes a Zod field and returns all check definitions as defined\n * by Zod's internal structure. Returns Zod's raw check definition objects directly,\n * including all properties like `check`, `minimum`, `maximum`, `value`, `inclusive`,\n * `format`, `pattern`, etc.\n *\n * **Unwrapping behavior:** Automatically unwraps optional, nullable, and default layers.\n * For unions, checks only the first option (same as other schema utilities).\n *\n * **Supported check types:** Returns any of the 21 check types defined in {@link ZodUnionCheck},\n * including length, size, numeric range, format validation, string patterns, and more.\n *\n * @template T - The Zod type to extract checks from\n * @param field - The Zod field to analyze\n * @returns Array of Zod check definition objects (see {@link ZodUnionCheck})\n *\n * @example\n * String with length constraints\n * ```typescript\n * const username = z.string().min(3).max(20);\n * const checks = getFieldChecks(username);\n * // [\n * // { check: 'min_length', minimum: 3, when: [Function], ... },\n * // { check: 'max_length', maximum: 20, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Number with range constraints\n * ```typescript\n * const age = z.number().min(18).max(120);\n * const checks = getFieldChecks(age);\n * // [\n * // { check: 'greater_than', value: 18, inclusive: true, when: [Function], ... },\n * // { check: 'less_than', value: 120, inclusive: true, when: [Function], ... }\n * // ]\n * ```\n *\n * @example\n * Array with item count constraints\n * ```typescript\n * const tags = z.array(z.string()).min(1).max(5);\n * const checks = getFieldChecks(tags);\n * // [\n * // { check: 'min_length', minimum: 1, ... },\n * // { check: 'max_length', maximum: 5, ... }\n * // ]\n * ```\n *\n * @example\n * String with format validation\n * ```typescript\n * const email = z.string().email();\n * const checks = getFieldChecks(email);\n * // [\n * // { check: 'string_format', format: 'email', ... }\n * // ]\n * ```\n *\n * @example\n * Unwrapping optional/nullable/default layers\n * ```typescript\n * const bio = z.string().min(10).max(500).optional();\n * const checks = getFieldChecks(bio);\n * // [\n * // { check: 'min_length', minimum: 10, ... },\n * // { check: 'max_length', maximum: 500, ... }\n * // ]\n * ```\n *\n * @see {@link ZodUnionCheck} for all supported check types\n * @since 0.4.0\n */\nexport function getFieldChecks<T extends z.ZodTypeAny>(\n field: T,\n): Array<ZodUnionCheck> {\n const primitiveType = getPrimitiveType(field);\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return (primitiveType.def.checks?.map((check) => check._zod.def) ||\n []) as Array<ZodUnionCheck>;\n}\n","import * as z from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { canUnwrap, getPrimitiveType, tryStripNullishOnly } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n Simplify,\n} 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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 = extractDefaultValue(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 extractDefaultValue<T extends z.ZodType>(\n field: T,\n): z.input<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.input<T>;\n }\n\n if (canUnwrap(field)) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.unwrap()) as z.input<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 extractDefaultValue(unwrapped) as z.input<T>;\n }\n\n // Multiple non-nullish types or all nullish - no default\n return undefined;\n }\n\n if (field instanceof z.ZodPipe && field.def.in instanceof z.ZodType) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return extractDefaultValue(field.def.in) as z.input<T>;\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 extractDefaultValue} for extracting defaults from individual fields\n * @since 0.1.0\n */\nexport function getSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n schema: TSchema,\n options?: {\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): Simplify<Partial<z.input<TSchema>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n let targetSchema: z.ZodObject | undefined;\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (options?.discriminator) {\n targetSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...options.discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n targetSchema = primitiveSchema;\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 = extractDefaultValue(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.input<TSchema>>;\n}\n","import { z } from 'zod';\nimport { extractDiscriminatedSchema } from './discriminatedSchema';\nimport { getPrimitiveType } from './schema';\nimport type {\n Discriminator,\n DiscriminatorKey,\n DiscriminatorValue,\n FieldSelector,\n ValidPaths,\n} from './types';\n\n// Split 'a.b.c' into ['a', 'b', 'c']\ntype Split<S extends string> = S extends `${infer Head}.${infer Tail}`\n ? [Head, ...Split<Tail>]\n : [S];\n\n// Check if string is numeric\ntype IsNumeric<S extends string> = S extends `${number}` ? true : false;\n\n// Unwrap ZodOptional, ZodNullable, ZodDefault\ntype Unwrap<T> = T extends z.ZodOptional<infer U>\n ? Unwrap<U>\n : T extends z.ZodNullable<infer U>\n ? Unwrap<U>\n : T extends z.ZodDefault<infer U>\n ? Unwrap<U>\n : T;\n\n// Navigate Zod schema by path array\ntype NavigateZod<T, Path extends string[]> = Path extends [\n infer First extends string,\n ...infer Rest extends string[],\n]\n ? Unwrap<T> extends z.ZodObject<infer Shape>\n ? First extends keyof Shape\n ? Rest extends []\n ? Shape[First]\n : NavigateZod<Shape[First], Rest>\n : never\n : Unwrap<T> extends z.ZodArray<infer Element>\n ? IsNumeric<First> extends true\n ? Rest extends []\n ? Element\n : NavigateZod<Element, Rest>\n : never\n : never\n : T;\n\nexport type ExtractZodByPath<Schema, Path extends string> = NavigateZod<\n Schema,\n Split<Path>\n>;\n\nexport function extractFieldFromSchema<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>({\n schema,\n name,\n discriminator,\n}: FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n>): (ExtractZodByPath<TSchema, TPath> & z.ZodType) | undefined {\n let currentSchema: z.ZodType | undefined;\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const primitiveSchema = getPrimitiveType(schema) as TSchema;\n\n if (primitiveSchema instanceof z.ZodDiscriminatedUnion) {\n if (discriminator) {\n currentSchema = extractDiscriminatedSchema({\n schema: primitiveSchema,\n ...discriminator,\n });\n }\n } else if (primitiveSchema instanceof z.ZodObject) {\n currentSchema = primitiveSchema;\n }\n\n if (!currentSchema) return undefined;\n\n // Split name into segments (e.g., \"contact.email\" -> [\"contact\", \"email\"])\n const segments = String(name).split('.');\n\n for (const segment of segments) {\n if (!currentSchema) return undefined;\n\n const unwrapped: z.ZodType = getPrimitiveType(currentSchema);\n\n if (unwrapped instanceof z.ZodObject) {\n currentSchema = unwrapped.shape[segment];\n } else if (unwrapped instanceof z.ZodArray) {\n // Arrays are keyed by integers only\n if (/^\\d+$/.test(segment) && unwrapped.element instanceof z.ZodType) {\n currentSchema = unwrapped.element;\n } else {\n return undefined;\n }\n } else {\n return undefined;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return currentSchema as\n | (ExtractZodByPath<TSchema, TPath> & z.ZodType)\n | undefined;\n}\n\n/**\n * Extends a Zod field with a transformation while preserving its metadata.\n *\n * This is useful when you want to add validations or transformations to a field\n * but keep the original metadata (like translationKey) intact.\n *\n * @param field - The original Zod field\n * @param transform - A function that transforms the field\n * @returns The transformed field with preserved metadata\n *\n * @example\n * ```typescript\n * const baseField = z.string().meta({ translationKey: 'user.field.name' });\n *\n * // Add min/max validation while keeping the translationKey\n * const extendedField = extendWithMeta(baseField, (f) => f.min(3).max(100));\n * extendedField.meta(); // { translationKey: 'user.field.name' }\n * ```\n */\nexport function extendWithMeta<T extends z.ZodType, R extends z.ZodType>(\n field: T,\n transform: (f: T) => R,\n): R {\n const transformedField = transform(field);\n const meta = field.meta();\n if (!meta) {\n return transformedField;\n }\n return transformedField.meta({ ...meta });\n}\n\n/**\n * Merges factory props with component props into a FieldSelector.\n * Encapsulates type assertion so callers don't need eslint-disable.\n *\n * @param factoryProps - Props from factory function (contains schema)\n * @param componentProps - Props from component call (contains name, discriminator)\n * @returns Properly typed FieldSelector\n *\n * @example\n * ```typescript\n * const selectorProps = mergeFieldSelectorProps(\n * { schema: mySchema },\n * { name: 'fieldName', discriminator: { key: 'mode', value: 'create' } }\n * );\n * ```\n */\nexport function mergeFieldSelectorProps<\n TSchema extends z.ZodType,\n TPath extends ValidPaths<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n factoryProps: { schema: TSchema },\n componentProps: {\n name: TPath;\n discriminator?: Discriminator<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n },\n): FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return { ...factoryProps, ...componentProps } as FieldSelector<\n TSchema,\n TPath,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zod-utils/core",
3
- "version": "2.0.2",
3
+ "version": "3.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",