@conform-to/react 1.19.3 → 1.20.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
@@ -7,7 +7,7 @@
7
7
  ╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
8
8
  ```
9
9
 
10
- Version 1.19.3 / License MIT / Copyright (c) 2026 Edmund Hung
10
+ Version 1.20.0 / License MIT / Copyright (c) 2026 Edmund Hung
11
11
 
12
12
  Progressively enhance HTML forms with React. Build resilient, type-safe forms with no hassle using web standards.
13
13
 
package/dist/context.d.ts CHANGED
@@ -20,7 +20,7 @@ export type Metadata<Schema, FormSchema extends Record<string, unknown>, FormErr
20
20
  valid: boolean;
21
21
  dirty: boolean;
22
22
  };
23
- export type FormMetadata<Schema extends Record<string, unknown> = Record<string, unknown>, FormError = string[]> = Omit<Metadata<Schema, Schema, FormError>, 'id'> & Pick<FormContext<Schema, FormError>, Intent['type']> & {
23
+ export type FormMetadata<Schema extends Record<string, unknown> = any, FormError = string[]> = Omit<Metadata<Schema, Schema, FormError>, 'id'> & Pick<FormContext<Schema, FormError>, Intent['type']> & {
24
24
  id: FormId<Schema, FormError>;
25
25
  context: Wrapped<FormContext<Schema, FormError>>;
26
26
  status?: 'success' | 'error';
@@ -37,7 +37,7 @@ type SubfieldMetadata<Schema, FormSchema extends Record<string, any>, FormError>
37
37
  [Key in keyof Combine<Schema>]: FieldMetadata<Combine<Schema>[Key], FormSchema, FormError>;
38
38
  }>;
39
39
  } : {};
40
- export type FieldMetadata<Schema = unknown, FormSchema extends Record<string, any> = Record<string, unknown>, FormError = string[]> = Metadata<Schema, FormSchema, FormError> & Constraint & {
40
+ export type FieldMetadata<Schema = unknown, FormSchema extends Record<string, any> = any, FormError = string[]> = Metadata<Schema, FormSchema, FormError> & Constraint & {
41
41
  formId: FormId<FormSchema, FormError>;
42
42
  } & SubfieldMetadata<Schema, FormSchema, FormError>;
43
43
  export declare const Form: import("react").Context<FormContext<any, string[], any>[]>;
@@ -1,5 +1,5 @@
1
1
  import { Serialize } from '@conform-to/dom/future';
2
- import type { ControlOptions, ErrorContext, FormRef, IntentDispatcher } from './types';
2
+ import type { ControlOptions, ErrorContext, FormRef, IntentDispatcher, IntentHandler } from './types';
3
3
  export declare function getFormElement(formRef: FormRef | undefined): HTMLFormElement | null;
4
4
  export declare function getSubmitEvent(event: React.FormEvent<HTMLFormElement>): SubmitEvent;
5
5
  export declare function initializeField(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, options: {
@@ -20,7 +20,7 @@ export declare function resetFormValue(form: HTMLFormElement, defaultValue: Reco
20
20
  * Creates a proxy that dynamically generates intent dispatch functions.
21
21
  * Each property access returns a function that submits the intent to the form.
22
22
  */
23
- export declare function createIntentDispatcher<FormShape extends Record<string, any>>(formElement: HTMLFormElement | (() => HTMLFormElement | null), intentName: string): IntentDispatcher<FormShape>;
23
+ export declare function createIntentDispatcher<FormShape extends Record<string, any>, IntentHandlers extends Record<string, IntentHandler<any, any>>>(formElement: HTMLFormElement | (() => HTMLFormElement | null), intentName: string): IntentDispatcher<FormShape, IntentHandlers>;
24
24
  /**
25
25
  * Restores values from preserved inputs and removes them.
26
26
  * Called when PreserveBoundary mounts.
@@ -97,7 +97,9 @@ function resolveControlPayload(input) {
97
97
  }
98
98
  }
99
99
  for (var [name, _value] of entries) {
100
- future.setPathValue(result, name, _value);
100
+ future.setPathValue(result, name, _value, {
101
+ mutate: true
102
+ });
101
103
  }
102
104
  return future.getPathValue(result, input.name);
103
105
  }
@@ -118,7 +120,7 @@ function deriveDefaultPayload(options) {
118
120
  * Does nothing if the submission was triggered with a specific intent (e.g. validate / insert)
119
121
  */
120
122
  function focusFirstInvalidField(ctx) {
121
- if (ctx.intent) {
123
+ if (ctx.intent && ctx.intent.type !== 'submit') {
122
124
  return;
123
125
  }
124
126
  for (var element of ctx.formElement.elements) {
@@ -182,14 +184,17 @@ function createIntentDispatcher(formElement, intentName) {
182
184
  if (typeof type === 'string') {
183
185
  var _target$type;
184
186
  // @ts-expect-error
185
- (_target$type = target[type]) !== null && _target$type !== void 0 ? _target$type : target[type] = payload => {
187
+ (_target$type = target[type]) !== null && _target$type !== void 0 ? _target$type : target[type] = function () {
186
188
  var form = typeof formElement === 'function' ? formElement() : formElement;
187
189
  if (!form) {
188
190
  throw new Error("Dispatching \"".concat(type, "\" intent failed; No form element found."));
189
191
  }
192
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
193
+ args[_key] = arguments[_key];
194
+ }
190
195
  future.requestIntent(form, intentName, intent.serializeIntent({
191
196
  type,
192
- payload
197
+ args
193
198
  }));
194
199
  };
195
200
  }
@@ -93,7 +93,9 @@ function resolveControlPayload(input) {
93
93
  }
94
94
  }
95
95
  for (var [name, _value] of entries) {
96
- setPathValue(result, name, _value);
96
+ setPathValue(result, name, _value, {
97
+ mutate: true
98
+ });
97
99
  }
98
100
  return getPathValue(result, input.name);
99
101
  }
@@ -114,7 +116,7 @@ function deriveDefaultPayload(options) {
114
116
  * Does nothing if the submission was triggered with a specific intent (e.g. validate / insert)
115
117
  */
116
118
  function focusFirstInvalidField(ctx) {
117
- if (ctx.intent) {
119
+ if (ctx.intent && ctx.intent.type !== 'submit') {
118
120
  return;
119
121
  }
120
122
  for (var element of ctx.formElement.elements) {
@@ -178,14 +180,17 @@ function createIntentDispatcher(formElement, intentName) {
178
180
  if (typeof type === 'string') {
179
181
  var _target$type;
180
182
  // @ts-expect-error
181
- (_target$type = target[type]) !== null && _target$type !== void 0 ? _target$type : target[type] = payload => {
183
+ (_target$type = target[type]) !== null && _target$type !== void 0 ? _target$type : target[type] = function () {
182
184
  var form = typeof formElement === 'function' ? formElement() : formElement;
183
185
  if (!form) {
184
186
  throw new Error("Dispatching \"".concat(type, "\" intent failed; No form element found."));
185
187
  }
188
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
189
+ args[_key] = arguments[_key];
190
+ }
186
191
  requestIntent(form, intentName, serializeIntent({
187
192
  type,
188
- payload
193
+ args
189
194
  }));
190
195
  };
191
196
  }
@@ -1,31 +1,46 @@
1
1
  import { FieldName } from '@conform-to/dom';
2
+ import { type Submission, FormValue } from '@conform-to/dom/future';
2
3
  import { StandardSchemaV1 } from './standard-schema';
3
- import { FormRef, FormsConfig, FormContext, FormMetadata, FormOptions, FieldMetadata, InferOutput, IntentDispatcher, InferFormShape, RequireKey, FormHandle } from './types';
4
- export declare function configureForms<BaseErrorShape = any, BaseSchema = StandardSchemaV1, SchemaErrorShape = string[], CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}>(config?: Partial<FormsConfig<BaseErrorShape, BaseSchema, SchemaErrorShape, CustomFormMetadata, CustomFieldMetadata>>): {
4
+ import type { FormRef, FormsConfig, FormContext, FormMetadata, FormOptions, FieldMetadata, FormHandle, FormCustomState, InferOutput, InferFormShape, DefaultIntentHandlers, IntentHandler, IntentDispatcher, RequireKey, FormIntent, CustomStateHandler } from './types';
5
+ export declare function configureForms<BaseErrorShape = any, BaseSchema = StandardSchemaV1, SchemaErrorShape = string[], CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}, GlobalIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, GlobalCustomStateHandlers extends Record<string, CustomStateHandler<any, any, BaseErrorShape>> = {}>(config?: Partial<FormsConfig<BaseErrorShape, BaseSchema, SchemaErrorShape, CustomFormMetadata, CustomFieldMetadata, GlobalIntentHandlers, GlobalCustomStateHandlers>>): {
5
6
  FormProvider: (props: {
6
- context: FormContext<BaseErrorShape>;
7
+ context: FormContext<BaseErrorShape, FormCustomState<GlobalCustomStateHandlers>>;
7
8
  children: React.ReactNode;
8
9
  }) => React.ReactElement;
9
10
  useForm: {
10
- <Schema extends BaseSchema, ErrorShape extends BaseErrorShape, Value = InferOutput<Schema>>(schema: Schema, options: RequireKey<FormOptions<InferFormShape<Schema>, ErrorShape, Value, Schema, SchemaErrorShape>, "onValidate">): FormHandle<InferFormShape<Schema>, ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
11
- <Schema extends BaseSchema, ErrorShape extends BaseErrorShape = SchemaErrorShape extends BaseErrorShape ? SchemaErrorShape : BaseErrorShape, Value = InferOutput<Schema>>(schema: Schema, options: RequireKey<FormOptions<InferFormShape<Schema>, ErrorShape, Value, Schema, SchemaErrorShape>, SchemaErrorShape extends BaseErrorShape ? never : "onValidate">): FormHandle<InferFormShape<Schema>, ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
12
- <FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = BaseErrorShape, Value = undefined>(options: RequireKey<FormOptions<FormShape, ErrorShape, Value, undefined, SchemaErrorShape>, "onValidate">): FormHandle<FormShape, ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
11
+ <Schema extends BaseSchema, ErrorShape extends BaseErrorShape, Value = InferOutput<Schema>, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(schema: Schema, options: RequireKey<FormOptions<InferFormShape<Schema>, ErrorShape, Value, Schema, SchemaErrorShape, CustomIntentHandlers, GlobalIntentHandlers, CustomStateHandlers>, "onValidate">): FormHandle<InferFormShape<Schema>, ErrorShape, CustomFormMetadata, CustomFieldMetadata, GlobalIntentHandlers & CustomIntentHandlers, FormCustomState<GlobalCustomStateHandlers & CustomStateHandlers>>;
12
+ <Schema extends BaseSchema, ErrorShape extends BaseErrorShape = SchemaErrorShape extends BaseErrorShape ? SchemaErrorShape : BaseErrorShape, Value = InferOutput<Schema>, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(schema: Schema, options: RequireKey<FormOptions<InferFormShape<Schema>, ErrorShape, Value, Schema, SchemaErrorShape, CustomIntentHandlers, GlobalIntentHandlers, CustomStateHandlers>, SchemaErrorShape extends BaseErrorShape ? never : "onValidate">): FormHandle<InferFormShape<Schema>, ErrorShape, CustomFormMetadata, CustomFieldMetadata, GlobalIntentHandlers & CustomIntentHandlers, FormCustomState<GlobalCustomStateHandlers & CustomStateHandlers>>;
13
+ <FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = BaseErrorShape, Value = undefined, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(options: RequireKey<FormOptions<FormShape, ErrorShape, Value, undefined, SchemaErrorShape, CustomIntentHandlers, GlobalIntentHandlers, CustomStateHandlers>, "onValidate">): FormHandle<FormShape, ErrorShape, CustomFormMetadata, CustomFieldMetadata, GlobalIntentHandlers & CustomIntentHandlers, FormCustomState<GlobalCustomStateHandlers & CustomStateHandlers>>;
13
14
  };
14
15
  useFormMetadata: (options?: {
15
16
  formId?: string;
16
- }) => FormMetadata<BaseErrorShape, CustomFormMetadata, CustomFieldMetadata>;
17
+ }) => FormMetadata<BaseErrorShape, CustomFormMetadata, CustomFieldMetadata, FormCustomState<GlobalCustomStateHandlers>>;
17
18
  useField: <FieldShape = any>(name: FieldName<FieldShape>, options?: {
18
19
  formId?: string;
19
20
  }) => FieldMetadata<FieldShape, BaseErrorShape, CustomFieldMetadata>;
20
- useIntent: <FormShape extends Record<string, any>>(formRef: FormRef) => IntentDispatcher<FormShape>;
21
- config: FormsConfig<BaseErrorShape, BaseSchema, SchemaErrorShape, CustomFormMetadata, CustomFieldMetadata>;
21
+ useIntent: {
22
+ (formRef: FormRef): IntentDispatcher<Record<string, any>, DefaultIntentHandlers & GlobalIntentHandlers>;
23
+ <IntentHandlers extends Record<string, IntentHandler<any, any>>>(formRef: FormRef): IntentDispatcher<Record<string, any>, DefaultIntentHandlers & GlobalIntentHandlers & IntentHandlers>;
24
+ <FormShape extends Record<string, any>>(formRef: FormRef): IntentDispatcher<FormShape, DefaultIntentHandlers & GlobalIntentHandlers>;
25
+ <FormShape extends Record<string, any>, IntentHandlers extends Record<string, IntentHandler<any, any>>>(formRef: FormRef): IntentDispatcher<FormShape, DefaultIntentHandlers & GlobalIntentHandlers & IntentHandlers>;
26
+ };
27
+ resolveSubmission: <IntentHandlers extends Record<string, IntentHandler<any, any>> = {}>(submission: Submission, options?: {
28
+ handlers?: IntentHandlers;
29
+ }) => {
30
+ intent: FormIntent<Record<string, any>, DefaultIntentHandlers & GlobalIntentHandlers & IntentHandlers> | {
31
+ type: "submit";
32
+ payload: undefined;
33
+ } | undefined;
34
+ targetValue: Record<string, FormValue> | undefined;
35
+ };
36
+ config: FormsConfig<BaseErrorShape, BaseSchema, SchemaErrorShape, CustomFormMetadata, CustomFieldMetadata, GlobalIntentHandlers, GlobalCustomStateHandlers>;
22
37
  };
23
38
  /**
24
39
  * Provides form context to child components.
25
40
  * Stacks contexts to support nested forms, with latest context taking priority.
26
41
  */
27
42
  export declare const FormProvider: (props: {
28
- context: FormContext<any>;
43
+ context: FormContext<any, FormCustomState<{}>>;
29
44
  children: React.ReactNode;
30
45
  }) => React.ReactElement;
31
46
  /**
@@ -75,7 +90,7 @@ export declare const FormProvider: (props: {
75
90
  * ```
