@zod-utils/react-hook-form 7.1.0 → 7.2.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
@@ -420,6 +420,46 @@ function FieldHint({ schema, name }: { schema: z.ZodType; name: string }) {
420
420
 
421
421
  **Supported check types:** `min_length`, `max_length`, `greater_than`, `less_than`, `string_format`, and more.
422
422
 
423
+ ### `useGetSchemaDefaults({ schema, discriminator? })`
424
+
425
+ Hook to extract default values from a Zod schema. Memoized for performance.
426
+
427
+ ```tsx
428
+ import { useGetSchemaDefaults } from "@zod-utils/react-hook-form";
429
+
430
+ function MyForm({ schema }: { schema: z.ZodType }) {
431
+ const defaults = useGetSchemaDefaults({ schema });
432
+ // { role: 'user', count: 0 } — only fields with explicit .default()
433
+ }
434
+ ```
435
+
436
+ ### `useGetSchemaMeta({ schema, discriminator? }, metaKey)`
437
+
438
+ Hook to extract meta values from all fields in a Zod schema. Memoized for performance.
439
+
440
+ ```tsx
441
+ import { useGetSchemaMeta } from "@zod-utils/react-hook-form";
442
+
443
+ function MyForm({ schema }: { schema: z.ZodType }) {
444
+ const labels = useGetSchemaMeta({ schema }, "label");
445
+ // { name: 'Name', age: 'Age' }
446
+ }
447
+ ```
448
+
449
+ ### `useGetMergedSchemaDefaults({ schema, discriminator? }, metaKey)`
450
+
451
+ Hook to get combined schema defaults and meta values. Meta values take precedence over defaults where both exist. Memoized for performance.
452
+
453
+ ```tsx
454
+ import { useGetMergedSchemaDefaults } from "@zod-utils/react-hook-form";
455
+
456
+ function MyForm({ schema }: { schema: z.ZodType }) {
457
+ const defaults = useGetMergedSchemaDefaults({ schema }, "label");
458
+ // { name: 'Name', age: 'Age', bio: '' }
459
+ // Meta 'Name' wins over default 'hello' for name field
460
+ }
461
+ ```
462
+
423
463
  ---
424
464
 
425
465
  ## Core Utilities (Re-exported)
@@ -430,6 +470,9 @@ All utilities from `@zod-utils/core` are re-exported for convenience:
430
470
  import {
431
471
  // Schema utilities (from @zod-utils/core)
432
472
  getSchemaDefaults,
473
+ extractMeta,
474
+ getSchemaMeta,
475
+ getMergedSchemaDefaults,
433
476
  requiresValidInput,
434
477
  getPrimitiveType,
435
478
  removeDefault,
@@ -448,6 +491,9 @@ import {
448
491
  isRequiredField,
449
492
  useExtractFieldFromSchema,
450
493
  useFieldChecks,
494
+ useGetSchemaDefaults,
495
+ useGetSchemaMeta,
496
+ useGetMergedSchemaDefaults,
451
497
 
452
498
  // Form field utilities
453
499
  flattenFieldSelector,
package/dist/index.d.mts CHANGED
@@ -221,6 +221,67 @@ declare function useExtractFieldFromSchema<TSchema extends z.ZodType, TDiscrimin
221
221
  * ```
222
222
  */
223
223
  declare function useFieldChecks<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true>(params: FieldSelectorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict> | undefined): ZodUnionCheck[];
224
+ /**
225
+ * Hook to extract default values from a Zod schema.
226
+ *
227
+ * Memoized - only recalculates when schema or discriminator changes.
228
+ *
229
+ * @param params - Schema and optional discriminator
230
+ * @returns Partial object with fields that have explicit `.default()` values, or undefined if params is undefined
231
+ *
232
+ * @example
233
+ * ```tsx
234
+ * function MyForm({ schema }: { schema: z.ZodType }) {
235
+ * const defaults = useGetSchemaDefaults({ schema });
236
+ * // Use defaults for form initialization
237
+ * }
238
+ * ```
239
+ *
240
+ * @see {@link getSchemaDefaults} for the non-hook version
241
+ */
242
+ declare function useGetSchemaDefaults<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined): Simplify<Partial<z.input<TSchema>>> | undefined;
243
+ /**
244
+ * Hook to extract meta values from all fields in a Zod schema.
245
+ *
246
+ * Memoized - only recalculates when schema, discriminator, or metaKey changes.
247
+ *
248
+ * @param params - Schema and optional discriminator
249
+ * @param metaKey - The meta property key to extract
250
+ * @returns Record mapping field names to their meta values, or undefined if params is undefined
251
+ *
252
+ * @example
253
+ * ```tsx
254
+ * function MyForm({ schema }: { schema: z.ZodType }) {
255
+ * const labels = useGetSchemaMeta({ schema }, 'label');
256
+ * // { name: 'Name', age: 'Age' }
257
+ * }
258
+ * ```
259
+ *
260
+ * @see {@link getSchemaMeta} for the non-hook version
261
+ */
262
+ declare function useGetSchemaMeta<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined, metaKey: string): Simplify<Partial<z.input<TSchema>>> | undefined;
263
+ /**
264
+ * Hook to get combined schema defaults and meta values.
265
+ *
266
+ * Meta values take precedence over defaults where both exist.
267
+ * Memoized - only recalculates when schema, discriminator, or metaKey changes.
268
+ *
269
+ * @param params - Schema and optional discriminator
270
+ * @param metaKey - The meta property key to extract
271
+ * @returns Deep-merged record of defaults and meta values (meta wins on conflict), or undefined if params is undefined
272
+ *
273
+ * @example
274
+ * ```tsx
275
+ * function MyForm({ schema }: { schema: z.ZodType }) {
276
+ * const defaults = useGetMergedSchemaDefaults({ schema }, 'label');
277
+ * // { name: 'Name', age: 'Age', bio: '' }
278
+ * // Meta 'Name' wins over default 'hello' for name field
279
+ * }
280
+ * ```
281
+ *
282
+ * @see {@link getMergedSchemaDefaults} for the non-hook version
283
+ */
284
+ declare function useGetMergedSchemaDefaults<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined, metaKey: string): Simplify<Partial<z.input<TSchema>>> | undefined;
224
285
 
225
286
  /**
226
287
  * Built-in types that should not be recursively transformed.
@@ -564,4 +625,4 @@ declare function flattenFieldSelector(params?: {
564
625
  };
565
626
  }): unknown[];
566
627
 
567
- export { FormSchemaContext, type FormSchemaContextType, type FormSchemaContextValue, FormSchemaProvider, type PartialFields, type PartialWithAllNullables, type PartialWithNullableObjects, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useIsRequiredField, useZodForm };
628
+ export { FormSchemaContext, type FormSchemaContextType, type FormSchemaContextValue, FormSchemaProvider, type PartialFields, type PartialWithAllNullables, type PartialWithNullableObjects, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useGetMergedSchemaDefaults, useGetSchemaDefaults, useGetSchemaMeta, useIsRequiredField, useZodForm };
package/dist/index.d.ts CHANGED
@@ -221,6 +221,67 @@ declare function useExtractFieldFromSchema<TSchema extends z.ZodType, TDiscrimin
221
221
  * ```
222
222
  */
223
223
  declare function useFieldChecks<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never, TFilterType = unknown, TStrict extends boolean = true>(params: FieldSelectorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue, TFilterType, TStrict> | undefined): ZodUnionCheck[];
224
+ /**
225
+ * Hook to extract default values from a Zod schema.
226
+ *
227
+ * Memoized - only recalculates when schema or discriminator changes.
228
+ *
229
+ * @param params - Schema and optional discriminator
230
+ * @returns Partial object with fields that have explicit `.default()` values, or undefined if params is undefined
231
+ *
232
+ * @example
233
+ * ```tsx
234
+ * function MyForm({ schema }: { schema: z.ZodType }) {
235
+ * const defaults = useGetSchemaDefaults({ schema });
236
+ * // Use defaults for form initialization
237
+ * }
238
+ * ```
239
+ *
240
+ * @see {@link getSchemaDefaults} for the non-hook version
241
+ */
242
+ declare function useGetSchemaDefaults<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined): Simplify<Partial<z.input<TSchema>>> | undefined;
243
+ /**
244
+ * Hook to extract meta values from all fields in a Zod schema.
245
+ *
246
+ * Memoized - only recalculates when schema, discriminator, or metaKey changes.
247
+ *
248
+ * @param params - Schema and optional discriminator
249
+ * @param metaKey - The meta property key to extract
250
+ * @returns Record mapping field names to their meta values, or undefined if params is undefined
251
+ *
252
+ * @example
253
+ * ```tsx
254
+ * function MyForm({ schema }: { schema: z.ZodType }) {
255
+ * const labels = useGetSchemaMeta({ schema }, 'label');
256
+ * // { name: 'Name', age: 'Age' }
257
+ * }
258
+ * ```
259
+ *
260
+ * @see {@link getSchemaMeta} for the non-hook version
261
+ */
262
+ declare function useGetSchemaMeta<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined, metaKey: string): Simplify<Partial<z.input<TSchema>>> | undefined;
263
+ /**
264
+ * Hook to get combined schema defaults and meta values.
265
+ *
266
+ * Meta values take precedence over defaults where both exist.
267
+ * Memoized - only recalculates when schema, discriminator, or metaKey changes.
268
+ *
269
+ * @param params - Schema and optional discriminator
270
+ * @param metaKey - The meta property key to extract
271
+ * @returns Deep-merged record of defaults and meta values (meta wins on conflict), or undefined if params is undefined
272
+ *
273
+ * @example
274
+ * ```tsx
275
+ * function MyForm({ schema }: { schema: z.ZodType }) {
276
+ * const defaults = useGetMergedSchemaDefaults({ schema }, 'label');
277
+ * // { name: 'Name', age: 'Age', bio: '' }
278
+ * // Meta 'Name' wins over default 'hello' for name field
279
+ * }
280
+ * ```
281
+ *
282
+ * @see {@link getMergedSchemaDefaults} for the non-hook version
283
+ */
284
+ declare function useGetMergedSchemaDefaults<TSchema extends z.ZodType, TDiscriminatorKey extends DiscriminatorKey<TSchema> = never, TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey> = never>(params: SchemaAndDiscriminatorProps<TSchema, TDiscriminatorKey, TDiscriminatorValue> | undefined, metaKey: string): Simplify<Partial<z.input<TSchema>>> | undefined;
224
285
 
225
286
  /**
226
287
  * Built-in types that should not be recursively transformed.
@@ -564,4 +625,4 @@ declare function flattenFieldSelector(params?: {
564
625
  };
565
626
  }): unknown[];
566
627
 
567
- export { FormSchemaContext, type FormSchemaContextType, type FormSchemaContextValue, FormSchemaProvider, type PartialFields, type PartialWithAllNullables, type PartialWithNullableObjects, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useIsRequiredField, useZodForm };
628
+ export { FormSchemaContext, type FormSchemaContextType, type FormSchemaContextValue, FormSchemaProvider, type PartialFields, type PartialWithAllNullables, type PartialWithNullableObjects, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useGetMergedSchemaDefaults, useGetSchemaDefaults, useGetSchemaMeta, useIsRequiredField, useZodForm };
package/dist/index.js CHANGED
@@ -89,6 +89,30 @@ function useFieldChecks(params) {
89
89
  return core.getFieldChecks(field);
90
90
  }, [...flattenFieldSelector(params)]);
91
91
  }
92
+ function useGetSchemaDefaults(params) {
93
+ return react.useMemo(() => {
94
+ if (!params) {
95
+ return void 0;
96
+ }
97
+ return core.getSchemaDefaults(params);
98
+ }, [...flattenFieldSelector(params)]);
99
+ }
100
+ function useGetSchemaMeta(params, metaKey) {
101
+ return react.useMemo(() => {
102
+ if (!params) {
103
+ return void 0;
104
+ }
105
+ return core.getSchemaMeta(params, metaKey);
106
+ }, [...flattenFieldSelector(params), metaKey]);
107
+ }
108
+ function useGetMergedSchemaDefaults(params, metaKey) {
109
+ return react.useMemo(() => {
110
+ if (!params) {
111
+ return void 0;
112
+ }
113
+ return core.getMergedSchemaDefaults(params, metaKey);
114
+ }, [...flattenFieldSelector(params), metaKey]);
115
+ }
92
116
 
93
117
  // src/types.ts
94
118
  function partialFields(schema) {
@@ -116,6 +140,9 @@ exports.partialFields = partialFields;
116
140
  exports.useExtractFieldFromSchema = useExtractFieldFromSchema;
117
141
  exports.useFieldChecks = useFieldChecks;
118
142
  exports.useFormSchema = useFormSchema;
143
+ exports.useGetMergedSchemaDefaults = useGetMergedSchemaDefaults;
144
+ exports.useGetSchemaDefaults = useGetSchemaDefaults;
145
+ exports.useGetSchemaMeta = useGetSchemaMeta;
119
146
  exports.useIsRequiredField = useIsRequiredField;
120
147
  exports.useZodForm = useZodForm;
121
148
  Object.keys(core).forEach(function (k) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/context.tsx","../src/types.ts","../src/use-zod-form.ts"],"names":["createContext","useContext","jsx","useMemo","extractFieldFromSchema","requiresValidInput","getFieldChecks","zodResolver","useForm"],"mappings":"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","file":"index.js","sourcesContent":["import type { z } from 'zod';\n\n/**\n * Flattens a FieldSelector into an array of primitive values for use in React dependency arrays.\n *\n * This is useful for `useMemo` and `useCallback` dependencies where you want to avoid\n * re-running when object references change but values stay the same.\n *\n * @param params - The FieldSelector containing schema, name, and optional discriminator\n * @returns An array of primitive values suitable for React dependency arrays\n *\n * @example\n * ```tsx\n * const memoizedValue = useMemo(() => {\n * return extractFieldFromSchema(params);\n * }, flattenFieldSelector(params));\n * ```\n */\nexport function flattenFieldSelector(params?: {\n schema?: z.ZodType;\n name?: string;\n discriminator?: { key: unknown; value: unknown };\n}) {\n return [\n params?.schema,\n params?.name,\n params?.discriminator?.key,\n params?.discriminator?.value,\n ];\n}\n","'use client';\n\nimport {\n type DiscriminatorKey,\n type DiscriminatorValue,\n extractFieldFromSchema,\n type FieldSelectorProps,\n getFieldChecks,\n requiresValidInput,\n type SchemaAndDiscriminatorProps,\n type ZodUnionCheck,\n} from '@zod-utils/core';\nimport {\n type Context,\n createContext,\n type ReactNode,\n useContext,\n useMemo,\n} from 'react';\nimport type { z } from 'zod';\nimport { flattenFieldSelector } from './utils';\n\n/**\n * Type for the FormSchemaContext with full generic support.\n * @internal\n */\nexport type FormSchemaContextType<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = Context<SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null>;\n\n/**\n * Context value type for FormSchemaContext.\n */\nexport type FormSchemaContextValue<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null;\n\n/**\n * React Context for providing Zod schema to form components.\n *\n * Use with {@link FormSchemaProvider} to provide schema context, and\n * {@link useFormSchema} to consume it in child components.\n */\nexport const FormSchemaContext = createContext<{\n schema: z.ZodType;\n discriminator?: {\n key: unknown;\n value: unknown;\n };\n} | null>(null);\n\n/**\n * Hook to access the form schema from context.\n *\n * The optional `_params` argument is used for TypeScript type inference only.\n * Pass your schema to get proper type narrowing of the context value.\n *\n * @param _params - Optional params for type inference (not used at runtime)\n * @returns The schema context value or null if not within a provider\n *\n * @example\n * ```tsx\n * // Without type params (returns generic context)\n * function MyFormField() {\n * const context = useFormSchema();\n * if (!context) return null;\n *\n * const { schema, discriminator } = context;\n * // Use schema for validation or field extraction\n * }\n *\n * // With type params (for type-safe schema access)\n * function TypedFormField() {\n * const context = useFormSchema({ schema: mySchema });\n * // context.schema is now typed as typeof mySchema\n * }\n * ```\n */\nexport function useFormSchema<\n TSchema extends z.ZodType = z.ZodType,\n TDiscriminatorKey extends\n DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n // Parameter used for type inference only, not at runtime\n _params?: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >,\n): FormSchemaContextValue<TSchema, TDiscriminatorKey, TDiscriminatorValue> {\n // Type assertion is necessary because React context is created with a generic type,\n // but we want to return a narrower type based on the generic parameters.\n // The caller is responsible for ensuring type safety via the _params argument.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useContext(FormSchemaContext) as FormSchemaContextValue<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}\n\n/**\n * Provider component that makes Zod schema available to all child components.\n *\n * Use this to wrap your form and provide schema context to nested components\n * like field labels and validation indicators.\n *\n * @example\n * Basic usage with ZodObject\n * ```tsx\n * const schema = z.object({\n * name: z.string(),\n * email: z.string().email().optional()\n * });\n *\n * <FormSchemaProvider schema={schema}>\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n *\n * @example\n * Usage with discriminated union\n * ```tsx\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 * <FormSchemaProvider\n * schema={schema}\n * discriminator={{ key: 'mode', value: 'create' }}\n * >\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n */\nexport function FormSchemaProvider<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n discriminator,\n children,\n}: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> & { children: ReactNode }) {\n return (\n <FormSchemaContext.Provider value={{ schema, discriminator }}>\n {children}\n </FormSchemaContext.Provider>\n );\n}\n\n/**\n * Hook to check if a field requires valid input based on the Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```tsx\n * function MyFieldLabel({ name, schema }: { name: string; schema: z.ZodType }) {\n * const isRequired = useIsRequiredField({ schema, name });\n *\n * return (\n * <label>\n * {name}\n * {isRequired && <span className=\"text-red-500\">*</span>}\n * </label>\n * );\n * }\n * ```\n */\nexport function useIsRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): boolean {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return false;\n }\n return isRequiredField(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Determines if a field requires valid input (will show validation errors on empty/invalid input).\n *\n * Uses `requiresValidInput` from `@zod-utils/core` which checks the underlying field after\n * removing defaults. This tells you if the field will error when user submits empty input.\n *\n * Returns false if the underlying field accepts:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty strings (plain `z.string()` without `.min(1)`)\n * - Empty arrays (plain `z.array()` without `.min(1)`)\n *\n * @param options - Schema, field name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * bio: z.string().optional(),\n * });\n *\n * isRequiredField({ schema, name: 'name' }); // true\n * isRequiredField({ schema, name: 'bio' }); // false\n * ```\n *\n * @example\n * With discriminated union\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string().min(1) }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * isRequiredField({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * }); // true\n * ```\n */\nexport function isRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params: FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n): boolean {\n const field = extractFieldFromSchema(params);\n\n if (!field) {\n return false;\n }\n\n return requiresValidInput(field);\n}\n\n/**\n * React hook to extract a field's Zod schema from a parent schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Supports nested paths and discriminated unions.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns The Zod schema for the field, or undefined if not found\n *\n * @example\n * ```tsx\n * function MyFieldInfo({ name, schema }: { name: string; schema: z.ZodType }) {\n * const fieldSchema = useExtractFieldFromSchema({ schema, name });\n *\n * if (!fieldSchema) return null;\n *\n * // Use fieldSchema for custom validation or field info\n * return <span>{fieldSchema._zod.typeName}</span>;\n * }\n * ```\n *\n * @example\n * With discriminated union\n * ```tsx\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 * const fieldSchema = useExtractFieldFromSchema({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * });\n * // Returns z.string() schema\n * ```\n */\nexport function useExtractFieldFromSchema<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): z.ZodType | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return extractFieldFromSchema(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to get validation checks from a field's Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Combines field extraction and check retrieval in one cached operation.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns Array of validation checks (min, max, pattern, etc.) or empty array\n *\n * @example\n * ```tsx\n * function MyFieldHint({ schema, name }: { schema: z.ZodType; name: string }) {\n * const checks = useFieldChecks({ schema, name });\n *\n * const maxLength = checks.find(c => c.check === 'max_length');\n * if (maxLength) {\n * return <span>Max {maxLength.maximum} characters</span>;\n * }\n * return null;\n * }\n * ```\n */\nexport function useFieldChecks<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): ZodUnionCheck[] {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return [];\n }\n\n const field = extractFieldFromSchema(params);\n if (!field) return [];\n return getFieldChecks(field);\n }, [...flattenFieldSelector(params)]);\n}\n","import type { Simplify } from '@zod-utils/core';\nimport type { z } from 'zod';\n\n/**\n * Built-in types that should not be recursively transformed.\n * These are treated as leaf values (like primitives) and only get `| null` added.\n * @internal\n */\ntype BuiltInObject =\n | Date\n | RegExp\n | Map<unknown, unknown>\n | Set<unknown>\n | WeakMap<object, unknown>\n | WeakSet<object>\n | Promise<unknown>\n | Error;\n\n/**\n * Brand symbol for marking objects that should receive recursive partial transformation.\n * @internal\n */\ndeclare const FormInputBrand: unique symbol;\n\n/**\n * Branded type to mark objects that should have their direct fields made partial.\n * Use with {@link partialFields} helper to mark specific schema objects.\n *\n * @example\n * ```typescript\n * // Objects marked with PartialFields will have their direct fields made optional\n * type MarkedObject = PartialFields<{ name: string; age: number }>;\n * ```\n */\nexport type PartialFields<T> = T & {\n readonly [FormInputBrand]?: typeof FormInputBrand;\n};\n\n/**\n * Helper type to safely get the brand property from a type.\n * Returns `unknown` for non-branded types, `typeof FormInputBrand | undefined` for branded types.\n * @internal\n */\ntype GetPartialFieldsBrand<T> = (T & {\n [FormInputBrand]?: unknown;\n})[typeof FormInputBrand];\n\n/**\n * Helper type to check if a type has the PartialFields brand.\n * Uses bidirectional extends check to distinguish branded from non-branded types.\n * @internal\n */\ntype IsPartialFieldsBranded<T> = GetPartialFieldsBrand<T> extends\n | typeof FormInputBrand\n | undefined\n ? typeof FormInputBrand extends GetPartialFieldsBrand<T>\n ? true\n : false\n : false;\n\n/**\n * Helper type to extract the inner type from a PartialFields branded type.\n * @internal\n */\ntype ExtractPartialFieldsInner<T> = T extends PartialFields<infer U>\n ? U\n : never;\n\n/**\n * Helper function to mark a Zod schema so its direct fields become partial.\n *\n * By default, nested objects in form inputs keep their fields strict (only the object\n * itself becomes nullable). Use this helper to opt-in specific objects to have their\n * direct fields also become optional.\n *\n * **Note:** This only affects the direct fields of the marked object. Nested objects\n * within it will still stay strict unless they are also wrapped with `partialFields()`.\n *\n * **Use cases:**\n * - Form input fields that users fill in manually (should be partial)\n * - Objects from selectors/dropdowns should NOT use this (keep strict)\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n *\n * const schema = z.object({\n * price: z.number(),\n * // User fills in these fields - opt-in to partial\n * detail: partialFields(z.object({\n * hotel: z.string(),\n * nights: z.number(),\n * })),\n * // Selected from dropdown - stays strict\n * agent: z.object({\n * name: z.string(),\n * fee: z.number(),\n * }),\n * });\n *\n * // Result with PartialWithNullableObjects:\n * // detail.hotel → string | undefined (partial - user input)\n * // detail.nights → number | undefined (partial - user input)\n * // agent.name → string (strict! - from selector)\n * // agent.fee → number (strict! - from selector)\n * ```\n */\nexport function partialFields<T extends z.ZodType>(\n schema: T,\n): T & z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema as T &\n z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>>;\n}\n\n/**\n * Transforms object types for form inputs with selective recursion.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives** (string, number, boolean): optional → `type | undefined`\n * - **Arrays**: optional → `type[] | undefined`\n * - **Built-in objects** (Date, RegExp, etc.): optional and nullable → `type | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict** → `{ strictField: type } | null | undefined`\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * This ensures objects from selectors/dropdowns keep strict types for their fields,\n * while form input fields can be partially filled.\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * price: z.number(),\n * detail: partialFields(z.object({ hotel: z.string(), nights: z.number() })),\n * agent: z.object({ name: z.string(), fee: z.number() }),\n * });\n *\n * type FormInput = PartialWithNullableObjects<z.infer<typeof schema>>;\n * // {\n * // price?: number;\n * // detail?: { hotel?: string; nights?: number } | null; // Recursive!\n * // agent?: { name: string; fee: number } | null; // Strict nested!\n * // }\n * ```\n */\nexport type PartialWithNullableObjects<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] // Arrays: just optional (via ?), no null, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithNullableObjects<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K]; // Primitives: just optional (via ?)\n};\n\n/**\n * Transforms all fields to be optional and nullable, with selective recursion.\n *\n * Similar to {@link PartialWithNullableObjects} but also adds `| null` to primitives and arrays.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives**: optional and nullable → `type | null | undefined`\n * - **Arrays**: optional and nullable → `type[] | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict**\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * @example\n * ```typescript\n * type User = { name: string; age: number; profile: { bio: string } };\n * type FormInput = PartialWithAllNullables<User>;\n * // { name?: string | null; age?: number | null; profile?: { bio: string } | null; }\n * // Note: profile.bio stays strict (string, not string | null | undefined)\n * ```\n */\nexport type PartialWithAllNullables<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] | null // Arrays: optional + nullable, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithAllNullables<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K] | null; // Primitives: optional + nullable\n};\n","import { zodResolver } from '@hookform/resolvers/zod';\nimport { type FieldValues, type UseFormProps, useForm } from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TFormInput - The form input type (defaults to PartialWithNullableObjects<TInput>)\n * @template TDefaultValues - The type of default values (inferred from usage for better type safety)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TInput extends FieldValues,\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TInput> = PartialWithNullableObjects<TInput>,\n TDefaultValues extends Partial<TFormInput> | undefined = undefined,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefaultValues;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useForm({\n resolver,\n ...formOptions,\n } as unknown as UseFormProps<TFormInput, unknown, TOutput>);\n};\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/context.tsx","../src/types.ts","../src/use-zod-form.ts"],"names":["createContext","useContext","jsx","useMemo","extractFieldFromSchema","requiresValidInput","getFieldChecks","getSchemaDefaults","getSchemaMeta","getMergedSchemaDefaults","zodResolver","useForm"],"mappings":"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","file":"index.js","sourcesContent":["import type { z } from 'zod';\n\n/**\n * Flattens a FieldSelector into an array of primitive values for use in React dependency arrays.\n *\n * This is useful for `useMemo` and `useCallback` dependencies where you want to avoid\n * re-running when object references change but values stay the same.\n *\n * @param params - The FieldSelector containing schema, name, and optional discriminator\n * @returns An array of primitive values suitable for React dependency arrays\n *\n * @example\n * ```tsx\n * const memoizedValue = useMemo(() => {\n * return extractFieldFromSchema(params);\n * }, flattenFieldSelector(params));\n * ```\n */\nexport function flattenFieldSelector(params?: {\n schema?: z.ZodType;\n name?: string;\n discriminator?: { key: unknown; value: unknown };\n}) {\n return [\n params?.schema,\n params?.name,\n params?.discriminator?.key,\n params?.discriminator?.value,\n ];\n}\n","'use client';\n\nimport {\n type DiscriminatorKey,\n type DiscriminatorValue,\n extractFieldFromSchema,\n type FieldSelectorProps,\n getFieldChecks,\n getMergedSchemaDefaults,\n getSchemaDefaults,\n getSchemaMeta,\n requiresValidInput,\n type SchemaAndDiscriminatorProps,\n type Simplify,\n type ZodUnionCheck,\n} from '@zod-utils/core';\nimport {\n type Context,\n createContext,\n type ReactNode,\n useContext,\n useMemo,\n} from 'react';\nimport type { z } from 'zod';\nimport { flattenFieldSelector } from './utils';\n\n/**\n * Type for the FormSchemaContext with full generic support.\n * @internal\n */\nexport type FormSchemaContextType<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = Context<SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null>;\n\n/**\n * Context value type for FormSchemaContext.\n */\nexport type FormSchemaContextValue<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null;\n\n/**\n * React Context for providing Zod schema to form components.\n *\n * Use with {@link FormSchemaProvider} to provide schema context, and\n * {@link useFormSchema} to consume it in child components.\n */\nexport const FormSchemaContext = createContext<{\n schema: z.ZodType;\n discriminator?: {\n key: unknown;\n value: unknown;\n };\n} | null>(null);\n\n/**\n * Hook to access the form schema from context.\n *\n * The optional `_params` argument is used for TypeScript type inference only.\n * Pass your schema to get proper type narrowing of the context value.\n *\n * @param _params - Optional params for type inference (not used at runtime)\n * @returns The schema context value or null if not within a provider\n *\n * @example\n * ```tsx\n * // Without type params (returns generic context)\n * function MyFormField() {\n * const context = useFormSchema();\n * if (!context) return null;\n *\n * const { schema, discriminator } = context;\n * // Use schema for validation or field extraction\n * }\n *\n * // With type params (for type-safe schema access)\n * function TypedFormField() {\n * const context = useFormSchema({ schema: mySchema });\n * // context.schema is now typed as typeof mySchema\n * }\n * ```\n */\nexport function useFormSchema<\n TSchema extends z.ZodType = z.ZodType,\n TDiscriminatorKey extends\n DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n // Parameter used for type inference only, not at runtime\n _params?: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >,\n): FormSchemaContextValue<TSchema, TDiscriminatorKey, TDiscriminatorValue> {\n // Type assertion is necessary because React context is created with a generic type,\n // but we want to return a narrower type based on the generic parameters.\n // The caller is responsible for ensuring type safety via the _params argument.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useContext(FormSchemaContext) as FormSchemaContextValue<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}\n\n/**\n * Provider component that makes Zod schema available to all child components.\n *\n * Use this to wrap your form and provide schema context to nested components\n * like field labels and validation indicators.\n *\n * @example\n * Basic usage with ZodObject\n * ```tsx\n * const schema = z.object({\n * name: z.string(),\n * email: z.string().email().optional()\n * });\n *\n * <FormSchemaProvider schema={schema}>\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n *\n * @example\n * Usage with discriminated union\n * ```tsx\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 * <FormSchemaProvider\n * schema={schema}\n * discriminator={{ key: 'mode', value: 'create' }}\n * >\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n */\nexport function FormSchemaProvider<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n discriminator,\n children,\n}: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> & { children: ReactNode }) {\n return (\n <FormSchemaContext.Provider value={{ schema, discriminator }}>\n {children}\n </FormSchemaContext.Provider>\n );\n}\n\n/**\n * Hook to check if a field requires valid input based on the Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```tsx\n * function MyFieldLabel({ name, schema }: { name: string; schema: z.ZodType }) {\n * const isRequired = useIsRequiredField({ schema, name });\n *\n * return (\n * <label>\n * {name}\n * {isRequired && <span className=\"text-red-500\">*</span>}\n * </label>\n * );\n * }\n * ```\n */\nexport function useIsRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): boolean {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return false;\n }\n return isRequiredField(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Determines if a field requires valid input (will show validation errors on empty/invalid input).\n *\n * Uses `requiresValidInput` from `@zod-utils/core` which checks the underlying field after\n * removing defaults. This tells you if the field will error when user submits empty input.\n *\n * Returns false if the underlying field accepts:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty strings (plain `z.string()` without `.min(1)`)\n * - Empty arrays (plain `z.array()` without `.min(1)`)\n *\n * @param options - Schema, field name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * bio: z.string().optional(),\n * });\n *\n * isRequiredField({ schema, name: 'name' }); // true\n * isRequiredField({ schema, name: 'bio' }); // false\n * ```\n *\n * @example\n * With discriminated union\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string().min(1) }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * isRequiredField({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * }); // true\n * ```\n */\nexport function isRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params: FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n): boolean {\n const field = extractFieldFromSchema(params);\n\n if (!field) {\n return false;\n }\n\n return requiresValidInput(field);\n}\n\n/**\n * React hook to extract a field's Zod schema from a parent schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Supports nested paths and discriminated unions.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns The Zod schema for the field, or undefined if not found\n *\n * @example\n * ```tsx\n * function MyFieldInfo({ name, schema }: { name: string; schema: z.ZodType }) {\n * const fieldSchema = useExtractFieldFromSchema({ schema, name });\n *\n * if (!fieldSchema) return null;\n *\n * // Use fieldSchema for custom validation or field info\n * return <span>{fieldSchema._zod.typeName}</span>;\n * }\n * ```\n *\n * @example\n * With discriminated union\n * ```tsx\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 * const fieldSchema = useExtractFieldFromSchema({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * });\n * // Returns z.string() schema\n * ```\n */\nexport function useExtractFieldFromSchema<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): z.ZodType | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return extractFieldFromSchema(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to get validation checks from a field's Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Combines field extraction and check retrieval in one cached operation.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns Array of validation checks (min, max, pattern, etc.) or empty array\n *\n * @example\n * ```tsx\n * function MyFieldHint({ schema, name }: { schema: z.ZodType; name: string }) {\n * const checks = useFieldChecks({ schema, name });\n *\n * const maxLength = checks.find(c => c.check === 'max_length');\n * if (maxLength) {\n * return <span>Max {maxLength.maximum} characters</span>;\n * }\n * return null;\n * }\n * ```\n */\nexport function useFieldChecks<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): ZodUnionCheck[] {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return [];\n }\n\n const field = extractFieldFromSchema(params);\n if (!field) return [];\n return getFieldChecks(field);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to extract default values from a Zod schema.\n *\n * Memoized - only recalculates when schema or discriminator changes.\n *\n * @param params - Schema and optional discriminator\n * @returns Partial object with fields that have explicit `.default()` values, or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const defaults = useGetSchemaDefaults({ schema });\n * // Use defaults for form initialization\n * }\n * ```\n *\n * @see {@link getSchemaDefaults} for the non-hook version\n */\nexport function useGetSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getSchemaDefaults(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to extract meta values from all fields in a Zod schema.\n *\n * Memoized - only recalculates when schema, discriminator, or metaKey changes.\n *\n * @param params - Schema and optional discriminator\n * @param metaKey - The meta property key to extract\n * @returns Record mapping field names to their meta values, or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const labels = useGetSchemaMeta({ schema }, 'label');\n * // { name: 'Name', age: 'Age' }\n * }\n * ```\n *\n * @see {@link getSchemaMeta} for the non-hook version\n */\nexport function useGetSchemaMeta<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n metaKey: string,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getSchemaMeta(params, metaKey);\n }, [...flattenFieldSelector(params), metaKey]);\n}\n\n/**\n * Hook to get combined schema defaults and meta values.\n *\n * Meta values take precedence over defaults where both exist.\n * Memoized - only recalculates when schema, discriminator, or metaKey changes.\n *\n * @param params - Schema and optional discriminator\n * @param metaKey - The meta property key to extract\n * @returns Deep-merged record of defaults and meta values (meta wins on conflict), or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const defaults = useGetMergedSchemaDefaults({ schema }, 'label');\n * // { name: 'Name', age: 'Age', bio: '' }\n * // Meta 'Name' wins over default 'hello' for name field\n * }\n * ```\n *\n * @see {@link getMergedSchemaDefaults} for the non-hook version\n */\nexport function useGetMergedSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n metaKey: string,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getMergedSchemaDefaults(params, metaKey);\n }, [...flattenFieldSelector(params), metaKey]);\n}\n","import type { Simplify } from '@zod-utils/core';\nimport type { z } from 'zod';\n\n/**\n * Built-in types that should not be recursively transformed.\n * These are treated as leaf values (like primitives) and only get `| null` added.\n * @internal\n */\ntype BuiltInObject =\n | Date\n | RegExp\n | Map<unknown, unknown>\n | Set<unknown>\n | WeakMap<object, unknown>\n | WeakSet<object>\n | Promise<unknown>\n | Error;\n\n/**\n * Brand symbol for marking objects that should receive recursive partial transformation.\n * @internal\n */\ndeclare const FormInputBrand: unique symbol;\n\n/**\n * Branded type to mark objects that should have their direct fields made partial.\n * Use with {@link partialFields} helper to mark specific schema objects.\n *\n * @example\n * ```typescript\n * // Objects marked with PartialFields will have their direct fields made optional\n * type MarkedObject = PartialFields<{ name: string; age: number }>;\n * ```\n */\nexport type PartialFields<T> = T & {\n readonly [FormInputBrand]?: typeof FormInputBrand;\n};\n\n/**\n * Helper type to safely get the brand property from a type.\n * Returns `unknown` for non-branded types, `typeof FormInputBrand | undefined` for branded types.\n * @internal\n */\ntype GetPartialFieldsBrand<T> = (T & {\n [FormInputBrand]?: unknown;\n})[typeof FormInputBrand];\n\n/**\n * Helper type to check if a type has the PartialFields brand.\n * Uses bidirectional extends check to distinguish branded from non-branded types.\n * @internal\n */\ntype IsPartialFieldsBranded<T> = GetPartialFieldsBrand<T> extends\n | typeof FormInputBrand\n | undefined\n ? typeof FormInputBrand extends GetPartialFieldsBrand<T>\n ? true\n : false\n : false;\n\n/**\n * Helper type to extract the inner type from a PartialFields branded type.\n * @internal\n */\ntype ExtractPartialFieldsInner<T> = T extends PartialFields<infer U>\n ? U\n : never;\n\n/**\n * Helper function to mark a Zod schema so its direct fields become partial.\n *\n * By default, nested objects in form inputs keep their fields strict (only the object\n * itself becomes nullable). Use this helper to opt-in specific objects to have their\n * direct fields also become optional.\n *\n * **Note:** This only affects the direct fields of the marked object. Nested objects\n * within it will still stay strict unless they are also wrapped with `partialFields()`.\n *\n * **Use cases:**\n * - Form input fields that users fill in manually (should be partial)\n * - Objects from selectors/dropdowns should NOT use this (keep strict)\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n *\n * const schema = z.object({\n * price: z.number(),\n * // User fills in these fields - opt-in to partial\n * detail: partialFields(z.object({\n * hotel: z.string(),\n * nights: z.number(),\n * })),\n * // Selected from dropdown - stays strict\n * agent: z.object({\n * name: z.string(),\n * fee: z.number(),\n * }),\n * });\n *\n * // Result with PartialWithNullableObjects:\n * // detail.hotel → string | undefined (partial - user input)\n * // detail.nights → number | undefined (partial - user input)\n * // agent.name → string (strict! - from selector)\n * // agent.fee → number (strict! - from selector)\n * ```\n */\nexport function partialFields<T extends z.ZodType>(\n schema: T,\n): T & z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema as T &\n z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>>;\n}\n\n/**\n * Transforms object types for form inputs with selective recursion.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives** (string, number, boolean): optional → `type | undefined`\n * - **Arrays**: optional → `type[] | undefined`\n * - **Built-in objects** (Date, RegExp, etc.): optional and nullable → `type | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict** → `{ strictField: type } | null | undefined`\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * This ensures objects from selectors/dropdowns keep strict types for their fields,\n * while form input fields can be partially filled.\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * price: z.number(),\n * detail: partialFields(z.object({ hotel: z.string(), nights: z.number() })),\n * agent: z.object({ name: z.string(), fee: z.number() }),\n * });\n *\n * type FormInput = PartialWithNullableObjects<z.infer<typeof schema>>;\n * // {\n * // price?: number;\n * // detail?: { hotel?: string; nights?: number } | null; // Recursive!\n * // agent?: { name: string; fee: number } | null; // Strict nested!\n * // }\n * ```\n */\nexport type PartialWithNullableObjects<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] // Arrays: just optional (via ?), no null, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithNullableObjects<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K]; // Primitives: just optional (via ?)\n};\n\n/**\n * Transforms all fields to be optional and nullable, with selective recursion.\n *\n * Similar to {@link PartialWithNullableObjects} but also adds `| null` to primitives and arrays.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives**: optional and nullable → `type | null | undefined`\n * - **Arrays**: optional and nullable → `type[] | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict**\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * @example\n * ```typescript\n * type User = { name: string; age: number; profile: { bio: string } };\n * type FormInput = PartialWithAllNullables<User>;\n * // { name?: string | null; age?: number | null; profile?: { bio: string } | null; }\n * // Note: profile.bio stays strict (string, not string | null | undefined)\n * ```\n */\nexport type PartialWithAllNullables<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] | null // Arrays: optional + nullable, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithAllNullables<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K] | null; // Primitives: optional + nullable\n};\n","import { zodResolver } from '@hookform/resolvers/zod';\nimport { type FieldValues, type UseFormProps, useForm } from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TFormInput - The form input type (defaults to PartialWithNullableObjects<TInput>)\n * @template TDefaultValues - The type of default values (inferred from usage for better type safety)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TInput extends FieldValues,\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TInput> = PartialWithNullableObjects<TInput>,\n TDefaultValues extends Partial<TFormInput> | undefined = undefined,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefaultValues;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useForm({\n resolver,\n ...formOptions,\n } as unknown as UseFormProps<TFormInput, unknown, TOutput>);\n};\n"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { extractFieldFromSchema, requiresValidInput, getFieldChecks } from '@zod-utils/core';
1
+ import { extractFieldFromSchema, requiresValidInput, getFieldChecks, getSchemaDefaults, getSchemaMeta, getMergedSchemaDefaults } from '@zod-utils/core';
2
2
  export * from '@zod-utils/core';
3
3
  import { createContext, useContext, useMemo } from 'react';
4
4
  import { jsx } from 'react/jsx-runtime';
@@ -88,6 +88,30 @@ function useFieldChecks(params) {
88
88
  return getFieldChecks(field);
89
89
  }, [...flattenFieldSelector(params)]);
