@conform-to/react 1.15.0 → 1.16.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.
@@ -46,28 +46,33 @@ export type Control = {
46
46
  * both [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) and
47
47
  * [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) events.
48
48
  */
49
- change(value: string | string[] | boolean | File | File[] | FileList | null): void;
49
+ change: (value: string | string[] | boolean | File | File[] | FileList | null) => void;
50
50
  /**
51
51
  * Emits [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) and
52
52
  * [focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) events.
53
53
  * Does not actually move focus.
54
54
  */
55
- focus(): void;
55
+ focus: () => void;
56
56
  /**
57
57
  * Emits [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) and
58
58
  * [focusin](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event) events.
59
59
  * This does not move the actual keyboard focus to the input. Use `element.focus()` instead
60
60
  * if you want to move focus to the input.
61
61
  */
62
- blur(): void;
62
+ blur: () => void;
63
63
  };
64
- export type Selector<FormValue, Result> = (formData: FormValue | null, lastResult: Result | undefined) => Result;
65
- export type UseFormDataOptions = {
64
+ export type Selector<FormValue, Result> = (formData: FormValue, lastResult: Result | undefined) => Result;
65
+ export type UseFormDataOptions<Value = undefined> = {
66
66
  /**
67
67
  * Set to `true` to preserve file inputs and receive a `FormData` object in the selector.
68
68
  * If omitted or `false`, the selector receives a `URLSearchParams` object, where all values are coerced to strings.
69
69
  */
70
70
  acceptFiles?: boolean;
71
+ /**
72
+ * The fallback value to return when the form element is not available (e.g., on SSR or initial client render).
73
+ * If not provided, the hook returns `undefined` when the form is unavailable.
74
+ */
75
+ fallback?: Value;
71
76
  };
72
77
  export type DefaultValue<Shape> = Shape extends Record<string, any> ? {
73
78
  [Key in keyof Shape]?: DefaultValue<Shape[Key]>;
@@ -95,6 +100,152 @@ export type FormAction<ErrorShape extends BaseErrorShape = DefaultErrorShape, In
95
100
  intent: Intent;
96
101
  ctx: Context;
97
102
  };
103
+ /**
104
+ * Augment this interface to customize schema type inference for your schema library.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * import type { ZodTypeAny, input, output } from 'zod';
109
+ * import type { ZodSchemaOptions } from '@conform-to/zod/v3/future';
110
+ *
111
+ * declare module '@conform-to/react/future' {
112
+ * interface CustomSchemaTypes<Schema> {
113
+ * input: Schema extends ZodTypeAny ? input<Schema> : never;
114
+ * output: Schema extends ZodTypeAny ? output<Schema> : never;
115
+ * options: Schema extends ZodTypeAny ? ZodSchemaOptions : never;
116
+ * }
117
+ * }
118
+ * ```
119
+ */
120
+ export interface CustomSchemaTypes<Schema = unknown> {
121
+ }
122
+ /**
123
+ * Infer schema options type.
124
+ * Uses CustomSchemaTypes if augmented, otherwise returns never.
125
+ */
126
+ export type InferOptions<Schema> = [Schema] extends [undefined] ? never : CustomSchemaTypes<Schema> extends {
127
+ options: infer T;
128
+ } ? T : never;
129
+ /**
130
+ * Marker type for conditional field metadata properties.
131
+ * Used to indicate that a property should only be present when FieldShape matches a condition.
132
+ */
133
+ export type ConditionalFieldMetadata<T, Condition> = T & {
134
+ '~condition': Condition;
135
+ };
136
+ /**
137
+ * Check if T is a FieldName type by checking if '~shape' is a known key.
138
+ * Plain strings don't have '~shape' in their keyof, but FieldName<T> does.
139
+ */
140
+ type IsFieldName<T> = '~shape' extends keyof T ? true : false;
141
+ /**
142
+ * Transforms a single value, restoring FieldName<unknown> to FieldName<FieldShape>.
143
+ */
144
+ type RestoreFieldShapeValue<T, FieldShape> = T extends ConditionalFieldMetadata<infer Inner, infer Condition> ? FieldShape extends Condition ? Inner : never : IsFieldName<T> extends true ? FieldName<FieldShape> : T;
145
+ /**
146
+ * Restores FieldShape-dependent types that were inferred as `unknown`.
147
+ * This is needed because TypeScript infers generic return types with unresolved type parameters.
148
+ * Also handles conditional field metadata by checking if FieldShape extends the condition.
149
+ *
150
+ * Transforms up to 2 levels deep to handle cases like `textFieldProps.name`.
151
+ */
152
+ export type RestoreFieldShape<T, FieldShape> = {
153
+ [K in keyof T]: RestoreFieldShapeValue<T[K], FieldShape> extends infer V ? V extends Record<string, unknown> ? {
154
+ [P in keyof V]: RestoreFieldShapeValue<V[P], FieldShape>;
155
+ } : V : never;
156
+ };
157
+ /**
158
+ * Type guard function for conditional field metadata.
159
+ * Used to specify when a custom field metadata property should be available.
160
+ */
161
+ export type FieldShapeGuard<Condition> = (shape: unknown) => shape is Condition;
162
+ /**
163
+ * Function type for creating conditional field metadata based on shape constraints.
164
+ */
165
+ export type DefineConditionalField = <FieldShape, ErrorShape, Metadata>(metadata: BaseFieldMetadata<unknown, ErrorShape>, shape: FieldShapeGuard<FieldShape>, fn: (metadata: BaseFieldMetadata<FieldShape, ErrorShape>) => Metadata) => ConditionalFieldMetadata<Metadata, FieldShape>;
166
+ /**
167
+ * Extract the condition type from a FieldShapeGuard.
168
+ */
169
+ export type ExtractFieldCondition<T> = T extends FieldShapeGuard<infer C> ? C : never;
170
+ /**
171
+ * Extract conditions from a record of field shape guards.
172
+ */
173
+ export type ExtractFieldConditions<T extends Record<string, FieldShapeGuard<any>>> = {
174
+ [K in keyof T]: ExtractFieldCondition<T[K]>;
175
+ };
176
+ /**
177
+ * Resolved configuration from configureForms factory.
178
+ * Properties with defaults are required, others remain optional.
179
+ */
180
+ export type FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata extends Record<string, unknown>, CustomFieldMetadata extends Record<string, unknown>> = {
181
+ /**
182
+ * The name of the submit button field that indicates the submission intent.
183
+ * @default "__intent__"
184
+ */
185
+ intentName: string;
186
+ /**
187
+ * A custom serialization function for converting form data.
188
+ */
189
+ serialize: Serialize;
190
+ /**
191
+ * Determines when validation should run for the first time on a field.
192
+ * @default "onSubmit"
193
+ */
194
+ shouldValidate: 'onSubmit' | 'onBlur' | 'onInput';
195
+ /**
196
+ * Determines when validation should run again after the field has been validated once.
197
+ * @default Same as shouldValidate
198
+ */
199
+ shouldRevalidate: 'onSubmit' | 'onBlur' | 'onInput';
200
+ /**
201
+ * Runtime type guard to check if a value is a schema.
202
+ * Used to determine if the first argument to useForm is a schema or options object.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * import { configureForms } from '@conform-to/react/future';
207
+ * import {
208
+ * isSchema,
209
+ * validateSchema,
210
+ * getConstraints,
211
+ * } from '@conform-to/zod/v3/future';
212
+ *
213
+ * const { useForm } = configureForms({
214
+ * isSchema,
215
+ * validateSchema,
216
+ * getConstraints,
217
+ * });
218
+ * ```
219
+ */
220
+ isSchema: (schema: unknown) => schema is BaseSchema;
221
+ /**
222
+ * Validates a schema against form payload.
223
+ */
224
+ validateSchema: <Schema extends BaseSchema>(schema: Schema, payload: Record<string, FormValue>, options?: InferOptions<Schema>) => MaybePromise<{
225
+ error: FormError<string> | null;
226
+ value?: InferOutput<Schema>;
227
+ }>;
228
+ /**
229
+ * A type guard function to specify the shape of error objects.
230
+ */
231
+ isError?: (error: unknown) => error is BaseErrorShape;
232
+ /**
233
+ * Extracts HTML validation constraints from a schema.
234
+ */
235
+ getConstraints?: <Schema extends BaseSchema>(schema: Schema) => Record<string, ValidationAttributes> | undefined;
236
+ /**
237
+ * Extends form metadata with custom properties.
238
+ */
239
+ extendFormMetadata?: <ErrorShape extends BaseErrorShape>(metadata: BaseFormMetadata<ErrorShape>) => CustomFormMetadata;
240
+ /**
241
+ * Extends field metadata with custom properties.
242
+ * Use `when` for properties that depend on the field shape.
243
+ */
244
+ extendFieldMetadata?: <FieldShape, ErrorShape extends BaseErrorShape>(metadata: BaseFieldMetadata<FieldShape, ErrorShape>, ctx: {
245
+ form: BaseFormMetadata<ErrorShape>;
246
+ when: DefineConditionalField;
247
+ }) => CustomFieldMetadata;
248
+ };
98
249
  export type GlobalFormOptions = {
99
250
  /**
100
251
  * The name of the submit button field that indicates the submission intent.
@@ -129,9 +280,23 @@ export type NonPartial<T> = {
129
280
  };
130
281
  export type RequireKey<T, K extends keyof T> = Prettify<T & Pick<NonPartial<T>, K>>;
131
282
  export type BaseSchemaType = StandardSchemaV1<any, any>;
132
- export type InferInput<Schema> = Schema extends StandardSchemaV1<infer input, any> ? input : unknown;
133
- export type InferOutput<Schema> = Schema extends StandardSchemaV1<any, infer output> ? output : undefined;
134
- export type BaseFormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = undefined> = {
283
+ /**
284
+ * Infer schema input type.
285
+ * For StandardSchemaV1 schemas (zod, valibot, etc.), uses StandardSchemaV1.InferInput.
286
+ * For other schemas, uses CustomSchemaTypes if augmented.
287
+ */
288
+ export type InferInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<Schema> : CustomSchemaTypes<Schema> extends {
289
+ input: infer T;
290
+ } ? T : Record<string, any>;
291
+ /**
292
+ * Infer schema output type.
293
+ * For StandardSchemaV1 schemas (zod, valibot, etc.), uses StandardSchemaV1.InferOutput.
294
+ * For other schemas, uses CustomSchemaTypes if augmented.
295
+ */
296
+ export type InferOutput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<Schema> : CustomSchemaTypes<Schema> extends {
297
+ output: infer T;
298
+ } ? T : undefined;
299
+ export type BaseFormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = unknown> = {
135
300
  /** Optional form identifier. If not provided, a unique ID is automatically generated. */
136
301
  id?: string | undefined;
137
302
  /** Optional key for form state reset. When the key changes, the form resets to its initial state. */
@@ -144,6 +309,11 @@ export type BaseFormOptions<FormShape extends Record<string, any> = Record<strin
144
309
  defaultValue?: DefaultValue<FormShape> | undefined;
145
310
  /** HTML validation attributes for fields (required, minLength, pattern, etc.). */
146
311
  constraint?: Record<string, ValidationAttributes> | undefined;
312
+ /**
313
+ * Schema-specific validation options (e.g., Zod's errorMap).
314
+ * The available options depend on the schema library configured in `configureForms`.
315
+ */
316
+ schemaOptions?: InferOptions<Schema>;
147
317
  /**
148
318
  * Determines when validation should run for the first time on a field.
149
319
  * Overrides the global default set by FormOptionsProvider if provided.
@@ -167,7 +337,7 @@ export type BaseFormOptions<FormShape extends Record<string, any> = Record<strin
167
337
  /** Custom validation handler. Can be skipped if using the schema property, or combined with schema to customize validation errors. */
168
338
  onValidate?: ValidateHandler<ErrorShape, Value, InferOutput<Schema>> | undefined;
169
339
  };
170
- export type FormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = undefined, RequiredKeys extends keyof BaseFormOptions<FormShape, ErrorShape, Value, Schema> = never> = RequireKey<BaseFormOptions<FormShape, ErrorShape, Value, Schema>, RequiredKeys>;
340
+ export type FormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = unknown, RequiredKeys extends keyof BaseFormOptions<FormShape, ErrorShape, Value, Schema> = never> = RequireKey<BaseFormOptions<FormShape, ErrorShape, Value, Schema>, RequiredKeys>;
171
341
  export interface FormContext<ErrorShape extends BaseErrorShape = DefaultErrorShape> {
172
342
  /** The form's unique identifier */
173
343
  formId: string;
@@ -329,8 +499,16 @@ export type BaseErrorShape = CustomTypes extends {
329
499
  export type DefaultErrorShape = CustomTypes extends {
330
500
  errorShape: infer Shape;
331
501
  } ? Shape : string;
332
- /** Base field metadata object containing field state, validation attributes, and accessibility IDs. */
333
- export type BaseMetadata<FieldShape, ErrorShape extends BaseErrorShape> = ValidationAttributes & {
502
+ export type SatisfyComponentProps<ElementType extends React.ElementType, CustomProps extends React.ComponentPropsWithoutRef<ElementType>> = CustomProps;
503
+ /**
504
+ * Interface for extending field metadata with additional properties.
505
+ * @deprecated Use `configureForms()` with the `extendFieldMetadata` option for full type inference support.
506
+ */
507
+ export interface CustomMetadata<FieldShape = any, ErrorShape extends BaseErrorShape = DefaultErrorShape> {
508
+ }
509
+ export type DefaultCustomMetadata<FieldShape, ErrorShape> = keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<FieldShape, ErrorShape>;
510
+ /** Field metadata object containing field state, validation attributes, and nested field access methods. */
511
+ export type FieldMetadata<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = DefaultCustomMetadata<FieldShape, ErrorShape>> = Readonly<Prettify<ValidationAttributes & {
334
512
  /** Unique key for React list rendering (for array fields). */
335
513
  key: string | undefined;
336
514
  /** The field name path exactly as provided. */
@@ -380,27 +558,31 @@ export type BaseMetadata<FieldShape, ErrorShape extends BaseErrorShape> = Valida
380
558
  /** String value for the `aria-describedby` attribute. Contains the errorId when invalid, undefined otherwise. Merge with descriptionId manually if needed (e.g. `${metadata.descriptionId} ${metadata.ariaDescribedBy}`). */
381
559
  ariaDescribedBy: string | undefined;
382
560
  /** Method to get nested fieldset for object fields under this field. */
383
- getFieldset<FieldsetShape = keyof NonNullable<FieldShape> extends never ? unknown : FieldShape>(): Fieldset<FieldsetShape, ErrorShape>;
561
+ getFieldset<FieldsetShape = keyof NonNullable<FieldShape> extends never ? unknown : FieldShape>(): Fieldset<FieldsetShape, ErrorShape, CustomFieldMetadata>;
384
562
  /** Method to get array of fields for list/array fields under this field. */
385
563
  getFieldList<FieldItemShape = [FieldShape] extends [
386
564
  Array<infer ItemShape> | null | undefined
387
- ] ? ItemShape : unknown>(): Array<FieldMetadata<FieldItemShape, ErrorShape>>;
388
- };
389
- export type SatisfyComponentProps<ElementType extends React.ElementType, CustomProps extends React.ComponentPropsWithoutRef<ElementType>> = CustomProps;
565
+ ] ? ItemShape : unknown>(): Array<FieldMetadata<FieldItemShape, ErrorShape, CustomFieldMetadata>>;
566
+ } & RestoreFieldShape<CustomFieldMetadata, FieldShape>>>;
390
567
  /**
391
- * Interface for extending field metadata with additional properties.
568
+ * Field metadata without custom extensions. This is the type received in `extendFieldMetadata`.
569
+ * Equivalent to `FieldMetadata<FieldShape, ErrorShape, {}>`.
392
570
  */
393
- export interface CustomMetadata<FieldShape = any, ErrorShape extends BaseErrorShape = DefaultErrorShape> {
394
- }
395
- export type CustomMetadataDefinition = <FieldShape, ErrorShape extends BaseErrorShape>(metadata: BaseMetadata<FieldShape, ErrorShape>) => keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<any, any>;
396
- /** Field metadata object containing field state, validation attributes, and nested field access methods. */
397
- export type FieldMetadata<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape> = Readonly<(keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<FieldShape, ErrorShape>) & BaseMetadata<FieldShape, ErrorShape>>;
571
+ export type BaseFieldMetadata<FieldShape, ErrorShape extends BaseErrorShape> = FieldMetadata<FieldShape, ErrorShape, {}>;
572
+ /**
573
+ * @deprecated Renamed to `BaseFieldMetadata`. This will be removed in the next minor version.
574
+ */
575
+ export type BaseMetadata<FieldShape, ErrorShape extends BaseErrorShape> = BaseFieldMetadata<FieldShape, ErrorShape>;
576
+ /**
577
+ * @deprecated Use `configureForms()` with the `extendFieldMetadata` option instead.
578
+ */
579
+ export type CustomMetadataDefinition = <FieldShape, ErrorShape extends BaseErrorShape>(metadata: BaseFieldMetadata<FieldShape, ErrorShape>) => keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<any, any>;
398
580
  /** Fieldset object containing all form fields as properties with their respective field metadata. */
399
- export type Fieldset<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape> = {
400
- [Key in keyof Combine<FieldShape>]-?: FieldMetadata<Combine<FieldShape>[Key], ErrorShape>;
581
+ export type Fieldset<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = DefaultCustomMetadata<FieldShape, ErrorShape>> = {
582
+ [Key in keyof Combine<FieldShape>]-?: FieldMetadata<Combine<FieldShape>[Key], ErrorShape, CustomFieldMetadata>;
401
583
  };
402
584
  /** Form-level metadata and state object containing validation status, errors, and field access methods. */
403
- export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape> = Readonly<{
585
+ export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}> = Readonly<{
404
586
  /** Unique identifier that changes on form reset */
405
587
  key: string;
406
588
  /** The form's unique identifier. */
@@ -432,14 +614,19 @@ export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape>
432
614
  /** The current state of the form */
433
615
  context: FormContext<ErrorShape>;
434
616
  /** Method to get metadata for a specific field by name. */
435
- getField<FieldShape>(name: FieldName<FieldShape>): FieldMetadata<FieldShape, ErrorShape>;
617
+ getField<FieldShape>(name: FieldName<FieldShape>): FieldMetadata<FieldShape, ErrorShape, CustomFieldMetadata>;
436
618
  /** Method to get a fieldset object for nested object fields. */
437
- getFieldset<FieldShape>(name?: FieldName<FieldShape>): Fieldset<keyof NonNullable<FieldShape> extends never ? unknown : FieldShape, ErrorShape>;
619
+ getFieldset<FieldShape>(name?: FieldName<FieldShape>): Fieldset<keyof NonNullable<FieldShape> extends never ? unknown : FieldShape, ErrorShape, CustomFieldMetadata>;
438
620
  /** Method to get an array of field objects for array fields. */
439
621
  getFieldList<FieldShape>(name: FieldName<FieldShape>): Array<FieldMetadata<[
440
622
  FieldShape
441
- ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape>>;
442
- }>;
623
+ ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape, CustomFieldMetadata>>;
624
+ } & CustomFormMetadata>;
625
+ /**
626
+ * Form metadata without custom extensions. This is the type received in `extendFormMetadata`.
627
+ * Equivalent to `FormMetadata<ErrorShape, {}, CustomFieldMetadata>`.
628
+ */
629
+ export type BaseFormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = {}> = FormMetadata<ErrorShape, {}, CustomFieldMetadata>;
443
630
  export type ValidateResult<ErrorShape, Value> = FormError<ErrorShape> | null | {
444
631
  error: FormError<ErrorShape> | null;
445
632
  value?: Value;
@@ -508,5 +695,65 @@ export type SubmitContext<FormShape extends Record<string, any> = Record<string,
508
695
  }) => void;
509
696
  };
510
697
  export type SubmitHandler<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = DefaultErrorShape, Value = undefined> = (event: React.FormEvent<HTMLFormElement>, ctx: SubmitContext<FormShape, ErrorShape, Value>) => void | Promise<void>;
698
+ /**
699
+ * Infer the base error shape from a FormsConfig.
700
+ *
701
+ * @example
702
+ * ```ts
703
+ * const { config } = configureForms({ isError: shape<{ message: string }>() });
704
+ * type ErrorShape = InferBaseErrorShape<typeof config>; // { message: string }
705
+ * ```
706
+ */
707
+ export type InferBaseErrorShape<Config> = Config extends FormsConfig<infer ErrorShape, any, any, any> ? ErrorShape : string;
708
+ /**
709
+ * Infer the custom form metadata extension from a FormsConfig.
710
+ * Use this to compose with FormMetadata, FieldMetadata, or Fieldset types.
711
+ *
712
+ * @example
713
+ * ```ts
714
+ * const { config } = configureForms({
715
+ * extendFormMetadata: (meta) => ({ customProp: meta.id })
716
+ * });
717
+ * type MyFormMetadata = FormMetadata<
718
+ * InferBaseErrorShape<typeof config>,
719
+ * InferCustomFormMetadata<typeof config>,
720
+ * InferCustomFieldMetadata<typeof config>
721
+ * >;
722
+ * ```
723
+ */
724
+ export type InferCustomFormMetadata<Config> = Config extends FormsConfig<any, any, infer CustomFormMetadata, any> ? CustomFormMetadata : {};
725
+ /**
726
+ * Infer the custom field metadata extension from a FormsConfig.
727
+ * Use this to compose with FieldMetadata or Fieldset types.
728
+ *
729
+ * @example
730
+ * ```ts
731
+ * const { config } = configureForms({
732
+ * extendFieldMetadata: (meta) => ({ inputProps: { name: meta.name } })
733
+ * });
734
+ * type MyFieldMetadata<T> = FieldMetadata<T, InferBaseErrorShape<typeof config>, InferCustomFieldMetadata<typeof config>>;
735
+ * type MyFieldset<T> = Fieldset<T, InferBaseErrorShape<typeof config>, InferCustomFieldMetadata<typeof config>>;
736
+ * ```
737
+ */
738
+ export type InferCustomFieldMetadata<Config> = Config extends FormsConfig<any, any, any, infer CustomFieldMetadata> ? CustomFieldMetadata : {};
739
+ /**
740
+ * Transform a type to make specific keys conditional based on FieldShape.
741
+ * Keys in ConditionalKeys will only be present when FieldShape extends the specified type.
742
+ * Uses ConditionalFieldMetadata wrapper that RestoreFieldShape will detect and evaluate.
743
+ *
744
+ * @example
745
+ * ```ts
746
+ * type Result = MakeConditional<
747
+ * { textFieldProps: {...}, dateRangePickerProps: {...} },
748
+ * { dateRangePickerProps: { start: string; end: string } }
749
+ * >;
750
+ * // dateRangePickerProps is wrapped with ConditionalFieldMetadata
751
+ * // and will only be present when FieldShape extends { start: string; end: string }
752
+ * ```
753
+ */
754
+ export type MakeConditional<T, ConditionalKeys extends Record<string, unknown>> = Omit<T, keyof ConditionalKeys> & {
755
+ [K in keyof ConditionalKeys]: K extends keyof T ? ConditionalFieldMetadata<T[K], ConditionalKeys[K]> : never;
756
+ };
757
+ export type MaybePromise<T> = T | Promise<T>;
511
758
  export {};
512
759
  //# sourceMappingURL=types.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import type { FormError } from '@conform-to/dom/future';
2
2
  import type { StandardSchemaV1 } from './standard-schema';
3
- import { ValidateHandler, ValidateResult } from './types';
3
+ import { ValidateHandler, ValidateResult, BaseFieldMetadata, ConditionalFieldMetadata } from './types';
4
4
  export declare function isUndefined(value: unknown): value is undefined;
5
5
  export declare function isString(value: unknown): value is string;
6
6
  export declare function isNumber(value: unknown): value is number;
@@ -42,6 +42,9 @@ export declare function resolveValidateResult<ErrorShape, Value>(result: ReturnT
42
42
  value?: Value | undefined;
43
43
  }> | undefined;
44
44
  };
45
+ /**
46
+ * Resolves a StandardSchema validation result to conform's format.
47
+ */
45
48
  export declare function resolveStandardSchemaResult<Value>(result: StandardSchemaV1.Result<Value>): {
46
49
  error: FormError<string> | null;
47
50
  value?: Value;
@@ -65,4 +68,44 @@ export declare function appendUniqueItem<Item>(list: Array<Item>, item: Item): I
65
68
  */
66
69
  export declare function compactMap<Item>(list: Array<NonNullable<Item>>, fn: (value: Item) => Item | null): Array<Item>;
67
70
  export declare function generateUniqueKey(): string;
71
+ /**
72
+ * Creates a type-only marker for TypeScript inference.
73
+ *
74
+ * This function always returns `true` at runtime. It exists
75
+ * purely to capture the generic type parameter for compile-time type checking.
76
+ * No runtime validation is performed.
77
+ *
78
+ * Common uses:
79
+ * - `isError`: Specify the error shape for type inference
80
+ * - `when`: Narrow field metadata to specific shapes for conditional props
81
+ *
82
+ * @example Specify error shape
83
+ * ```ts
84
+ * configureForms({
85
+ * isError: shape<string>(), // errors are strings
86
+ * });
87
+ * ```
88
+ *
89
+ * @example Conditional field metadata
90
+ * ```ts
91
+ * extendFieldMetadata(metadata, { when }) {
92
+ * return {
93
+ * get dateRangePickerProps() {
94
+ * return when(metadata, shape<{ start: string; end: string }>(), (m) => ({
95
+ * startName: m.getFieldset().start.name,
96
+ * endName: m.getFieldset().end.name,
97
+ * }));
98
+ * },
99
+ * };
100
+ * }
101
+ * ```
102
+ */
103
+ export declare function shape<T>(): (value: unknown) => value is T;
104
+ /**
105
+ * Creates a conditional field metadata property that is only available
106
+ * when the field shape matches the specified type.
107
+ */
108
+ export declare function when<FieldShape, ErrorShape, Metadata>(metadata: BaseFieldMetadata<unknown, ErrorShape>, _shape: (value: unknown) => value is FieldShape, fn: (m: BaseFieldMetadata<FieldShape, ErrorShape>) => Metadata): ConditionalFieldMetadata<Metadata, FieldShape>;
109
+ export declare function isStandardSchemaV1(schema: unknown): schema is StandardSchemaV1;
110
+ export declare function validateStandardSchemaV1<Schema extends StandardSchemaV1>(schema: Schema, payload: Record<string, unknown>): any;
68
111
  //# sourceMappingURL=util.d.ts.map
@@ -118,6 +118,10 @@ function resolveValidateResult(result) {
118
118
  asyncResult: asyncResult ? asyncResult.then(normalizeValidateResult) : undefined
119
119
  };
120
120
  }
121
+
122
+ /**
123
+ * Resolves a StandardSchema validation result to conform's format.
124
+ */
121
125
  function resolveStandardSchemaResult(result) {
122
126
  if (!result.issues) {
123
127
  return {
@@ -149,10 +153,10 @@ function merge(obj, update) {
149
153
  */
150
154
  function transformKeys(obj, fn) {
151
155
  var result = {};
152
- for (var [_key, _value] of Object.entries(obj)) {
156
+ for (var [_key, _value2] of Object.entries(obj)) {
153
157
  var _name = fn(_key);
154
158
  if (_name !== null) {
155
- result[_name] = _value;
159
+ result[_name] = _value2;
156
160
  }
157
161
  }
158
162
  return result;
@@ -175,9 +179,9 @@ function appendUniqueItem(list, item) {
175
179
  function compactMap(list, fn) {
176
180
  var result = [];
177
181
  for (var item of list) {
178
- var _value2 = fn(item);
179
- if (_value2 !== null) {
180
- result.push(_value2);
182
+ var _value3 = fn(item);
183
+ if (_value3 !== null) {
184
+ result.push(_value3);
181
185
  }
182
186
  }
183
187
  return result;
@@ -186,6 +190,60 @@ function generateUniqueKey() {
186
190
  return Math.trunc(Date.now() * Math.random()).toString(36);
187
191
  }
188
192
 
193
+ /**
194
+ * Creates a type-only marker for TypeScript inference.
195
+ *
196
+ * This function always returns `true` at runtime. It exists
197
+ * purely to capture the generic type parameter for compile-time type checking.
198
+ * No runtime validation is performed.
199
+ *
200
+ * Common uses:
201
+ * - `isError`: Specify the error shape for type inference
202
+ * - `when`: Narrow field metadata to specific shapes for conditional props
203
+ *
204
+ * @example Specify error shape
205
+ * ```ts
206
+ * configureForms({
207
+ * isError: shape<string>(), // errors are strings
208
+ * });
209
+ * ```
210
+ *
211
+ * @example Conditional field metadata
212
+ * ```ts
213
+ * extendFieldMetadata(metadata, { when }) {
214
+ * return {
215
+ * get dateRangePickerProps() {
216
+ * return when(metadata, shape<{ start: string; end: string }>(), (m) => ({
217
+ * startName: m.getFieldset().start.name,
218
+ * endName: m.getFieldset().end.name,
219
+ * }));
220
+ * },
221
+ * };
222
+ * }
223
+ * ```
224
+ */
225
+ function shape() {
226
+ return _value => true;
227
+ }
228
+
229
+ /**
230
+ * Creates a conditional field metadata property that is only available
231
+ * when the field shape matches the specified type.
232
+ */
233
+ function when(metadata, _shape, fn) {
234
+ return fn(metadata);
235
+ }
236
+ function isStandardSchemaV1(schema) {
237
+ return typeof schema === 'object' && schema !== null && '~standard' in schema && typeof schema['~standard'] === 'object' && schema['~standard'] !== null && 'version' in schema['~standard'] && schema['~standard'].version === 1;
238
+ }
239
+ function validateStandardSchemaV1(schema, payload) {
240
+ var result = schema['~standard'].validate(payload);
241
+ if (result instanceof Promise) {
242
+ return result.then(actualResult => resolveStandardSchemaResult(actualResult));
243
+ }
244
+ return resolveStandardSchemaResult(result);
245
+ }
246
+
189
247
  exports.appendUniqueItem = appendUniqueItem;
190
248
  exports.compactMap = compactMap;
191
249
  exports.createPathIndexUpdater = createPathIndexUpdater;
@@ -194,6 +252,7 @@ exports.getArrayAtPath = getArrayAtPath;
194
252
  exports.isNullable = isNullable;
195
253
  exports.isNumber = isNumber;
196
254
  exports.isOptional = isOptional;
255
+ exports.isStandardSchemaV1 = isStandardSchemaV1;
197
256
  exports.isString = isString;
198
257
  exports.isUndefined = isUndefined;
199
258
  exports.merge = merge;
@@ -201,5 +260,8 @@ exports.normalizeFormError = normalizeFormError;
201
260
  exports.normalizeValidateResult = normalizeValidateResult;
202
261
  exports.resolveStandardSchemaResult = resolveStandardSchemaResult;
203
262
  exports.resolveValidateResult = resolveValidateResult;
263
+ exports.shape = shape;
204
264
  exports.transformKeys = transformKeys;
205
265
  exports.updateValueAtPath = updateValueAtPath;
266
+ exports.validateStandardSchemaV1 = validateStandardSchemaV1;
267
+ exports.when = when;
@@ -114,6 +114,10 @@ function resolveValidateResult(result) {
114
114
  asyncResult: asyncResult ? asyncResult.then(normalizeValidateResult) : undefined
115
115
  };
116
116
  }
117
+
118
+ /**
119
+ * Resolves a StandardSchema validation result to conform's format.
120
+ */
117
121
  function resolveStandardSchemaResult(result) {
118
122
  if (!result.issues) {
119
123
  return {
@@ -145,10 +149,10 @@ function merge(obj, update) {
145
149
  */
146
150
  function transformKeys(obj, fn) {
147
151
  var result = {};
148
- for (var [_key, _value] of Object.entries(obj)) {
152
+ for (var [_key, _value2] of Object.entries(obj)) {
149
153
  var _name = fn(_key);
150
154
  if (_name !== null) {
151
- result[_name] = _value;
155
+ result[_name] = _value2;
152
156
  }
153
157
  }
154
158
  return result;
@@ -171,9 +175,9 @@ function appendUniqueItem(list, item) {
171
175
  function compactMap(list, fn) {
172
176
  var result = [];
173
177
  for (var item of list) {
174
- var _value2 = fn(item);
175
- if (_value2 !== null) {
176
- result.push(_value2);
178
+ var _value3 = fn(item);
179
+ if (_value3 !== null) {
180
+ result.push(_value3);
177
181
  }
178
182
  }
179
183
  return result;
@@ -182,4 +186,58 @@ function generateUniqueKey() {
182
186
  return Math.trunc(Date.now() * Math.random()).toString(36);
183
187
  }
184
188
 
185
- export { appendUniqueItem, compactMap, createPathIndexUpdater, generateUniqueKey, getArrayAtPath, isNullable, isNumber, isOptional, isString, isUndefined, merge, normalizeFormError, normalizeValidateResult, resolveStandardSchemaResult, resolveValidateResult, transformKeys, updateValueAtPath };
189
+ /**
190
+ * Creates a type-only marker for TypeScript inference.
191
+ *
192
+ * This function always returns `true` at runtime. It exists
193
+ * purely to capture the generic type parameter for compile-time type checking.
194
+ * No runtime validation is performed.
195
+ *
196
+ * Common uses:
197
+ * - `isError`: Specify the error shape for type inference
198
+ * - `when`: Narrow field metadata to specific shapes for conditional props
199
+ *
200
+ * @example Specify error shape
201
+ * ```ts
202
+ * configureForms({
203
+ * isError: shape<string>(), // errors are strings
204
+ * });
205
+ * ```
206
+ *
207
+ * @example Conditional field metadata
208
+ * ```ts
209
+ * extendFieldMetadata(metadata, { when }) {
210
+ * return {
211
+ * get dateRangePickerProps() {
212
+ * return when(metadata, shape<{ start: string; end: string }>(), (m) => ({
213
+ * startName: m.getFieldset().start.name,
214
+ * endName: m.getFieldset().end.name,
215
+ * }));
216
+ * },
217
+ * };
218
+ * }
219
+ * ```
220
+ */
221
+ function shape() {
222
+ return _value => true;
223
+ }
224
+
225
+ /**
226
+ * Creates a conditional field metadata property that is only available
227
+ * when the field shape matches the specified type.
228
+ */
229
+ function when(metadata, _shape, fn) {
230
+ return fn(metadata);
231
+ }
232
+ function isStandardSchemaV1(schema) {
233
+ return typeof schema === 'object' && schema !== null && '~standard' in schema && typeof schema['~standard'] === 'object' && schema['~standard'] !== null && 'version' in schema['~standard'] && schema['~standard'].version === 1;
234
+ }
235
+ function validateStandardSchemaV1(schema, payload) {
236
+ var result = schema['~standard'].validate(payload);
237
+ if (result instanceof Promise) {
238
+ return result.then(actualResult => resolveStandardSchemaResult(actualResult));
239
+ }
240
+ return resolveStandardSchemaResult(result);
241
+ }
242
+
243
+ export { appendUniqueItem, compactMap, createPathIndexUpdater, generateUniqueKey, getArrayAtPath, isNullable, isNumber, isOptional, isStandardSchemaV1, isString, isUndefined, merge, normalizeFormError, normalizeValidateResult, resolveStandardSchemaResult, resolveValidateResult, shape, transformKeys, updateValueAtPath, validateStandardSchemaV1, when };