76
91
  */
77
92
  export declare const useForm: {
78
- <Schema extends StandardSchemaV1<unknown, unknown>, ErrorShape extends any, Value = InferOutput<Schema>>(schema: Schema, options: {
93
+ <Schema extends StandardSchemaV1<unknown, unknown>, ErrorShape extends any, Value = InferOutput<Schema>, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(schema: Schema, options: {
79
94
  id?: string | undefined | undefined;
80
95
  key?: string | undefined | undefined;
81
96
  defaultValue?: import("./types").DefaultValue<InferFormShape<Schema>> | undefined;
@@ -87,11 +102,13 @@ export declare const useForm: {
87
102
  shouldRevalidate?: "onSubmit" | "onBlur" | "onInput" | undefined | undefined;
88
103
  onSubmit?: import("./types").SubmitHandler<InferFormShape<Schema>, NoInfer<ErrorShape>, NoInfer<Value>> | undefined;
89
104
  onError?: import("./types").ErrorHandler<NoInfer<ErrorShape>> | undefined;
105
+ customState?: { [Key in keyof CustomStateHandlers]: CustomStateHandlers[Key] extends CustomStateHandler<any, infer RequiredIntents extends Record<string, IntentHandler<any, any>>, any> ? Exclude<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)> extends never ? RequiredIntents extends Pick<{} & CustomIntentHandlers, Extract<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)>> ? CustomStateHandlers[Key] : never : never : never; } | undefined;
90
106
  serialize?: import("@conform-to/dom/future").CustomSerialize | undefined;
107
+ intents?: CustomIntentHandlers | undefined;
91
108
  schemaOptions?: import("./types").InferOptions<Schema> | undefined;
92
- onValidate: import("./types").ValidateHandler<ErrorShape, Value, InferOutput<Schema>, string[]>;
93
- }): FormHandle<InferFormShape<Schema>, ErrorShape, {}, {}>;
94
- <Schema extends StandardSchemaV1<unknown, unknown>, ErrorShape extends any = string[], Value = InferOutput<Schema>>(schema: Schema, options: {
109
+ onValidate: import("./types").ValidateHandler<ErrorShape, Value, InferOutput<Schema>, string[], FormIntent<InferFormShape<Schema>, DefaultIntentHandlers & CustomIntentHandlers> | undefined>;
110
+ }): FormHandle<InferFormShape<Schema>, ErrorShape, {}, {}, {} & CustomIntentHandlers, FormCustomState<{} & CustomStateHandlers>>;
111
+ <Schema extends StandardSchemaV1<unknown, unknown>, ErrorShape extends any = string[], Value = InferOutput<Schema>, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(schema: Schema, options: {
95
112
  id?: string | undefined | undefined;
96
113
  key?: string | undefined | undefined;
97
114
  defaultValue?: import("./types").DefaultValue<InferFormShape<Schema>> | undefined;
@@ -101,13 +118,15 @@ export declare const useForm: {
101
118
  lastResult?: import("@conform-to/dom/future").SubmissionResult<ErrorShape> | null | undefined;
102
119
  shouldValidate?: "onSubmit" | "onBlur" | "onInput" | undefined | undefined;
103
120
  shouldRevalidate?: "onSubmit" | "onBlur" | "onInput" | undefined | undefined;
104
- onValidate?: import("./types").ValidateHandler<ErrorShape, Value, InferOutput<Schema>, string[]> | undefined;
121
+ onValidate?: import("./types").ValidateHandler<ErrorShape, Value, InferOutput<Schema>, string[], FormIntent<InferFormShape<Schema>, DefaultIntentHandlers & CustomIntentHandlers> | undefined> | undefined;
105
122
  onSubmit?: import("./types").SubmitHandler<InferFormShape<Schema>, NoInfer<ErrorShape>, NoInfer<Value>> | undefined;
106
123
  onError?: import("./types").ErrorHandler<NoInfer<ErrorShape>> | undefined;
124
+ customState?: { [Key in keyof CustomStateHandlers]: CustomStateHandlers[Key] extends CustomStateHandler<any, infer RequiredIntents extends Record<string, IntentHandler<any, any>>, any> ? Exclude<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)> extends never ? RequiredIntents extends Pick<{} & CustomIntentHandlers, Extract<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)>> ? CustomStateHandlers[Key] : never : never : never; } | undefined;
107
125
  serialize?: import("@conform-to/dom/future").CustomSerialize | undefined;
126
+ intents?: CustomIntentHandlers | undefined;
108
127
  schemaOptions?: import("./types").InferOptions<Schema> | undefined;
109
- }): FormHandle<InferFormShape<Schema>, ErrorShape, {}, {}>;
110
- <FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends any = any, Value = undefined>(options: {
128
+ }): FormHandle<InferFormShape<Schema>, ErrorShape, {}, {}, {} & CustomIntentHandlers, FormCustomState<{} & CustomStateHandlers>>;
129
+ <FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends any = any, Value = undefined, CustomIntentHandlers extends Record<string, IntentHandler<any, any>> = {}, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(options: {
111
130
  id?: string | undefined | undefined;
112
131
  key?: string | undefined | undefined;
113
132
  defaultValue?: import("./types").DefaultValue<FormShape> | undefined;
@@ -119,10 +138,12 @@ export declare const useForm: {
119
138
  shouldRevalidate?: "onSubmit" | "onBlur" | "onInput" | undefined | undefined;
120
139
  onSubmit?: import("./types").SubmitHandler<FormShape, NoInfer<ErrorShape>, NoInfer<Value>> | undefined;
121
140
  onError?: import("./types").ErrorHandler<NoInfer<ErrorShape>> | undefined;
141
+ customState?: { [Key in keyof CustomStateHandlers]: CustomStateHandlers[Key] extends CustomStateHandler<any, infer RequiredIntents extends Record<string, IntentHandler<any, any>>, any> ? Exclude<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)> extends never ? RequiredIntents extends Pick<{} & CustomIntentHandlers, Extract<keyof RequiredIntents, keyof ({} & CustomIntentHandlers)>> ? CustomStateHandlers[Key] : never : never : never; } | undefined;
122
142
  serialize?: import("@conform-to/dom/future").CustomSerialize | undefined;
143
+ intents?: CustomIntentHandlers | undefined;
123
144
  schemaOptions?: undefined;
124
- onValidate: import("./types").ValidateHandler<ErrorShape, Value, undefined, string[]>;
125
- }): FormHandle<FormShape, ErrorShape, {}, {}>;
145
+ onValidate: import("./types").ValidateHandler<ErrorShape, Value, undefined, string[], FormIntent<FormShape, DefaultIntentHandlers & CustomIntentHandlers> | undefined>;
146
+ }): FormHandle<FormShape, ErrorShape, {}, {}, {} & CustomIntentHandlers, FormCustomState<{} & CustomStateHandlers>>;
126
147
  };