90
90
  }
91
+ function useGetSchemaDefaults(params) {
92
+ return useMemo(() => {
93
+ if (!params) {
94
+ return void 0;
95
+ }
96
+ return getSchemaDefaults(params);
97
+ }, [...flattenFieldSelector(params)]);
98
+ }
99
+ function useGetSchemaMeta(params, metaKey) {
100
+ return useMemo(() => {
101
+ if (!params) {
102
+ return void 0;
103
+ }
104
+ return getSchemaMeta(params, metaKey);
105
+ }, [...flattenFieldSelector(params), metaKey]);
106
+ }
107
+ function useGetMergedSchemaDefaults(params, metaKey) {
108
+ return useMemo(() => {
109
+ if (!params) {
110
+ return void 0;
111
+ }
112
+ return getMergedSchemaDefaults(params, metaKey);
113
+ }, [...flattenFieldSelector(params), metaKey]);
114
+ }
91
115
 
92
116
  // src/types.ts
93
117
  function partialFields(schema) {
@@ -107,6 +131,6 @@ var useZodForm = (_a) => {
107
131
  }, formOptions));
108
132
  };
109
133
 
110
- export { FormSchemaContext, FormSchemaProvider, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useIsRequiredField, useZodForm };
134
+ export { FormSchemaContext, FormSchemaProvider, flattenFieldSelector, isRequiredField, partialFields, useExtractFieldFromSchema, useFieldChecks, useFormSchema, useGetMergedSchemaDefaults, useGetSchemaDefaults, useGetSchemaMeta, useIsRequiredField, useZodForm };
111
135
  //# sourceMappingURL=index.mjs.map