127
148
  /**
128
149
  * A React hook that provides access to form-level metadata and state.
@@ -156,6 +177,7 @@ export declare const useFormMetadata: (options?: {
156
177
  errors: any;
157
178
  fieldErrors: Record<string, any>;
158
179
  defaultValue: Record<string, unknown>;
180
+ customState: FormCustomState<{}>;
159
181
  props: Readonly<{
160
182
  id: string;
161
183
  onSubmit: React.FormEventHandler<HTMLFormElement>;
@@ -163,7 +185,7 @@ export declare const useFormMetadata: (options?: {
163
185
  onInput: React.FormEventHandler<HTMLFormElement>;
164
186
  noValidate: boolean;
165
187
  }>;
166
- context: FormContext<any>;
188
+ context: FormContext<any, FormCustomState<{}>>;
167
189
  getField<FieldShape>(name: import("@conform-to/dom/future").FieldName<FieldShape>): Readonly<{
168
190
  required?: boolean | undefined | undefined;
169
191
  minLength?: number | undefined | undefined;
@@ -1170,5 +1192,24 @@ export declare const useField: <FieldShape = any>(name: FieldName<FieldShape>, o
1170
1192
  * }
1171
1193
  * ```
1172
1194
  */
1173
- export declare const useIntent: <FormShape extends Record<string, any>>(formRef: FormRef) => IntentDispatcher<FormShape>;
1195
+ export declare const useIntent: {
1196
+ (formRef: FormRef): IntentDispatcher<Record<string, any>, DefaultIntentHandlers>;
1197
+ <IntentHandlers extends Record<string, IntentHandler<any, any>>>(formRef: FormRef): IntentDispatcher<Record<string, any>, DefaultIntentHandlers & IntentHandlers>;
1198
+ <FormShape extends Record<string, any>>(formRef: FormRef): IntentDispatcher<FormShape, DefaultIntentHandlers>;
1199
+ <FormShape extends Record<string, any>, IntentHandlers extends Record<string, IntentHandler<any, any>>>(formRef: FormRef): IntentDispatcher<FormShape, DefaultIntentHandlers & IntentHandlers>;
1200
+ };
1201
+ /**
1202
+ * Parses and resolves a submission payload using the default configured form intent handlers.
1203
+ *
1204
+ * See https://conform.guide/api/react/future/resolveSubmission
1205
+ */
1206
+ export declare const resolveSubmission: <IntentHandlers extends Record<string, IntentHandler<any, any>> = {}>(submission: Submission, options?: {
1207
+ handlers?: IntentHandlers | undefined;
1208
+ } | undefined) => {
1209
+ intent: FormIntent<Record<string, any>, DefaultIntentHandlers & IntentHandlers> | {
1210
+ type: "submit";
1211
+ payload: undefined;
1212
+ } | undefined;
1213
+ targetValue: Record<string, FormValue> | undefined;
1214
+ };
1174
1215
  //# sourceMappingURL=forms.d.ts.map