112
136
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/context.tsx","../src/types.ts","../src/use-zod-form.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { z } from 'zod';\n\n/**\n * Flattens a FieldSelector into an array of primitive values for use in React dependency arrays.\n *\n * This is useful for `useMemo` and `useCallback` dependencies where you want to avoid\n * re-running when object references change but values stay the same.\n *\n * @param params - The FieldSelector containing schema, name, and optional discriminator\n * @returns An array of primitive values suitable for React dependency arrays\n *\n * @example\n * ```tsx\n * const memoizedValue = useMemo(() => {\n * return extractFieldFromSchema(params);\n * }, flattenFieldSelector(params));\n * ```\n */\nexport function flattenFieldSelector(params?: {\n schema?: z.ZodType;\n name?: string;\n discriminator?: { key: unknown; value: unknown };\n}) {\n return [\n params?.schema,\n params?.name,\n params?.discriminator?.key,\n params?.discriminator?.value,\n ];\n}\n","'use client';\n\nimport {\n type DiscriminatorKey,\n type DiscriminatorValue,\n extractFieldFromSchema,\n type FieldSelectorProps,\n getFieldChecks,\n requiresValidInput,\n type SchemaAndDiscriminatorProps,\n type ZodUnionCheck,\n} from '@zod-utils/core';\nimport {\n type Context,\n createContext,\n type ReactNode,\n useContext,\n useMemo,\n} from 'react';\nimport type { z } from 'zod';\nimport { flattenFieldSelector } from './utils';\n\n/**\n * Type for the FormSchemaContext with full generic support.\n * @internal\n */\nexport type FormSchemaContextType<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = Context<SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null>;\n\n/**\n * Context value type for FormSchemaContext.\n */\nexport type FormSchemaContextValue<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null;\n\n/**\n * React Context for providing Zod schema to form components.\n *\n * Use with {@link FormSchemaProvider} to provide schema context, and\n * {@link useFormSchema} to consume it in child components.\n */\nexport const FormSchemaContext = createContext<{\n schema: z.ZodType;\n discriminator?: {\n key: unknown;\n value: unknown;\n };\n} | null>(null);\n\n/**\n * Hook to access the form schema from context.\n *\n * The optional `_params` argument is used for TypeScript type inference only.\n * Pass your schema to get proper type narrowing of the context value.\n *\n * @param _params - Optional params for type inference (not used at runtime)\n * @returns The schema context value or null if not within a provider\n *\n * @example\n * ```tsx\n * // Without type params (returns generic context)\n * function MyFormField() {\n * const context = useFormSchema();\n * if (!context) return null;\n *\n * const { schema, discriminator } = context;\n * // Use schema for validation or field extraction\n * }\n *\n * // With type params (for type-safe schema access)\n * function TypedFormField() {\n * const context = useFormSchema({ schema: mySchema });\n * // context.schema is now typed as typeof mySchema\n * }\n * ```\n */\nexport function useFormSchema<\n TSchema extends z.ZodType = z.ZodType,\n TDiscriminatorKey extends\n DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n // Parameter used for type inference only, not at runtime\n _params?: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >,\n): FormSchemaContextValue<TSchema, TDiscriminatorKey, TDiscriminatorValue> {\n // Type assertion is necessary because React context is created with a generic type,\n // but we want to return a narrower type based on the generic parameters.\n // The caller is responsible for ensuring type safety via the _params argument.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useContext(FormSchemaContext) as FormSchemaContextValue<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}\n\n/**\n * Provider component that makes Zod schema available to all child components.\n *\n * Use this to wrap your form and provide schema context to nested components\n * like field labels and validation indicators.\n *\n * @example\n * Basic usage with ZodObject\n * ```tsx\n * const schema = z.object({\n * name: z.string(),\n * email: z.string().email().optional()\n * });\n *\n * <FormSchemaProvider schema={schema}>\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n *\n * @example\n * Usage with discriminated union\n * ```tsx\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 * <FormSchemaProvider\n * schema={schema}\n * discriminator={{ key: 'mode', value: 'create' }}\n * >\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n */\nexport function FormSchemaProvider<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n discriminator,\n children,\n}: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> & { children: ReactNode }) {\n return (\n <FormSchemaContext.Provider value={{ schema, discriminator }}>\n {children}\n </FormSchemaContext.Provider>\n );\n}\n\n/**\n * Hook to check if a field requires valid input based on the Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```tsx\n * function MyFieldLabel({ name, schema }: { name: string; schema: z.ZodType }) {\n * const isRequired = useIsRequiredField({ schema, name });\n *\n * return (\n * <label>\n * {name}\n * {isRequired && <span className=\"text-red-500\">*</span>}\n * </label>\n * );\n * }\n * ```\n */\nexport function useIsRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): boolean {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return false;\n }\n return isRequiredField(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Determines if a field requires valid input (will show validation errors on empty/invalid input).\n *\n * Uses `requiresValidInput` from `@zod-utils/core` which checks the underlying field after\n * removing defaults. This tells you if the field will error when user submits empty input.\n *\n * Returns false if the underlying field accepts:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty strings (plain `z.string()` without `.min(1)`)\n * - Empty arrays (plain `z.array()` without `.min(1)`)\n *\n * @param options - Schema, field name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * bio: z.string().optional(),\n * });\n *\n * isRequiredField({ schema, name: 'name' }); // true\n * isRequiredField({ schema, name: 'bio' }); // false\n * ```\n *\n * @example\n * With discriminated union\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string().min(1) }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * isRequiredField({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * }); // true\n * ```\n */\nexport function isRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params: FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n): boolean {\n const field = extractFieldFromSchema(params);\n\n if (!field) {\n return false;\n }\n\n return requiresValidInput(field);\n}\n\n/**\n * React hook to extract a field's Zod schema from a parent schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Supports nested paths and discriminated unions.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns The Zod schema for the field, or undefined if not found\n *\n * @example\n * ```tsx\n * function MyFieldInfo({ name, schema }: { name: string; schema: z.ZodType }) {\n * const fieldSchema = useExtractFieldFromSchema({ schema, name });\n *\n * if (!fieldSchema) return null;\n *\n * // Use fieldSchema for custom validation or field info\n * return <span>{fieldSchema._zod.typeName}</span>;\n * }\n * ```\n *\n * @example\n * With discriminated union\n * ```tsx\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 * const fieldSchema = useExtractFieldFromSchema({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * });\n * // Returns z.string() schema\n * ```\n */\nexport function useExtractFieldFromSchema<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): z.ZodType | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return extractFieldFromSchema(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to get validation checks from a field's Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Combines field extraction and check retrieval in one cached operation.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns Array of validation checks (min, max, pattern, etc.) or empty array\n *\n * @example\n * ```tsx\n * function MyFieldHint({ schema, name }: { schema: z.ZodType; name: string }) {\n * const checks = useFieldChecks({ schema, name });\n *\n * const maxLength = checks.find(c => c.check === 'max_length');\n * if (maxLength) {\n * return <span>Max {maxLength.maximum} characters</span>;\n * }\n * return null;\n * }\n * ```\n */\nexport function useFieldChecks<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): ZodUnionCheck[] {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return [];\n }\n\n const field = extractFieldFromSchema(params);\n if (!field) return [];\n return getFieldChecks(field);\n }, [...flattenFieldSelector(params)]);\n}\n","import type { Simplify } from '@zod-utils/core';\nimport type { z } from 'zod';\n\n/**\n * Built-in types that should not be recursively transformed.\n * These are treated as leaf values (like primitives) and only get `| null` added.\n * @internal\n */\ntype BuiltInObject =\n | Date\n | RegExp\n | Map<unknown, unknown>\n | Set<unknown>\n | WeakMap<object, unknown>\n | WeakSet<object>\n | Promise<unknown>\n | Error;\n\n/**\n * Brand symbol for marking objects that should receive recursive partial transformation.\n * @internal\n */\ndeclare const FormInputBrand: unique symbol;\n\n/**\n * Branded type to mark objects that should have their direct fields made partial.\n * Use with {@link partialFields} helper to mark specific schema objects.\n *\n * @example\n * ```typescript\n * // Objects marked with PartialFields will have their direct fields made optional\n * type MarkedObject = PartialFields<{ name: string; age: number }>;\n * ```\n */\nexport type PartialFields<T> = T & {\n readonly [FormInputBrand]?: typeof FormInputBrand;\n};\n\n/**\n * Helper type to safely get the brand property from a type.\n * Returns `unknown` for non-branded types, `typeof FormInputBrand | undefined` for branded types.\n * @internal\n */\ntype GetPartialFieldsBrand<T> = (T & {\n [FormInputBrand]?: unknown;\n})[typeof FormInputBrand];\n\n/**\n * Helper type to check if a type has the PartialFields brand.\n * Uses bidirectional extends check to distinguish branded from non-branded types.\n * @internal\n */\ntype IsPartialFieldsBranded<T> = GetPartialFieldsBrand<T> extends\n | typeof FormInputBrand\n | undefined\n ? typeof FormInputBrand extends GetPartialFieldsBrand<T>\n ? true\n : false\n : false;\n\n/**\n * Helper type to extract the inner type from a PartialFields branded type.\n * @internal\n */\ntype ExtractPartialFieldsInner<T> = T extends PartialFields<infer U>\n ? U\n : never;\n\n/**\n * Helper function to mark a Zod schema so its direct fields become partial.\n *\n * By default, nested objects in form inputs keep their fields strict (only the object\n * itself becomes nullable). Use this helper to opt-in specific objects to have their\n * direct fields also become optional.\n *\n * **Note:** This only affects the direct fields of the marked object. Nested objects\n * within it will still stay strict unless they are also wrapped with `partialFields()`.\n *\n * **Use cases:**\n * - Form input fields that users fill in manually (should be partial)\n * - Objects from selectors/dropdowns should NOT use this (keep strict)\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n *\n * const schema = z.object({\n * price: z.number(),\n * // User fills in these fields - opt-in to partial\n * detail: partialFields(z.object({\n * hotel: z.string(),\n * nights: z.number(),\n * })),\n * // Selected from dropdown - stays strict\n * agent: z.object({\n * name: z.string(),\n * fee: z.number(),\n * }),\n * });\n *\n * // Result with PartialWithNullableObjects:\n * // detail.hotel → string | undefined (partial - user input)\n * // detail.nights → number | undefined (partial - user input)\n * // agent.name → string (strict! - from selector)\n * // agent.fee → number (strict! - from selector)\n * ```\n */\nexport function partialFields<T extends z.ZodType>(\n schema: T,\n): T & z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema as T &\n z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>>;\n}\n\n/**\n * Transforms object types for form inputs with selective recursion.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives** (string, number, boolean): optional → `type | undefined`\n * - **Arrays**: optional → `type[] | undefined`\n * - **Built-in objects** (Date, RegExp, etc.): optional and nullable → `type | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict** → `{ strictField: type } | null | undefined`\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * This ensures objects from selectors/dropdowns keep strict types for their fields,\n * while form input fields can be partially filled.\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * price: z.number(),\n * detail: partialFields(z.object({ hotel: z.string(), nights: z.number() })),\n * agent: z.object({ name: z.string(), fee: z.number() }),\n * });\n *\n * type FormInput = PartialWithNullableObjects<z.infer<typeof schema>>;\n * // {\n * // price?: number;\n * // detail?: { hotel?: string; nights?: number } | null; // Recursive!\n * // agent?: { name: string; fee: number } | null; // Strict nested!\n * // }\n * ```\n */\nexport type PartialWithNullableObjects<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] // Arrays: just optional (via ?), no null, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithNullableObjects<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K]; // Primitives: just optional (via ?)\n};\n\n/**\n * Transforms all fields to be optional and nullable, with selective recursion.\n *\n * Similar to {@link PartialWithNullableObjects} but also adds `| null` to primitives and arrays.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives**: optional and nullable → `type | null | undefined`\n * - **Arrays**: optional and nullable → `type[] | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict**\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * @example\n * ```typescript\n * type User = { name: string; age: number; profile: { bio: string } };\n * type FormInput = PartialWithAllNullables<User>;\n * // { name?: string | null; age?: number | null; profile?: { bio: string } | null; }\n * // Note: profile.bio stays strict (string, not string | null | undefined)\n * ```\n */\nexport type PartialWithAllNullables<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] | null // Arrays: optional + nullable, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithAllNullables<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K] | null; // Primitives: optional + nullable\n};\n","import { zodResolver } from '@hookform/resolvers/zod';\nimport { type FieldValues, type UseFormProps, useForm } from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TFormInput - The form input type (defaults to PartialWithNullableObjects<TInput>)\n * @template TDefaultValues - The type of default values (inferred from usage for better type safety)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TInput extends FieldValues,\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TInput> = PartialWithNullableObjects<TInput>,\n TDefaultValues extends Partial<TFormInput> | undefined = undefined,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefaultValues;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useForm({\n resolver,\n ...formOptions,\n } as unknown as UseFormProps<TFormInput, unknown, TOutput>);\n};\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/context.tsx","../src/types.ts","../src/use-zod-form.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { z } from 'zod';\n\n/**\n * Flattens a FieldSelector into an array of primitive values for use in React dependency arrays.\n *\n * This is useful for `useMemo` and `useCallback` dependencies where you want to avoid\n * re-running when object references change but values stay the same.\n *\n * @param params - The FieldSelector containing schema, name, and optional discriminator\n * @returns An array of primitive values suitable for React dependency arrays\n *\n * @example\n * ```tsx\n * const memoizedValue = useMemo(() => {\n * return extractFieldFromSchema(params);\n * }, flattenFieldSelector(params));\n * ```\n */\nexport function flattenFieldSelector(params?: {\n schema?: z.ZodType;\n name?: string;\n discriminator?: { key: unknown; value: unknown };\n}) {\n return [\n params?.schema,\n params?.name,\n params?.discriminator?.key,\n params?.discriminator?.value,\n ];\n}\n","'use client';\n\nimport {\n type DiscriminatorKey,\n type DiscriminatorValue,\n extractFieldFromSchema,\n type FieldSelectorProps,\n getFieldChecks,\n getMergedSchemaDefaults,\n getSchemaDefaults,\n getSchemaMeta,\n requiresValidInput,\n type SchemaAndDiscriminatorProps,\n type Simplify,\n type ZodUnionCheck,\n} from '@zod-utils/core';\nimport {\n type Context,\n createContext,\n type ReactNode,\n useContext,\n useMemo,\n} from 'react';\nimport type { z } from 'zod';\nimport { flattenFieldSelector } from './utils';\n\n/**\n * Type for the FormSchemaContext with full generic support.\n * @internal\n */\nexport type FormSchemaContextType<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = Context<SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null>;\n\n/**\n * Context value type for FormSchemaContext.\n */\nexport type FormSchemaContextValue<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n> = SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> | null;\n\n/**\n * React Context for providing Zod schema to form components.\n *\n * Use with {@link FormSchemaProvider} to provide schema context, and\n * {@link useFormSchema} to consume it in child components.\n */\nexport const FormSchemaContext = createContext<{\n schema: z.ZodType;\n discriminator?: {\n key: unknown;\n value: unknown;\n };\n} | null>(null);\n\n/**\n * Hook to access the form schema from context.\n *\n * The optional `_params` argument is used for TypeScript type inference only.\n * Pass your schema to get proper type narrowing of the context value.\n *\n * @param _params - Optional params for type inference (not used at runtime)\n * @returns The schema context value or null if not within a provider\n *\n * @example\n * ```tsx\n * // Without type params (returns generic context)\n * function MyFormField() {\n * const context = useFormSchema();\n * if (!context) return null;\n *\n * const { schema, discriminator } = context;\n * // Use schema for validation or field extraction\n * }\n *\n * // With type params (for type-safe schema access)\n * function TypedFormField() {\n * const context = useFormSchema({ schema: mySchema });\n * // context.schema is now typed as typeof mySchema\n * }\n * ```\n */\nexport function useFormSchema<\n TSchema extends z.ZodType = z.ZodType,\n TDiscriminatorKey extends\n DiscriminatorKey<TSchema> = DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>(\n // Parameter used for type inference only, not at runtime\n _params?: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >,\n): FormSchemaContextValue<TSchema, TDiscriminatorKey, TDiscriminatorValue> {\n // Type assertion is necessary because React context is created with a generic type,\n // but we want to return a narrower type based on the generic parameters.\n // The caller is responsible for ensuring type safety via the _params argument.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useContext(FormSchemaContext) as FormSchemaContextValue<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >;\n}\n\n/**\n * Provider component that makes Zod schema available to all child components.\n *\n * Use this to wrap your form and provide schema context to nested components\n * like field labels and validation indicators.\n *\n * @example\n * Basic usage with ZodObject\n * ```tsx\n * const schema = z.object({\n * name: z.string(),\n * email: z.string().email().optional()\n * });\n *\n * <FormSchemaProvider schema={schema}>\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n *\n * @example\n * Usage with discriminated union\n * ```tsx\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 * <FormSchemaProvider\n * schema={schema}\n * discriminator={{ key: 'mode', value: 'create' }}\n * >\n * <YourFormComponents />\n * </FormSchemaProvider>\n * ```\n */\nexport function FormSchemaProvider<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema>,\n TDiscriminatorValue extends DiscriminatorValue<TSchema, TDiscriminatorKey>,\n>({\n schema,\n discriminator,\n children,\n}: SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n> & { children: ReactNode }) {\n return (\n <FormSchemaContext.Provider value={{ schema, discriminator }}>\n {children}\n </FormSchemaContext.Provider>\n );\n}\n\n/**\n * Hook to check if a field requires valid input based on the Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```tsx\n * function MyFieldLabel({ name, schema }: { name: string; schema: z.ZodType }) {\n * const isRequired = useIsRequiredField({ schema, name });\n *\n * return (\n * <label>\n * {name}\n * {isRequired && <span className=\"text-red-500\">*</span>}\n * </label>\n * );\n * }\n * ```\n */\nexport function useIsRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): boolean {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return false;\n }\n return isRequiredField(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Determines if a field requires valid input (will show validation errors on empty/invalid input).\n *\n * Uses `requiresValidInput` from `@zod-utils/core` which checks the underlying field after\n * removing defaults. This tells you if the field will error when user submits empty input.\n *\n * Returns false if the underlying field accepts:\n * - `undefined` (via `.optional()`)\n * - `null` (via `.nullable()`)\n * - Empty strings (plain `z.string()` without `.min(1)`)\n * - Empty arrays (plain `z.array()` without `.min(1)`)\n *\n * @param options - Schema, field name, and optional discriminator\n * @returns true if the field requires valid input, false otherwise\n *\n * @example\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * bio: z.string().optional(),\n * });\n *\n * isRequiredField({ schema, name: 'name' }); // true\n * isRequiredField({ schema, name: 'bio' }); // false\n * ```\n *\n * @example\n * With discriminated union\n * ```typescript\n * const schema = z.discriminatedUnion('mode', [\n * z.object({ mode: z.literal('create'), name: z.string().min(1) }),\n * z.object({ mode: z.literal('edit'), id: z.number() }),\n * ]);\n *\n * isRequiredField({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * }); // true\n * ```\n */\nexport function isRequiredField<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params: FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >,\n): boolean {\n const field = extractFieldFromSchema(params);\n\n if (!field) {\n return false;\n }\n\n return requiresValidInput(field);\n}\n\n/**\n * React hook to extract a field's Zod schema from a parent schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Supports nested paths and discriminated unions.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns The Zod schema for the field, or undefined if not found\n *\n * @example\n * ```tsx\n * function MyFieldInfo({ name, schema }: { name: string; schema: z.ZodType }) {\n * const fieldSchema = useExtractFieldFromSchema({ schema, name });\n *\n * if (!fieldSchema) return null;\n *\n * // Use fieldSchema for custom validation or field info\n * return <span>{fieldSchema._zod.typeName}</span>;\n * }\n * ```\n *\n * @example\n * With discriminated union\n * ```tsx\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 * const fieldSchema = useExtractFieldFromSchema({\n * schema,\n * name: 'name',\n * discriminator: { key: 'mode', value: 'create' },\n * });\n * // Returns z.string() schema\n * ```\n */\nexport function useExtractFieldFromSchema<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): z.ZodType | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return extractFieldFromSchema(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to get validation checks from a field's Zod schema.\n *\n * Memoized - only recalculates when schema, name, or discriminator changes.\n * Combines field extraction and check retrieval in one cached operation.\n *\n * @param params - Schema, name, and optional discriminator\n * @returns Array of validation checks (min, max, pattern, etc.) or empty array\n *\n * @example\n * ```tsx\n * function MyFieldHint({ schema, name }: { schema: z.ZodType; name: string }) {\n * const checks = useFieldChecks({ schema, name });\n *\n * const maxLength = checks.find(c => c.check === 'max_length');\n * if (maxLength) {\n * return <span>Max {maxLength.maximum} characters</span>;\n * }\n * return null;\n * }\n * ```\n */\nexport function useFieldChecks<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n TFilterType = unknown,\n TStrict extends boolean = true,\n>(\n params:\n | FieldSelectorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue,\n TFilterType,\n TStrict\n >\n | undefined,\n): ZodUnionCheck[] {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return [];\n }\n\n const field = extractFieldFromSchema(params);\n if (!field) return [];\n return getFieldChecks(field);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to extract default values from a Zod schema.\n *\n * Memoized - only recalculates when schema or discriminator changes.\n *\n * @param params - Schema and optional discriminator\n * @returns Partial object with fields that have explicit `.default()` values, or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const defaults = useGetSchemaDefaults({ schema });\n * // Use defaults for form initialization\n * }\n * ```\n *\n * @see {@link getSchemaDefaults} for the non-hook version\n */\nexport function useGetSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getSchemaDefaults(params);\n }, [...flattenFieldSelector(params)]);\n}\n\n/**\n * Hook to extract meta values from all fields in a Zod schema.\n *\n * Memoized - only recalculates when schema, discriminator, or metaKey changes.\n *\n * @param params - Schema and optional discriminator\n * @param metaKey - The meta property key to extract\n * @returns Record mapping field names to their meta values, or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const labels = useGetSchemaMeta({ schema }, 'label');\n * // { name: 'Name', age: 'Age' }\n * }\n * ```\n *\n * @see {@link getSchemaMeta} for the non-hook version\n */\nexport function useGetSchemaMeta<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n metaKey: string,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getSchemaMeta(params, metaKey);\n }, [...flattenFieldSelector(params), metaKey]);\n}\n\n/**\n * Hook to get combined schema defaults and meta values.\n *\n * Meta values take precedence over defaults where both exist.\n * Memoized - only recalculates when schema, discriminator, or metaKey changes.\n *\n * @param params - Schema and optional discriminator\n * @param metaKey - The meta property key to extract\n * @returns Deep-merged record of defaults and meta values (meta wins on conflict), or undefined if params is undefined\n *\n * @example\n * ```tsx\n * function MyForm({ schema }: { schema: z.ZodType }) {\n * const defaults = useGetMergedSchemaDefaults({ schema }, 'label');\n * // { name: 'Name', age: 'Age', bio: '' }\n * // Meta 'Name' wins over default 'hello' for name field\n * }\n * ```\n *\n * @see {@link getMergedSchemaDefaults} for the non-hook version\n */\nexport function useGetMergedSchemaDefaults<\n TSchema extends z.ZodType,\n TDiscriminatorKey extends DiscriminatorKey<TSchema> = never,\n TDiscriminatorValue extends DiscriminatorValue<\n TSchema,\n TDiscriminatorKey\n > = never,\n>(\n params:\n | SchemaAndDiscriminatorProps<\n TSchema,\n TDiscriminatorKey,\n TDiscriminatorValue\n >\n | undefined,\n metaKey: string,\n): Simplify<Partial<z.input<TSchema>>> | undefined {\n // biome-ignore lint/correctness/useExhaustiveDependencies: using flattenFieldSelector for stable deps\n return useMemo(() => {\n if (!params) {\n return undefined;\n }\n return getMergedSchemaDefaults(params, metaKey);\n }, [...flattenFieldSelector(params), metaKey]);\n}\n","import type { Simplify } from '@zod-utils/core';\nimport type { z } from 'zod';\n\n/**\n * Built-in types that should not be recursively transformed.\n * These are treated as leaf values (like primitives) and only get `| null` added.\n * @internal\n */\ntype BuiltInObject =\n | Date\n | RegExp\n | Map<unknown, unknown>\n | Set<unknown>\n | WeakMap<object, unknown>\n | WeakSet<object>\n | Promise<unknown>\n | Error;\n\n/**\n * Brand symbol for marking objects that should receive recursive partial transformation.\n * @internal\n */\ndeclare const FormInputBrand: unique symbol;\n\n/**\n * Branded type to mark objects that should have their direct fields made partial.\n * Use with {@link partialFields} helper to mark specific schema objects.\n *\n * @example\n * ```typescript\n * // Objects marked with PartialFields will have their direct fields made optional\n * type MarkedObject = PartialFields<{ name: string; age: number }>;\n * ```\n */\nexport type PartialFields<T> = T & {\n readonly [FormInputBrand]?: typeof FormInputBrand;\n};\n\n/**\n * Helper type to safely get the brand property from a type.\n * Returns `unknown` for non-branded types, `typeof FormInputBrand | undefined` for branded types.\n * @internal\n */\ntype GetPartialFieldsBrand<T> = (T & {\n [FormInputBrand]?: unknown;\n})[typeof FormInputBrand];\n\n/**\n * Helper type to check if a type has the PartialFields brand.\n * Uses bidirectional extends check to distinguish branded from non-branded types.\n * @internal\n */\ntype IsPartialFieldsBranded<T> = GetPartialFieldsBrand<T> extends\n | typeof FormInputBrand\n | undefined\n ? typeof FormInputBrand extends GetPartialFieldsBrand<T>\n ? true\n : false\n : false;\n\n/**\n * Helper type to extract the inner type from a PartialFields branded type.\n * @internal\n */\ntype ExtractPartialFieldsInner<T> = T extends PartialFields<infer U>\n ? U\n : never;\n\n/**\n * Helper function to mark a Zod schema so its direct fields become partial.\n *\n * By default, nested objects in form inputs keep their fields strict (only the object\n * itself becomes nullable). Use this helper to opt-in specific objects to have their\n * direct fields also become optional.\n *\n * **Note:** This only affects the direct fields of the marked object. Nested objects\n * within it will still stay strict unless they are also wrapped with `partialFields()`.\n *\n * **Use cases:**\n * - Form input fields that users fill in manually (should be partial)\n * - Objects from selectors/dropdowns should NOT use this (keep strict)\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n *\n * const schema = z.object({\n * price: z.number(),\n * // User fills in these fields - opt-in to partial\n * detail: partialFields(z.object({\n * hotel: z.string(),\n * nights: z.number(),\n * })),\n * // Selected from dropdown - stays strict\n * agent: z.object({\n * name: z.string(),\n * fee: z.number(),\n * }),\n * });\n *\n * // Result with PartialWithNullableObjects:\n * // detail.hotel → string | undefined (partial - user input)\n * // detail.nights → number | undefined (partial - user input)\n * // agent.name → string (strict! - from selector)\n * // agent.fee → number (strict! - from selector)\n * ```\n */\nexport function partialFields<T extends z.ZodType>(\n schema: T,\n): T & z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>> {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return schema as T &\n z.ZodType<PartialFields<z.infer<T>>, PartialFields<z.input<T>>>;\n}\n\n/**\n * Transforms object types for form inputs with selective recursion.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives** (string, number, boolean): optional → `type | undefined`\n * - **Arrays**: optional → `type[] | undefined`\n * - **Built-in objects** (Date, RegExp, etc.): optional and nullable → `type | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict** → `{ strictField: type } | null | undefined`\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * This ensures objects from selectors/dropdowns keep strict types for their fields,\n * while form input fields can be partially filled.\n *\n * @example\n * ```typescript\n * import { partialFields } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * price: z.number(),\n * detail: partialFields(z.object({ hotel: z.string(), nights: z.number() })),\n * agent: z.object({ name: z.string(), fee: z.number() }),\n * });\n *\n * type FormInput = PartialWithNullableObjects<z.infer<typeof schema>>;\n * // {\n * // price?: number;\n * // detail?: { hotel?: string; nights?: number } | null; // Recursive!\n * // agent?: { name: string; fee: number } | null; // Strict nested!\n * // }\n * ```\n */\nexport type PartialWithNullableObjects<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] // Arrays: just optional (via ?), no null, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithNullableObjects<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K]; // Primitives: just optional (via ?)\n};\n\n/**\n * Transforms all fields to be optional and nullable, with selective recursion.\n *\n * Similar to {@link PartialWithNullableObjects} but also adds `| null` to primitives and arrays.\n *\n * **Default behavior (non-recursive):**\n * - **Primitives**: optional and nullable → `type | null | undefined`\n * - **Arrays**: optional and nullable → `type[] | null | undefined`\n * - **Plain objects**: optional and nullable, but nested fields stay **strict**\n *\n * **Opt-in recursive behavior:**\n * - Objects marked with {@link partialFields} will have their nested fields recursively transformed\n *\n * @example\n * ```typescript\n * type User = { name: string; age: number; profile: { bio: string } };\n * type FormInput = PartialWithAllNullables<User>;\n * // { name?: string | null; age?: number | null; profile?: { bio: string } | null; }\n * // Note: profile.bio stays strict (string, not string | null | undefined)\n * ```\n */\nexport type PartialWithAllNullables<T> = {\n [K in keyof T]?: T[K] extends readonly unknown[]\n ? T[K] | null // Arrays: optional + nullable, no recursion\n : T[K] extends BuiltInObject\n ? T[K] | null // Built-in objects: optional + nullable, no recursion\n : IsPartialFieldsBranded<T[K]> extends true\n ? Simplify<\n PartialWithAllNullables<ExtractPartialFieldsInner<T[K]>>\n > | null // FormInput marked: recurse + null + optional\n : T[K] extends object\n ? T[K] | null // Plain objects: optional + nullable, NO recursion (strict nested)\n : T[K] | null; // Primitives: optional + nullable\n};\n","import { zodResolver } from '@hookform/resolvers/zod';\nimport { type FieldValues, type UseFormProps, useForm } from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TFormInput - The form input type (defaults to PartialWithNullableObjects<TInput>)\n * @template TDefaultValues - The type of default values (inferred from usage for better type safety)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TInput extends FieldValues,\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TInput> = PartialWithNullableObjects<TInput>,\n TDefaultValues extends Partial<TFormInput> | undefined = undefined,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefaultValues;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return useForm({\n resolver,\n ...formOptions,\n } as unknown as UseFormProps<TFormInput, unknown, TOutput>);\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zod-utils/react-hook-form",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "description": "React Hook Form integration and utilities for Zod schemas",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",