@@ -11,6 +11,7 @@ var dom = require('./dom.js');
11
11
  var hooks = require('./hooks.js');
12
12
  var state = require('./state.js');
13
13
  var util = require('./util.js');
14
+ var intent = require('./intent.js');
14
15
  var jsxRuntime = require('react/jsx-runtime');
15
16
 
16
17
  function configureForms() {
@@ -20,11 +21,14 @@ function configureForms() {
20
21
  * Global serializer that composes the user-provided serializer with the default serializer.
21
22
  */
22
23
  var globalSerialize = util.resolveSerialize(config.serialize, future.defaultSerialize);
24
+ var globalIntentHandlers = intent.mergeIntentHandlers(intent.defaultIntentHandlers, config.intents);
23
25
 
24
26
  /**
25
27
  * Global configuration with defaults applied
26
28
  */
27
29
  var globalConfig = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), {}, {
30
+ intents: config.intents,
31
+ customState: config.customState,
28
32
  intentName: (_config$intentName = config.intentName) !== null && _config$intentName !== void 0 ? _config$intentName : future.DEFAULT_INTENT_NAME,
29
33
  shouldValidate: (_config$shouldValidat = config.shouldValidate) !== null && _config$shouldValidat !== void 0 ? _config$shouldValidat : 'onSubmit',
30
34
  shouldRevalidate: (_ref = (_config$shouldRevalid = config.shouldRevalidate) !== null && _config$shouldRevalid !== void 0 ? _config$shouldRevalid : config.shouldValidate) !== null && _ref !== void 0 ? _ref : 'onSubmit',
@@ -109,6 +113,7 @@ function configureForms() {
109
113
 
110
114
  function useForm(schemaOrOptions, maybeOptions) {
111
115
  var _options$constraint, _globalConfig$getCons, _options$id, _options$onError;
116
+ // implementation signature is broader than the public overloads above
112
117
  var schema;
113
118
  var options;
114
119
  if (globalConfig.isSchema(schemaOrOptions)) {
@@ -120,15 +125,19 @@ function configureForms() {
120
125
  var constraint = (_options$constraint = options.constraint) !== null && _options$constraint !== void 0 ? _options$constraint : schema ? (_globalConfig$getCons = globalConfig.getConstraints) === null || _globalConfig$getCons === void 0 ? void 0 : _globalConfig$getCons.call(globalConfig, schema) : undefined;
121
126
  var optionsRef = hooks.useLatest(options);
122
127
  var serialize = react.useMemo(() => util.resolveSerialize(options.serialize, globalSerialize), [options.serialize]);
128
+ var [customStateHandlers] = react.useState(() => state.mergeCustomStateHandlers(globalConfig.customState, options.customState));
123
129
  var fallbackId = react.useId();
124
130
  var formId = (_options$id = options.id) !== null && _options$id !== void 0 ? _options$id : "form-".concat(fallbackId);
125
131
  var [state$1, handleSubmit] = hooks.useConform(formId, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
132
+ intentHandlers: intent.mergeIntentHandlers(globalIntentHandlers, options.intents),
133
+ customStateHandlers,
126
134
  serialize,
127
135
  intentName: globalConfig.intentName,
128
136
  onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : dom.focusFirstInvalidField,
129
137
  onValidate(ctx) {
130
138
  var _options$onValidate, _options$onValidate2, _options;
131
139
  if (schema) {
140
+ var _validateResult$syncR;
132
141
  var schemaResult = globalConfig.validateSchema(schema, ctx.payload, options.schemaOptions);
133
142
  if (schemaResult instanceof Promise) {
134
143
  return schemaResult.then(resolvedResult => {
@@ -149,18 +158,28 @@ function configureForms() {
149
158
  var schemaValue = schemaResult.value;
150
159
  ctx.schemaValue = schemaValue;
151
160
  var validateResult = util.resolveValidateResult(options.onValidate(ctx));
152
- if (validateResult.syncResult) {
153
- var _validateResult$syncR, _validateResult$syncR2;
154
- (_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = schemaValue;
161
+ if (validateResult.syncResult && typeof schemaValue !== 'undefined' && typeof validateResult.syncResult.value === 'undefined') {
162
+ validateResult.syncResult = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, validateResult.syncResult), {}, {
163
+ value: schemaValue
164
+ });
155
165
  }
156
166
  if (validateResult.asyncResult) {
157
167
  validateResult.asyncResult = validateResult.asyncResult.then(result => {
158
- var _result$value;
159
- (_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = schemaValue;
168
+ if (typeof schemaValue !== 'undefined' && typeof result.value === 'undefined') {
169
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
170
+ value: schemaValue
171
+ });
172
+ }
160
173
  return result;
161
174
  });
162
175
  }
163
- return [validateResult.syncResult, validateResult.asyncResult];
176
+ if (validateResult.syncResult && validateResult.asyncResult) {
177
+ return {
178
+ result: validateResult.syncResult,
179
+ pending: validateResult.asyncResult
180
+ };
181
+ }
182
+ return (_validateResult$syncR = validateResult.syncResult) !== null && _validateResult$syncR !== void 0 ? _validateResult$syncR : validateResult.asyncResult;
164
183
  }
165
184
  return (_options$onValidate = (_options$onValidate2 = (_options = options).onValidate) === null || _options$onValidate2 === void 0 ? void 0 : _options$onValidate2.call(_options, ctx)) !== null && _options$onValidate !== void 0 ? _options$onValidate : {
166
185
  // To avoid conform falling back to server validation,
@@ -170,7 +189,7 @@ function configureForms() {
170
189
  };
171
190
  }
172
191
  }));
173
- var intent = useIntent(formId);
192
+ var intent$1 = useIntent(formId);
174
193
  var context = react.useMemo(() => ({
175
194
  formId,
176
195
  state: state$1,
@@ -192,7 +211,7 @@ function configureForms() {
192
211
  var shouldValidate = (_optionsRef$current$s = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s !== void 0 ? _optionsRef$current$s : globalConfig.shouldValidate;
193
212
  var shouldRevalidate = (_ref2 = (_optionsRef$current$s2 = optionsRef.current.shouldRevalidate) !== null && _optionsRef$current$s2 !== void 0 ? _optionsRef$current$s2 : optionsRef.current.shouldValidate) !== null && _ref2 !== void 0 ? _ref2 : globalConfig.shouldRevalidate;
194
213
  if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
195
- intent.validate(event.target.name);
214
+ intent$1.validate(event.target.name);
196
215
  }
197
216
  },
198
217
  handleBlur(event) {
@@ -210,10 +229,10 @@ function configureForms() {
210
229
  var shouldValidate = (_optionsRef$current$s3 = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s3 !== void 0 ? _optionsRef$current$s3 : globalConfig.shouldValidate;
211
230
  var shouldRevalidate = (_ref3 = (_optionsRef$current$s4 = optionsRef.current.shouldRevalidate) !== null && _optionsRef$current$s4 !== void 0 ? _optionsRef$current$s4 : optionsRef.current.shouldValidate) !== null && _ref3 !== void 0 ? _ref3 : globalConfig.shouldRevalidate;
212
231
  if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
213
- intent.validate(event.target.name);
232
+ intent$1.validate(event.target.name);
214
233
  }
215
234
  }
216
- }), [formId, state$1, serialize, constraint, handleSubmit, intent, optionsRef]);
235
+ }), [formId, state$1, serialize, constraint, handleSubmit, intent$1, optionsRef]);
217
236
  var form = react.useMemo(() => state.getFormMetadata(context, {
218
237
  extendFormMetadata: globalConfig.extendFormMetadata,
219
238
  extendFieldMetadata: globalConfig.extendFieldMetadata
@@ -224,7 +243,7 @@ function configureForms() {
224
243
  return {
225
244
  form,
226
245
  fields,
227
- intent
246
+ intent: intent$1
228
247
  };
229
248
  }
230
249
 
@@ -309,15 +328,36 @@ function configureForms() {
309
328
  * }
310
329
  * ```
311
330
  */
331
+
312
332
  function useIntent(formRef) {
313
333
  return react.useMemo(() => dom.createIntentDispatcher(() => dom.getFormElement(formRef), globalConfig.intentName), [formRef]);
314
334
  }
335
+
336
+ /**
337
+ * Parses and resolves a submission payload using the configured default and global intent handlers.
338
+ * Pass `handlers` to extend or override them for a specific call.
339
+ */
340
+ function resolveSubmission(submission, options) {
341
+ var handlers = intent.mergeIntentHandlers(globalIntentHandlers, options === null || options === void 0 ? void 0 : options.handlers);
342
+ var intent$1 = intent.parseIntent(submission.intent, {
343
+ handlers
344
+ });
345
+ var targetValue = intent.resolveIntent(submission, {
346
+ handlers,
347
+ intent: intent$1
348
+ });
349
+ return {
350
+ intent: intent$1,
351
+ targetValue
352
+ };
353
+ }
315
354
  return {
316
355
  FormProvider,
317
356
  useForm,
318
357
  useFormMetadata,
319
358
  useField,
320
359
  useIntent,
360
+ resolveSubmission,
321
361
  config: globalConfig
322
362
  };
323
363
  }
@@ -444,8 +484,16 @@ var useField = defaultForms.useField;
444
484
  */
445
485
  var useIntent = defaultForms.useIntent;
446
486
 
487
+ /**
488
+ * Parses and resolves a submission payload using the default configured form intent handlers.
489
+ *
490
+ * See https://conform.guide/api/react/future/resolveSubmission
491
+ */
492
+ var resolveSubmission = defaultForms.resolveSubmission;
493
+
447
494
  exports.FormProvider = FormProvider;
448
495
  exports.configureForms = configureForms;
496
+ exports.resolveSubmission = resolveSubmission;
449
497
  exports.useField = useField;
450
498
  exports.useForm = useForm;
451
499
  exports.useFormMetadata = useFormMetadata;
@@ -2,11 +2,12 @@
2
2
  import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
3
3
  import { isFieldElement } from '@conform-to/dom';
4
4
  import { DEFAULT_INTENT_NAME, defaultSerialize } from '@conform-to/dom/future';
5
- import { useContext, useMemo, useId, createContext } from 'react';
5
+ import { useContext, useMemo, useState, useId, createContext } from 'react';
6
6
  import { focusFirstInvalidField, getFormElement, createIntentDispatcher } from './dom.mjs';
7
7
  import { useLatest, useConform } from './hooks.mjs';
8
- import { isTouched, getFormMetadata, getFieldset, getField } from './state.mjs';
8
+ import { mergeCustomStateHandlers, isTouched, getFormMetadata, getFieldset, getField } from './state.mjs';
9
9
  import { resolveSerialize, isStandardSchemaV1, validateStandardSchemaV1, resolveValidateResult } from './util.mjs';
10
+ import { mergeIntentHandlers, defaultIntentHandlers, parseIntent, resolveIntent } from './intent.mjs';
10
11
  import { jsx } from 'react/jsx-runtime';
11
12
 
12
13
  function configureForms() {
@@ -16,11 +17,14 @@ function configureForms() {
16
17
  * Global serializer that composes the user-provided serializer with the default serializer.
17
18
  */
18
19
  var globalSerialize = resolveSerialize(config.serialize, defaultSerialize);
20
+ var globalIntentHandlers = mergeIntentHandlers(defaultIntentHandlers, config.intents);
19
21
 
20
22
  /**
21
23
  * Global configuration with defaults applied
22
24
  */
23
25
  var globalConfig = _objectSpread2(_objectSpread2({}, config), {}, {
26
+ intents: config.intents,
27
+ customState: config.customState,
24
28
  intentName: (_config$intentName = config.intentName) !== null && _config$intentName !== void 0 ? _config$intentName : DEFAULT_INTENT_NAME,
25
29
  shouldValidate: (_config$shouldValidat = config.shouldValidate) !== null && _config$shouldValidat !== void 0 ? _config$shouldValidat : 'onSubmit',
26
30
  shouldRevalidate: (_ref = (_config$shouldRevalid = config.shouldRevalidate) !== null && _config$shouldRevalid !== void 0 ? _config$shouldRevalid : config.shouldValidate) !== null && _ref !== void 0 ? _ref : 'onSubmit',
@@ -105,6 +109,7 @@ function configureForms() {
105
109
 
106
110
  function useForm(schemaOrOptions, maybeOptions) {
107
111
  var _options$constraint, _globalConfig$getCons, _options$id, _options$onError;
112
+ // implementation signature is broader than the public overloads above
108
113
  var schema;
109
114
  var options;
110
115
  if (globalConfig.isSchema(schemaOrOptions)) {
@@ -116,15 +121,19 @@ function configureForms() {
116
121
  var constraint = (_options$constraint = options.constraint) !== null && _options$constraint !== void 0 ? _options$constraint : schema ? (_globalConfig$getCons = globalConfig.getConstraints) === null || _globalConfig$getCons === void 0 ? void 0 : _globalConfig$getCons.call(globalConfig, schema) : undefined;
117
122
  var optionsRef = useLatest(options);
118
123
  var serialize = useMemo(() => resolveSerialize(options.serialize, globalSerialize), [options.serialize]);
124
+ var [customStateHandlers] = useState(() => mergeCustomStateHandlers(globalConfig.customState, options.customState));
119
125
  var fallbackId = useId();
120
126
  var formId = (_options$id = options.id) !== null && _options$id !== void 0 ? _options$id : "form-".concat(fallbackId);
121
127
  var [state, handleSubmit] = useConform(formId, _objectSpread2(_objectSpread2({}, options), {}, {
128
+ intentHandlers: mergeIntentHandlers(globalIntentHandlers, options.intents),
129
+ customStateHandlers,
122
130
  serialize,
123
131
  intentName: globalConfig.intentName,
124
132
  onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : focusFirstInvalidField,
125
133
  onValidate(ctx) {
126
134
  var _options$onValidate, _options$onValidate2, _options;
127
135
  if (schema) {
136
+ var _validateResult$syncR;
128
137
  var schemaResult = globalConfig.validateSchema(schema, ctx.payload, options.schemaOptions);
129
138
  if (schemaResult instanceof Promise) {
130
139
  return schemaResult.then(resolvedResult => {
@@ -145,18 +154,28 @@ function configureForms() {
145
154
  var schemaValue = schemaResult.value;
146
155
  ctx.schemaValue = schemaValue;
147
156
  var validateResult = resolveValidateResult(options.onValidate(ctx));
148
- if (validateResult.syncResult) {
149
- var _validateResult$syncR, _validateResult$syncR2;
150
- (_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = schemaValue;
157
+ if (validateResult.syncResult && typeof schemaValue !== 'undefined' && typeof validateResult.syncResult.value === 'undefined') {
158
+ validateResult.syncResult = _objectSpread2(_objectSpread2({}, validateResult.syncResult), {}, {
159
+ value: schemaValue
160
+ });
151
161
  }
152
162
  if (validateResult.asyncResult) {
153
163
  validateResult.asyncResult = validateResult.asyncResult.then(result => {
154
- var _result$value;
155
- (_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = schemaValue;
164
+ if (typeof schemaValue !== 'undefined' && typeof result.value === 'undefined') {
165
+ return _objectSpread2(_objectSpread2({}, result), {}, {
166
+ value: schemaValue
167
+ });
168
+ }
156
169
  return result;
157
170
  });
158
171
  }
159
- return [validateResult.syncResult, validateResult.asyncResult];
172
+ if (validateResult.syncResult && validateResult.asyncResult) {
173
+ return {
174
+ result: validateResult.syncResult,
175
+ pending: validateResult.asyncResult
176
+ };
177
+ }
178
+ return (_validateResult$syncR = validateResult.syncResult) !== null && _validateResult$syncR !== void 0 ? _validateResult$syncR : validateResult.asyncResult;
160
179
  }
161
180
  return (_options$onValidate = (_options$onValidate2 = (_options = options).onValidate) === null || _options$onValidate2 === void 0 ? void 0 : _options$onValidate2.call(_options, ctx)) !== null && _options$onValidate !== void 0 ? _options$onValidate : {
162
181
  // To avoid conform falling back to server validation,
@@ -305,15 +324,36 @@ function configureForms() {
305
324
  * }
306
325
  * ```
307
326
  */
327
+
308
328
  function useIntent(formRef) {
309
329
  return useMemo(() => createIntentDispatcher(() => getFormElement(formRef), globalConfig.intentName), [formRef]);
310
330
  }
331
+
332
+ /**
333
+ * Parses and resolves a submission payload using the configured default and global intent handlers.
334
+ * Pass `handlers` to extend or override them for a specific call.
335
+ */
336
+ function resolveSubmission(submission, options) {
337
+ var handlers = mergeIntentHandlers(globalIntentHandlers, options === null || options === void 0 ? void 0 : options.handlers);
338
+ var intent = parseIntent(submission.intent, {
339
+ handlers
340
+ });
341
+ var targetValue = resolveIntent(submission, {
342
+ handlers,
343
+ intent
344
+ });
345
+ return {
346
+ intent,
347
+ targetValue
348
+ };
349
+ }
311
350
  return {
312
351
  FormProvider,
313
352
  useForm,
314
353
  useFormMetadata,
315
354
  useField,
316
355
  useIntent,
356
+ resolveSubmission,
317
357
  config: globalConfig
318
358
  };
319
359
  }
@@ -440,4 +480,11 @@ var useField = defaultForms.useField;
440
480
  */
441
481
  var useIntent = defaultForms.useIntent;
442
482
 
443
- export { FormProvider, configureForms, useField, useForm, useFormMetadata, useIntent };
483
+ /**
484
+ * Parses and resolves a submission payload using the default configured form intent handlers.
485
+ *
486
+ * See https://conform.guide/api/react/future/resolveSubmission
487
+ */
488
+ var resolveSubmission = defaultForms.resolveSubmission;
489
+
490
+ export { FormProvider, configureForms, resolveSubmission, useField, useForm, useFormMetadata, useIntent };