@conform-to/react 1.9.1 → 1.10.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.9.1 / License MIT / Copyright (c) 2025 Edmund Hung
10
+ Version 1.10.0 / License MIT / Copyright (c) 2025 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
 
@@ -1,6 +1,6 @@
1
1
  import { type Serialize, type SubmissionResult, serialize } from '@conform-to/dom/future';
2
2
  import { useEffect } from 'react';
3
- import type { FormContext, DefaultFieldMetadata, IntentDispatcher, FormMetadata, Fieldset, FormOptions, FieldName, Field, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef } from './types';
3
+ import type { FormContext, DefaultMetadata, IntentDispatcher, FormMetadata, Fieldset, FormOptions, FieldName, FieldMetadata, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef } from './types';
4
4
  export declare const FormConfig: import("react").Context<{
5
5
  intentName: string;
6
6
  observer: {
@@ -66,7 +66,7 @@ export declare function useConform<ErrorShape, Value = undefined>(formRef: FormR
66
66
  */
67
67
  export declare function useForm<FormShape extends Record<string, any> = Record<string, any>, ErrorShape = string, Value = undefined>(options: FormOptions<FormShape, ErrorShape, Value>): {
68
68
  form: FormMetadata<ErrorShape>;
69
- fields: Fieldset<FormShape, DefaultFieldMetadata<ErrorShape>>;
69
+ fields: Fieldset<FormShape, DefaultMetadata<ErrorShape>>;
70
70
  intent: IntentDispatcher;
71
71
  };
72
72
  /**
@@ -79,7 +79,7 @@ export declare function useForm<FormShape extends Record<string, any> = Record<s
79
79
  * function ErrorSummary() {
80
80
  * const form = useFormMetadata();
81
81
  *
82
- * if (!form.invalid) return null;
82
+ * if (form.valid) return null;
83
83
  *
84
84
  * return (
85
85
  * <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
@@ -112,7 +112,7 @@ export declare function useFormMetadata<ErrorShape = string[]>(options?: {
112
112
  */
113
113
  export declare function useField<FieldShape = any>(name: FieldName<FieldShape>, options?: {
114
114
  formId?: string;
115
- }): Field<FieldShape>;
115
+ }): FieldMetadata<FieldShape>;
116
116
  /**
117
117
  * A React hook that provides an intent dispatcher for programmatic form actions.
118
118
  * Intent dispatchers allow you to trigger form operations like validation, field updates,
@@ -127,7 +127,7 @@ function useConform(formRef, options) {
127
127
  }
128
128
  }, [formRef, state$1.resetKey]);
129
129
  react.useEffect(() => {
130
- if (!state$1.intendedValue) {
130
+ if (!state$1.clientIntendedValue) {
131
131
  return;
132
132
  }
133
133
  var formElement = dom.getFormElement(formRef);
@@ -136,9 +136,9 @@ function useConform(formRef, options) {
136
136
  console.error('Failed to update form value; No form element found');
137
137
  return;
138
138
  }
139
- dom.updateFormValue(formElement, state$1.intendedValue, optionsRef.current.serialize);
139
+ dom.updateFormValue(formElement, state$1.clientIntendedValue, optionsRef.current.serialize);
140
140
  lastIntentedValueRef.current = undefined;
141
- }, [formRef, state$1.intendedValue, optionsRef]);
141
+ }, [formRef, state$1.clientIntendedValue, optionsRef]);
142
142
  var handleSubmit = react.useCallback(event => {
143
143
  var _abortControllerRef$c2, _lastAsyncResultRef$c;
144
144
  var abortController = new AbortController();
@@ -172,9 +172,8 @@ function useConform(formRef, options) {
172
172
  }
173
173
 
174
174
  // Override submission value if the last intended value is not applied yet (i.e. batch updates)
175
- if (typeof lastIntentedValueRef.current !== 'undefined') {
176
- var _lastIntentedValueRef;
177
- submission.payload = (_lastIntentedValueRef = lastIntentedValueRef.current) !== null && _lastIntentedValueRef !== void 0 ? _lastIntentedValueRef : {};
175
+ if (lastIntentedValueRef.current != null) {
176
+ submission.payload = lastIntentedValueRef.current;
178
177
  }
179
178
  var intendedValue = intent.applyIntent(submission);
180
179
 
@@ -432,7 +431,7 @@ function useForm(options) {
432
431
  * function ErrorSummary() {
433
432
  * const form = useFormMetadata();
434
433
  *
435
- * if (!form.invalid) return null;
434
+ * if (form.valid) return null;
436
435
  *
437
436
  * return (
438
437
  * <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
@@ -123,7 +123,7 @@ function useConform(formRef, options) {
123
123
  }
124
124
  }, [formRef, state.resetKey]);
125
125
  useEffect(() => {
126
- if (!state.intendedValue) {
126
+ if (!state.clientIntendedValue) {
127
127
  return;
128
128
  }
129
129
  var formElement = getFormElement(formRef);
@@ -132,9 +132,9 @@ function useConform(formRef, options) {
132
132
  console.error('Failed to update form value; No form element found');
133
133
  return;
134
134
  }
135
- updateFormValue(formElement, state.intendedValue, optionsRef.current.serialize);
135
+ updateFormValue(formElement, state.clientIntendedValue, optionsRef.current.serialize);
136
136
  lastIntentedValueRef.current = undefined;
137
- }, [formRef, state.intendedValue, optionsRef]);
137
+ }, [formRef, state.clientIntendedValue, optionsRef]);
138
138
  var handleSubmit = useCallback(event => {
139
139
  var _abortControllerRef$c2, _lastAsyncResultRef$c;
140
140
  var abortController = new AbortController();
@@ -168,9 +168,8 @@ function useConform(formRef, options) {
168
168
  }
169
169
 
170
170
  // Override submission value if the last intended value is not applied yet (i.e. batch updates)
171
- if (typeof lastIntentedValueRef.current !== 'undefined') {
172
- var _lastIntentedValueRef;
173
- submission.payload = (_lastIntentedValueRef = lastIntentedValueRef.current) !== null && _lastIntentedValueRef !== void 0 ? _lastIntentedValueRef : {};
171
+ if (lastIntentedValueRef.current != null) {
172
+ submission.payload = lastIntentedValueRef.current;
174
173
  }
175
174
  var intendedValue = applyIntent(submission);
176
175
 
@@ -428,7 +427,7 @@ function useForm(options) {
428
427
  * function ErrorSummary() {
429
428
  * const form = useFormMetadata();
430
429
  *
431
- * if (!form.invalid) return null;
430
+ * if (form.valid) return null;
432
431
  *
433
432
  * return (
434
433
  * <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
@@ -1,5 +1,6 @@
1
- export type { FormValue, FormError } from '@conform-to/dom/future';
1
+ export type { FormError, FormValue, Submission, SubmissionResult, } from '@conform-to/dom/future';
2
2
  export { parseSubmission, report, isDirty } from '@conform-to/dom/future';
3
- export type { Control, FormOptions, Fieldset, DefaultValue } from './types';
3
+ export type { Control, DefaultValue, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, FieldName, Fieldset, IntentDispatcher, } from './types';
4
4
  export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent, } from './hooks';
5
+ export { memoize } from './memoize';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var future = require('@conform-to/dom/future');
6
6
  var hooks = require('./hooks.js');
7
+ var memoize = require('./memoize.js');
7
8
 
8
9
 
9
10
 
@@ -26,3 +27,4 @@ exports.useForm = hooks.useForm;
26
27
  exports.useFormData = hooks.useFormData;
27
28
  exports.useFormMetadata = hooks.useFormMetadata;
28
29
  exports.useIntent = hooks.useIntent;
30
+ exports.memoize = memoize.memoize;
@@ -1,2 +1,3 @@
1
1
  export { isDirty, parseSubmission, report } from '@conform-to/dom/future';
2
2
  export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent } from './hooks.mjs';
3
+ export { memoize } from './memoize.mjs';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A memoized function with cache clearing capability.
3
+ */
4
+ export type Memoized<T extends (...args: any) => any> = {
5
+ (this: ThisParameterType<T>, ...args: Parameters<T>): ReturnType<T>;
6
+ /** Clears the memoization cache */
7
+ clearCache: () => void;
8
+ };
9
+ /**
10
+ * Default equality check that compares arguments using Object.is().
11
+ *
12
+ * @param prevArgs - Previous function arguments
13
+ * @param nextArgs - Current function arguments
14
+ * @returns True if all arguments are equal
15
+ */
16
+ export declare function defaultEqualityCheck(prevArgs: any[], nextArgs: any[]): boolean;
17
+ /**
18
+ * Memoizes function calls, caching only the most recent result to prevent redundant async validations.
19
+ *
20
+ * Built-in implementation based on memoize-one with enhanced async support.
21
+ * Can be replaced with other memoization libraries if needed.
22
+ *
23
+ * @param fn - The function to memoize
24
+ * @param isEqual - Custom equality function to compare arguments (defaults to shallow comparison)
25
+ * @returns Memoized function with cache clearing capability
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * // Async validation with API call
30
+ * const validateUsername = useMemo(
31
+ * () => memoize(async function isUnique(username: string) {
32
+ * const response = await fetch(`/api/users/${username}`);
33
+ * return response.ok ? null : ['Username is already taken'];
34
+ * }),
35
+ * []
36
+ * );
37
+ *
38
+ * // Usage in form validation
39
+ * async onValidate({ payload, error }) {
40
+ * if (payload.username && !error.fieldErrors.username) {
41
+ * const messages = await validateUsername(value.username);
42
+ * if (messages) error.fieldErrors.username = messages;
43
+ * }
44
+ * return error;
45
+ * }
46
+ * ```
47
+ */
48
+ export declare function memoize<T extends (...args: any) => any>(fn: T, isEqual?: (prevArgs: Parameters<T>, nextArgs: Parameters<T>) => boolean): Memoized<T>;
49
+ //# sourceMappingURL=memoize.d.ts.map
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * A memoized function with cache clearing capability.
7
+ */
8
+
9
+ /**
10
+ * Default equality check that compares arguments using Object.is().
11
+ *
12
+ * @param prevArgs - Previous function arguments
13
+ * @param nextArgs - Current function arguments
14
+ * @returns True if all arguments are equal
15
+ */
16
+ function defaultEqualityCheck(prevArgs, nextArgs) {
17
+ if (prevArgs.length !== nextArgs.length) {
18
+ return false;
19
+ }
20
+ for (var i = 0; i < prevArgs.length; i++) {
21
+ if (!Object.is(prevArgs[i], nextArgs[i])) {
22
+ return false;
23
+ }
24
+ }
25
+ return true;
26
+ }
27
+
28
+ /**
29
+ * Memoizes function calls, caching only the most recent result to prevent redundant async validations.
30
+ *
31
+ * Built-in implementation based on memoize-one with enhanced async support.
32
+ * Can be replaced with other memoization libraries if needed.
33
+ *
34
+ * @param fn - The function to memoize
35
+ * @param isEqual - Custom equality function to compare arguments (defaults to shallow comparison)
36
+ * @returns Memoized function with cache clearing capability
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Async validation with API call
41
+ * const validateUsername = useMemo(
42
+ * () => memoize(async function isUnique(username: string) {
43
+ * const response = await fetch(`/api/users/${username}`);
44
+ * return response.ok ? null : ['Username is already taken'];
45
+ * }),
46
+ * []
47
+ * );
48
+ *
49
+ * // Usage in form validation
50
+ * async onValidate({ payload, error }) {
51
+ * if (payload.username && !error.fieldErrors.username) {
52
+ * const messages = await validateUsername(value.username);
53
+ * if (messages) error.fieldErrors.username = messages;
54
+ * }
55
+ * return error;
56
+ * }
57
+ * ```
58
+ */
59
+ function memoize(fn) {
60
+ var isEqual = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultEqualityCheck;
61
+ var cache = null;
62
+ function memoized() {
63
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
64
+ args[_key] = arguments[_key];
65
+ }
66
+ // Check if new arguments match last arguments including the context (this)
67
+ if (cache && cache.this === this && isEqual(cache.args, args)) {
68
+ return cache.result;
69
+ }
70
+ var result = fn.apply(this, args);
71
+ if (result instanceof Promise) {
72
+ result = result.catch(e => {
73
+ // If the promise is rejected, clear the cache so that the next call will re-invoke fn
74
+ cache = null;
75
+
76
+ // Re-throw the exception so that it can be handled by the caller
77
+ throw e;
78
+ });
79
+ }
80
+
81
+ // Update the cache
82
+ cache = {
83
+ this: this,
84
+ args,
85
+ result
86
+ };
87
+ return result;
88
+ }
89
+ memoized.clearCache = function clearCache() {
90
+ cache = null;
91
+ };
92
+ return memoized;
93
+ }
94
+
95
+ exports.defaultEqualityCheck = defaultEqualityCheck;
96
+ exports.memoize = memoize;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * A memoized function with cache clearing capability.
3
+ */
4
+
5
+ /**
6
+ * Default equality check that compares arguments using Object.is().
7
+ *
8
+ * @param prevArgs - Previous function arguments
9
+ * @param nextArgs - Current function arguments
10
+ * @returns True if all arguments are equal
11
+ */
12
+ function defaultEqualityCheck(prevArgs, nextArgs) {
13
+ if (prevArgs.length !== nextArgs.length) {
14
+ return false;
15
+ }
16
+ for (var i = 0; i < prevArgs.length; i++) {
17
+ if (!Object.is(prevArgs[i], nextArgs[i])) {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+
24
+ /**
25
+ * Memoizes function calls, caching only the most recent result to prevent redundant async validations.
26
+ *
27
+ * Built-in implementation based on memoize-one with enhanced async support.
28
+ * Can be replaced with other memoization libraries if needed.
29
+ *
30
+ * @param fn - The function to memoize
31
+ * @param isEqual - Custom equality function to compare arguments (defaults to shallow comparison)
32
+ * @returns Memoized function with cache clearing capability
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // Async validation with API call
37
+ * const validateUsername = useMemo(
38
+ * () => memoize(async function isUnique(username: string) {
39
+ * const response = await fetch(`/api/users/${username}`);
40
+ * return response.ok ? null : ['Username is already taken'];
41
+ * }),
42
+ * []
43
+ * );
44
+ *
45
+ * // Usage in form validation
46
+ * async onValidate({ payload, error }) {
47
+ * if (payload.username && !error.fieldErrors.username) {
48
+ * const messages = await validateUsername(value.username);
49
+ * if (messages) error.fieldErrors.username = messages;
50
+ * }
51
+ * return error;
52
+ * }
53
+ * ```
54
+ */
55
+ function memoize(fn) {
56
+ var isEqual = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultEqualityCheck;
57
+ var cache = null;
58
+ function memoized() {
59
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
60
+ args[_key] = arguments[_key];
61
+ }
62
+ // Check if new arguments match last arguments including the context (this)
63
+ if (cache && cache.this === this && isEqual(cache.args, args)) {
64
+ return cache.result;
65
+ }
66
+ var result = fn.apply(this, args);
67
+ if (result instanceof Promise) {
68
+ result = result.catch(e => {
69
+ // If the promise is rejected, clear the cache so that the next call will re-invoke fn
70
+ cache = null;
71
+
72
+ // Re-throw the exception so that it can be handled by the caller
73
+ throw e;
74
+ });
75
+ }
76
+
77
+ // Update the cache
78
+ cache = {
79
+ this: this,
80
+ args,
81
+ result
82
+ };
83
+ return result;
84
+ }
85
+ memoized.clearCache = function clearCache() {
86
+ cache = null;
87
+ };
88
+ return memoized;
89
+ }
90
+
91
+ export { defaultEqualityCheck, memoize };
@@ -0,0 +1,56 @@
1
+ /** The Standard Schema interface. */
2
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
3
+ /** The Standard Schema properties. */
4
+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
5
+ }
6
+ export declare namespace StandardSchemaV1 {
7
+ /** The Standard Schema properties interface. */
8
+ interface Props<Input = unknown, Output = Input> {
9
+ /** The version number of the standard. */
10
+ readonly version: 1;
11
+ /** The vendor name of the schema library. */
12
+ readonly vendor: string;
13
+ /** Validates unknown input values. */
14
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
15
+ /** Inferred types associated with the schema. */
16
+ readonly types?: Types<Input, Output> | undefined;
17
+ }
18
+ /** The result interface of the validate function. */
19
+ type Result<Output> = SuccessResult<Output> | FailureResult;
20
+ /** The result interface if validation succeeds. */
21
+ interface SuccessResult<Output> {
22
+ /** The typed output value. */
23
+ readonly value: Output;
24
+ /** The non-existent issues. */
25
+ readonly issues?: undefined;
26
+ }
27
+ /** The result interface if validation fails. */
28
+ interface FailureResult {
29
+ /** The issues of failed validation. */
30
+ readonly issues: ReadonlyArray<Issue>;
31
+ }
32
+ /** The issue interface of the failure output. */
33
+ interface Issue {
34
+ /** The error message of the issue. */
35
+ readonly message: string;
36
+ /** The path of the issue, if any. */
37
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
38
+ }
39
+ /** The path segment interface of the issue. */
40
+ interface PathSegment {
41
+ /** The key representing a path segment. */
42
+ readonly key: PropertyKey;
43
+ }
44
+ /** The Standard Schema types interface. */
45
+ interface Types<Input = unknown, Output = Input> {
46
+ /** The input type of the schema. */
47
+ readonly input: Input;
48
+ /** The output type of the schema. */
49
+ readonly output: Output;
50
+ }
51
+ /** Infers the input type of a Standard Schema. */
52
+ type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
53
+ /** Infers the output type of a Standard Schema. */
54
+ type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
55
+ }
56
+ //# sourceMappingURL=standard-schema.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { type ValidationAttributes, type Serialize } from '@conform-to/dom/future';
2
- import type { DefaultFieldMetadata, Field, FieldName, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler } from './types';
2
+ import type { DefaultMetadata, FieldMetadata, FieldName, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler } from './types';
3
3
  export declare function initializeState<ErrorShape>(): FormState<ErrorShape>;
4
4
  /**
5
5
  * Updates form state based on action type:
@@ -25,6 +25,7 @@ export declare function getDefaultListKey(prefix: string, initialValue: Record<s
25
25
  export declare function getListKey(context: FormContext<any>, name: string): string[];
26
26
  export declare function getErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): ErrorShape[] | undefined;
27
27
  export declare function getFieldErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): Record<string, ErrorShape[]>;
28
+ export declare function isValid(state: FormState<any>, name?: string): boolean;
28
29
  /**
29
30
  * Gets validation constraint for a field, with fallback to parent array patterns.
30
31
  * e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
@@ -32,26 +33,26 @@ export declare function getFieldErrors<ErrorShape>(state: FormState<ErrorShape>,
32
33
  export declare function getConstraint(context: FormContext<any>, name: string): ValidationAttributes | undefined;
33
34
  export declare function getFormMetadata<ErrorShape>(context: FormContext<ErrorShape>, options: {
34
35
  serialize: Serialize;
35
- }): FormMetadata<ErrorShape, DefaultFieldMetadata<ErrorShape>>;
36
+ }): FormMetadata<ErrorShape, DefaultMetadata<ErrorShape>>;
36
37
  export declare function getField<FieldShape, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
37
38
  name: FieldName<FieldShape>;
38
39
  serialize: Serialize;
39
40
  key?: string;
40
- }): Field<FieldShape, DefaultFieldMetadata<ErrorShape>>;
41
+ }): FieldMetadata<FieldShape, DefaultMetadata<ErrorShape>>;
41
42
  /**
42
43
  * Creates a proxy that dynamically generates field objects when properties are accessed.
43
44
  */
44
45
  export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
45
46
  name?: FieldName<FieldShape>;
46
47
  serialize: Serialize;
47
- }): Fieldset<FieldShape, DefaultFieldMetadata<ErrorShape>>;
48
+ }): Fieldset<FieldShape, DefaultMetadata<ErrorShape>>;
48
49
  /**
49
50
  * Creates an array of field objects for list/array inputs
50
51
  */
51
52
  export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
52
53
  name: FieldName<FieldShape>;
53
54
  serialize: Serialize;
54
- }): Field<[
55
+ }): FieldMetadata<[
55
56
  FieldShape
56
- ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, DefaultFieldMetadata<ErrorShape>>[];
57
+ ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, DefaultMetadata<ErrorShape>>[];
57
58
  //# sourceMappingURL=state.d.ts.map
@@ -10,8 +10,8 @@ function initializeState() {
10
10
  return {
11
11
  resetKey: util.generateUniqueKey(),
12
12
  listKeys: {},
13
- intendedValue: null,
14
- serverValidatedValue: null,
13
+ clientIntendedValue: null,
14
+ serverIntendedValue: null,
15
15
  serverError: null,
16
16
  clientError: null,
17
17
  touchedFields: []
@@ -33,20 +33,20 @@ function updateState(state, action) {
33
33
 
34
34
  // Apply the form error and intended value from the result first
35
35
  state = action.type === 'client' ? util.merge(state, {
36
- intendedValue: !action.intent ? value : (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.intendedValue,
36
+ clientIntendedValue: (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.clientIntendedValue,
37
+ serverIntendedValue: action.intendedValue ? null : state.serverIntendedValue,
37
38
  // Update client error only if the error is different from the previous one to minimize unnecessary re-renders
38
39
  clientError: typeof action.error !== 'undefined' && !future.deepEqual(state.clientError, action.error) ? action.error : state.clientError,
39
40
  // Reset server error if form value is changed
40
- serverError: typeof action.error !== 'undefined' && !future.deepEqual(state.serverValidatedValue, value) ? null : state.serverError
41
+ serverError: typeof action.error !== 'undefined' && !future.deepEqual(state.serverIntendedValue, value) ? null : state.serverError
41
42
  }) : util.merge(state, {
42
- intendedValue: action.type === 'initialize' ? value : state.intendedValue,
43
43
  // Clear client error to avoid showing stale errors
44
44
  clientError: null,
45
45
  // Update server error if the error is defined.
46
46
  // There is no need to check if the error is different as we are updating other states as well
47
47
  serverError: typeof action.error !== 'undefined' ? action.error : state.serverError,
48
48
  // Keep track of the value that the serverError is based on
49
- serverValidatedValue: typeof action.error !== 'undefined' ? value : state.serverValidatedValue
49
+ serverIntendedValue: !future.deepEqual(state.serverIntendedValue, value) ? value : state.serverIntendedValue
50
50
  });
51
51
  if (action.type !== 'server' && typeof action.intent !== 'undefined') {
52
52
  var _action$intent, _action$ctx$handlers;
@@ -70,19 +70,19 @@ function updateState(state, action) {
70
70
  return state;
71
71
  }
72
72
  function getDefaultValue(context, name) {
73
- var _ref, _context$state$intend;
73
+ var _ref, _context$state$server;
74
74
  var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
75
- var value = future.getValueAtPath((_ref = (_context$state$intend = context.state.intendedValue) !== null && _context$state$intend !== void 0 ? _context$state$intend : context.defaultValue) !== null && _ref !== void 0 ? _ref : {}, name);
75
+ var value = future.getValueAtPath((_ref = (_context$state$server = context.state.serverIntendedValue) !== null && _context$state$server !== void 0 ? _context$state$server : context.state.clientIntendedValue) !== null && _ref !== void 0 ? _ref : context.defaultValue, name);
76
76
  var serializedValue = serialize(value);
77
77
  if (typeof serializedValue === 'string') {
78
78
  return serializedValue;
79
79
  }
80
80
  }
81
81
  function getDefaultOptions(context, name) {
82
- var _ref2, _context$state$intend2;
82
+ var _ref2, _context$state$server2;
83
83
  var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
84
- var value = future.getValueAtPath((_ref2 = (_context$state$intend2 = context.state.intendedValue) !== null && _context$state$intend2 !== void 0 ? _context$state$intend2 : context.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {}, name);
85
- var serializedValue = typeof value !== 'undefined' ? serialize(value) : undefined;
84
+ var value = future.getValueAtPath((_ref2 = (_context$state$server2 = context.state.serverIntendedValue) !== null && _context$state$server2 !== void 0 ? _context$state$server2 : context.state.clientIntendedValue) !== null && _ref2 !== void 0 ? _ref2 : context.defaultValue, name);
85
+ var serializedValue = serialize(value);
86
86
  if (Array.isArray(serializedValue) && serializedValue.every(item => typeof item === 'string')) {
87
87
  return serializedValue;
88
88
  }
@@ -91,10 +91,10 @@ function getDefaultOptions(context, name) {
91
91
  }
92
92
  }
93
93
  function isDefaultChecked(context, name) {
94
- var _ref3, _context$state$intend3;
94
+ var _ref3, _context$state$server3;
95
95
  var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
96
- var value = future.getValueAtPath((_ref3 = (_context$state$intend3 = context.state.intendedValue) !== null && _context$state$intend3 !== void 0 ? _context$state$intend3 : context.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : {}, name);
97
- var serializedValue = typeof value !== 'undefined' ? serialize(value) : undefined;
96
+ var value = future.getValueAtPath((_ref3 = (_context$state$server3 = context.state.serverIntendedValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.clientIntendedValue) !== null && _ref3 !== void 0 ? _ref3 : context.defaultValue, name);
97
+ var serializedValue = serialize(value);
98
98
  return serializedValue === 'on';
99
99
  }
100
100
 
@@ -116,8 +116,8 @@ function getDefaultListKey(prefix, initialValue, name) {
116
116
  return util.getArrayAtPath(initialValue, name).map((_, index) => "".concat(prefix, "-").concat(future.appendPathSegment(name, index)));
117
117
  }
118
118
  function getListKey(context, name) {
119
- var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
120
- return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
119
+ var _context$state$listKe, _context$state$listKe2, _ref4, _context$state$server4;
120
+ return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_ref4 = (_context$state$server4 = context.state.serverIntendedValue) !== null && _context$state$server4 !== void 0 ? _context$state$server4 : context.state.clientIntendedValue) !== null && _ref4 !== void 0 ? _ref4 : context.defaultValue, name);
121
121
  }
122
122
  function getErrors(state, name) {
123
123
  var _state$serverError;
@@ -131,19 +131,53 @@ function getErrors(state, name) {
131
131
  }
132
132
  }
133
133
  function getFieldErrors(state, name) {
134
+ var _state$serverError2;
134
135
  var result = {};
136
+ var error = (_state$serverError2 = state.serverError) !== null && _state$serverError2 !== void 0 ? _state$serverError2 : state.clientError;
137
+ if (error) {
138
+ var basePath = future.getPathSegments(name);
139
+ for (var field of Object.keys(error.fieldErrors)) {
140
+ var relativePath = future.getRelativePath(field, basePath);
141
+
142
+ // Only include errors for specified field's children
143
+ if (!relativePath || relativePath.length === 0) {
144
+ continue;
145
+ }
146
+ var _error = getErrors(state, field);
147
+ if (typeof _error !== 'undefined') {
148
+ result[future.formatPathSegments(relativePath)] = _error;
149
+ }
150
+ }
151
+ }
152
+ return result;
153
+ }
154
+ function isValid(state, name) {
155
+ var _state$serverError3;
156
+ var error = (_state$serverError3 = state.serverError) !== null && _state$serverError3 !== void 0 ? _state$serverError3 : state.clientError;
157
+
158
+ // If there is no error, it must be valid
159
+ if (!error) {
160
+ return true;
161
+ }
135
162
  var basePath = future.getPathSegments(name);
136
- for (var field of state.touchedFields) {
137
- var relativePath = future.getRelativePath(field, basePath);
138
- if (!relativePath || relativePath.length === 0) {
163
+ for (var field of Object.keys(error.fieldErrors)) {
164
+ // When checking a specific field, only check that field and its children
165
+ if (name && !future.getRelativePath(field, basePath)) {
139
166
  continue;
140
167
  }
141
- var error = getErrors(state, field);
142
- if (typeof error !== 'undefined') {
143
- result[future.formatPathSegments(relativePath)] = error;
168
+
169
+ // If the field is not touched, we don't consider its error
170
+ var _error2 = getErrors(state, field);
171
+ if (_error2) {
172
+ return false;
144
173
  }
145
174
  }
146
- return result;
175
+
176
+ // Make sure there is no form error when checking the whole form
177
+ if (!name) {
178
+ return !getErrors(state);
179
+ }
180
+ return true;
147
181
  }
148
182
 
149
183
  /**
@@ -189,7 +223,7 @@ function getFormMetadata(context, options) {
189
223
  return isTouched(context.state);
190
224
  },
191
225
  get valid() {
192
- return typeof getErrors(context.state) === 'undefined';
226
+ return isValid(context.state);
193
227
  },
194
228
  get invalid() {
195
229
  return !this.valid;
@@ -248,7 +282,7 @@ function getField(context, options) {
248
282
  return isTouched(context.state, options.name);
249
283
  },
250
284
  get valid() {
251
- return typeof getErrors(context.state, options.name) === 'undefined';
285
+ return isValid(context.state, options.name);
252
286
  },
253
287
  get invalid() {
254
288
  return !this.valid;
@@ -315,4 +349,5 @@ exports.getListKey = getListKey;
315
349
  exports.initializeState = initializeState;
316
350
  exports.isDefaultChecked = isDefaultChecked;
317
351
  exports.isTouched = isTouched;
352
+ exports.isValid = isValid;
318
353
  exports.updateState = updateState;
@@ -6,8 +6,8 @@ function initializeState() {
6
6
  return {
7
7
  resetKey: generateUniqueKey(),
8
8
  listKeys: {},
9
- intendedValue: null,
10
- serverValidatedValue: null,
9
+ clientIntendedValue: null,
10
+ serverIntendedValue: null,
11
11
  serverError: null,
12
12
  clientError: null,
13
13
  touchedFields: []
@@ -29,20 +29,20 @@ function updateState(state, action) {
29
29
 
30
30
  // Apply the form error and intended value from the result first
31
31
  state = action.type === 'client' ? merge(state, {
32
- intendedValue: !action.intent ? value : (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.intendedValue,
32
+ clientIntendedValue: (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.clientIntendedValue,
33
+ serverIntendedValue: action.intendedValue ? null : state.serverIntendedValue,
33
34
  // Update client error only if the error is different from the previous one to minimize unnecessary re-renders
34
35
  clientError: typeof action.error !== 'undefined' && !deepEqual(state.clientError, action.error) ? action.error : state.clientError,
35
36
  // Reset server error if form value is changed
36
- serverError: typeof action.error !== 'undefined' && !deepEqual(state.serverValidatedValue, value) ? null : state.serverError
37
+ serverError: typeof action.error !== 'undefined' && !deepEqual(state.serverIntendedValue, value) ? null : state.serverError
37
38
  }) : merge(state, {
38
- intendedValue: action.type === 'initialize' ? value : state.intendedValue,
39
39
  // Clear client error to avoid showing stale errors
40
40
  clientError: null,
41
41
  // Update server error if the error is defined.
42
42
  // There is no need to check if the error is different as we are updating other states as well
43
43
  serverError: typeof action.error !== 'undefined' ? action.error : state.serverError,
44
44
  // Keep track of the value that the serverError is based on
45
- serverValidatedValue: typeof action.error !== 'undefined' ? value : state.serverValidatedValue
45
+ serverIntendedValue: !deepEqual(state.serverIntendedValue, value) ? value : state.serverIntendedValue
46
46
  });
47
47
  if (action.type !== 'server' && typeof action.intent !== 'undefined') {
48
48
  var _action$intent, _action$ctx$handlers;
@@ -66,19 +66,19 @@ function updateState(state, action) {
66
66
  return state;
67
67
  }
68
68
  function getDefaultValue(context, name) {
69
- var _ref, _context$state$intend;
69
+ var _ref, _context$state$server;
70
70
  var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
71
- var value = getValueAtPath((_ref = (_context$state$intend = context.state.intendedValue) !== null && _context$state$intend !== void 0 ? _context$state$intend : context.defaultValue) !== null && _ref !== void 0 ? _ref : {}, name);
71
+ var value = getValueAtPath((_ref = (_context$state$server = context.state.serverIntendedValue) !== null && _context$state$server !== void 0 ? _context$state$server : context.state.clientIntendedValue) !== null && _ref !== void 0 ? _ref : context.defaultValue, name);
72
72
  var serializedValue = serialize$1(value);
73
73
  if (typeof serializedValue === 'string') {
74
74
  return serializedValue;
75
75
  }
76
76
  }
77
77
  function getDefaultOptions(context, name) {
78
- var _ref2, _context$state$intend2;
78
+ var _ref2, _context$state$server2;
79
79
  var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
80
- var value = getValueAtPath((_ref2 = (_context$state$intend2 = context.state.intendedValue) !== null && _context$state$intend2 !== void 0 ? _context$state$intend2 : context.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {}, name);
81
- var serializedValue = typeof value !== 'undefined' ? serialize$1(value) : undefined;
80
+ var value = getValueAtPath((_ref2 = (_context$state$server2 = context.state.serverIntendedValue) !== null && _context$state$server2 !== void 0 ? _context$state$server2 : context.state.clientIntendedValue) !== null && _ref2 !== void 0 ? _ref2 : context.defaultValue, name);
81
+ var serializedValue = serialize$1(value);
82
82
  if (Array.isArray(serializedValue) && serializedValue.every(item => typeof item === 'string')) {
83
83
  return serializedValue;
84
84
  }
@@ -87,10 +87,10 @@ function getDefaultOptions(context, name) {
87
87
  }
88
88
  }
89
89
  function isDefaultChecked(context, name) {
90
- var _ref3, _context$state$intend3;
90
+ var _ref3, _context$state$server3;
91
91
  var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
92
- var value = getValueAtPath((_ref3 = (_context$state$intend3 = context.state.intendedValue) !== null && _context$state$intend3 !== void 0 ? _context$state$intend3 : context.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : {}, name);
93
- var serializedValue = typeof value !== 'undefined' ? serialize$1(value) : undefined;
92
+ var value = getValueAtPath((_ref3 = (_context$state$server3 = context.state.serverIntendedValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.clientIntendedValue) !== null && _ref3 !== void 0 ? _ref3 : context.defaultValue, name);
93
+ var serializedValue = serialize$1(value);
94
94
  return serializedValue === 'on';
95
95
  }
96
96
 
@@ -112,8 +112,8 @@ function getDefaultListKey(prefix, initialValue, name) {
112
112
  return getArrayAtPath(initialValue, name).map((_, index) => "".concat(prefix, "-").concat(appendPathSegment(name, index)));
113
113
  }
114
114
  function getListKey(context, name) {
115
- var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
116
- return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
115
+ var _context$state$listKe, _context$state$listKe2, _ref4, _context$state$server4;
116
+ return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_ref4 = (_context$state$server4 = context.state.serverIntendedValue) !== null && _context$state$server4 !== void 0 ? _context$state$server4 : context.state.clientIntendedValue) !== null && _ref4 !== void 0 ? _ref4 : context.defaultValue, name);
117
117
  }
118
118
  function getErrors(state, name) {
119
119
  var _state$serverError;
@@ -127,19 +127,53 @@ function getErrors(state, name) {
127
127
  }
128
128
  }
129
129
  function getFieldErrors(state, name) {
130
+ var _state$serverError2;
130
131
  var result = {};
132
+ var error = (_state$serverError2 = state.serverError) !== null && _state$serverError2 !== void 0 ? _state$serverError2 : state.clientError;
133
+ if (error) {
134
+ var basePath = getPathSegments(name);
135
+ for (var field of Object.keys(error.fieldErrors)) {
136
+ var relativePath = getRelativePath(field, basePath);
137
+
138
+ // Only include errors for specified field's children
139
+ if (!relativePath || relativePath.length === 0) {
140
+ continue;
141
+ }
142
+ var _error = getErrors(state, field);
143
+ if (typeof _error !== 'undefined') {
144
+ result[formatPathSegments(relativePath)] = _error;
145
+ }
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+ function isValid(state, name) {
151
+ var _state$serverError3;
152
+ var error = (_state$serverError3 = state.serverError) !== null && _state$serverError3 !== void 0 ? _state$serverError3 : state.clientError;
153
+
154
+ // If there is no error, it must be valid
155
+ if (!error) {
156
+ return true;
157
+ }
131
158
  var basePath = getPathSegments(name);
132
- for (var field of state.touchedFields) {
133
- var relativePath = getRelativePath(field, basePath);
134
- if (!relativePath || relativePath.length === 0) {
159
+ for (var field of Object.keys(error.fieldErrors)) {
160
+ // When checking a specific field, only check that field and its children
161
+ if (name && !getRelativePath(field, basePath)) {
135
162
  continue;
136
163
  }
137
- var error = getErrors(state, field);
138
- if (typeof error !== 'undefined') {
139
- result[formatPathSegments(relativePath)] = error;
164
+
165
+ // If the field is not touched, we don't consider its error
166
+ var _error2 = getErrors(state, field);
167
+ if (_error2) {
168
+ return false;
140
169
  }
141
170
  }
142
- return result;
171
+
172
+ // Make sure there is no form error when checking the whole form
173
+ if (!name) {
174
+ return !getErrors(state);
175
+ }
176
+ return true;
143
177
  }
144
178
 
145
179
  /**
@@ -185,7 +219,7 @@ function getFormMetadata(context, options) {
185
219
  return isTouched(context.state);
186
220
  },
187
221
  get valid() {
188
- return typeof getErrors(context.state) === 'undefined';
222
+ return isValid(context.state);
189
223
  },
190
224
  get invalid() {
191
225
  return !this.valid;
@@ -244,7 +278,7 @@ function getField(context, options) {
244
278
  return isTouched(context.state, options.name);
245
279
  },
246
280
  get valid() {
247
- return typeof getErrors(context.state, options.name) === 'undefined';
281
+ return isValid(context.state, options.name);
248
282
  },
249
283
  get invalid() {
250
284
  return !this.valid;
@@ -297,4 +331,4 @@ function getFieldList(context, options) {
297
331
  });
298
332
  }
299
333
 
300
- export { getConstraint, getDefaultListKey, getDefaultOptions, getDefaultValue, getErrors, getField, getFieldErrors, getFieldList, getFieldset, getFormMetadata, getListKey, initializeState, isDefaultChecked, isTouched, updateState };
334
+ export { getConstraint, getDefaultListKey, getDefaultOptions, getDefaultValue, getErrors, getField, getFieldErrors, getFieldList, getFieldset, getFormMetadata, getListKey, initializeState, isDefaultChecked, isTouched, isValid, updateState };
@@ -1,5 +1,5 @@
1
1
  import type { FormError, FormValue, SubmissionResult, ValidationAttributes } from '@conform-to/dom/future';
2
- import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { StandardSchemaV1 } from './standard-schema';
3
3
  export type Prettify<T> = {
4
4
  [K in keyof T]: T[K];
5
5
  } & {};
@@ -70,10 +70,10 @@ export type DefaultValue<FormShape> = FormShape extends string | number | boolea
70
70
  export type FormState<ErrorShape> = {
71
71
  /** Unique identifier that changes on form reset to trigger reset side effects */
72
72
  resetKey: string;
73
- /** Form values from user intent actions (validate, update, insert, remove, etc.) */
74
- intendedValue: Record<string, unknown> | null;
75
- /** Form values that have been validated on the server */
76
- serverValidatedValue: Record<string, unknown> | null;
73
+ /** Form values from client actions that will be synced to the DOM */
74
+ clientIntendedValue: Record<string, unknown> | null;
75
+ /** Form values from server actions, or submitted values when no server intent exists */
76
+ serverIntendedValue: Record<string, unknown> | null;
77
77
  /** Validation errors from server-side processing */
78
78
  serverError: FormError<ErrorShape> | null;
79
79
  /** Validation errors from client-side validation */
@@ -241,7 +241,7 @@ export type Combine<T> = {
241
241
  [K in keyof BaseCombine<T>]: BaseCombine<T>[K];
242
242
  };
243
243
  /** Field metadata object containing field state, validation attributes, and nested field access methods. */
244
- export type Field<FieldShape, Metadata extends Record<string, unknown> = DefaultFieldMetadata<unknown>> = Readonly<Metadata & {
244
+ export type FieldMetadata<FieldShape, Metadata extends Record<string, unknown> = DefaultMetadata<unknown>> = Readonly<Metadata & {
245
245
  /** Unique key for React list rendering (for array fields). */
246
246
  key: string | undefined;
247
247
  /** The field name path exactly as provided. */
@@ -251,17 +251,17 @@ export type Field<FieldShape, Metadata extends Record<string, unknown> = Default
251
251
  FieldShape
252
252
  ] extends [Record<string, unknown> | null | undefined] ? FieldShape : unknown, Metadata>;
253
253
  /** Method to get array of fields for list/array fields under this field. */
254
- getFieldList(): Array<Field<[
254
+ getFieldList(): Array<FieldMetadata<[
255
255
  FieldShape
256
256
  ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, Metadata>>;
257
257
  }>;
258
258
  /** Fieldset object containing all form fields as properties with their respective field metadata. */
259
259
  export type Fieldset<FieldShape, // extends Record<string, unknown>,
260
- FieldMetadata extends Record<string, unknown>> = {
261
- [Key in keyof Combine<FieldShape>]-?: Field<Combine<FieldShape>[Key], FieldMetadata>;
260
+ Metadata extends Record<string, unknown>> = {
261
+ [Key in keyof Combine<FieldShape>]-?: FieldMetadata<Combine<FieldShape>[Key], Metadata>;
262
262
  };
263
263
  /** Form-level metadata and state object containing validation status, errors, and field access methods. */
264
- export type FormMetadata<ErrorShape, FieldMetadata extends Record<string, unknown> = DefaultFieldMetadata<ErrorShape>> = Readonly<{
264
+ export type FormMetadata<ErrorShape, Metadata extends Record<string, unknown> = DefaultMetadata<ErrorShape>> = Readonly<{
265
265
  /** Unique identifier that changes on form reset */
266
266
  key: string;
267
267
  /** The form's unique identifier. */
@@ -291,18 +291,18 @@ export type FormMetadata<ErrorShape, FieldMetadata extends Record<string, unknow
291
291
  /** The current state of the form */
292
292
  context: FormContext<ErrorShape>;
293
293
  /** Method to get metadata for a specific field by name. */
294
- getField<FieldShape>(name: FieldName<FieldShape>): Field<FieldShape, FieldMetadata>;
294
+ getField<FieldShape>(name: FieldName<FieldShape>): FieldMetadata<FieldShape, Metadata>;
295
295
  /** Method to get a fieldset object for nested object fields. */
296
296
  getFieldset<FieldShape>(name: FieldName<FieldShape>): Fieldset<[
297
297
  FieldShape
298
- ] extends [Record<string, unknown> | null | undefined] ? FieldShape : unknown, FieldMetadata>;
298
+ ] extends [Record<string, unknown> | null | undefined] ? FieldShape : unknown, Metadata>;
299
299
  /** Method to get an array of field objects for array fields. */
300
- getFieldList<FieldShape>(name: FieldName<FieldShape>): Array<Field<[
300
+ getFieldList<FieldShape>(name: FieldName<FieldShape>): Array<FieldMetadata<[
301
301
  FieldShape
302
- ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, FieldMetadata>>;
302
+ ] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, Metadata>>;
303
303
  }>;
304
304
  /** Default field metadata object containing field state, validation attributes, and accessibility IDs. */
305
- export type DefaultFieldMetadata<ErrorShape> = Readonly<ValidationAttributes & {
305
+ export type DefaultMetadata<ErrorShape> = Readonly<ValidationAttributes & {
306
306
  /** The field's unique identifier, automatically generated as {formId}-field-{fieldName}. */
307
307
  id: string;
308
308
  /** Auto-generated ID for associating field descriptions via aria-describedby. */
@@ -1,5 +1,5 @@
1
1
  import type { FormError } from '@conform-to/dom/future';
2
- import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { StandardSchemaV1 } from './standard-schema';
3
3
  import { ValidateHandler, ValidateResult } from './types';
4
4
  export declare function isUndefined(value: unknown): value is undefined;
5
5
  export declare function isString(value: unknown): value is string;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Conform view adapter for react",
4
4
  "homepage": "https://conform.guide",
5
5
  "license": "MIT",
6
- "version": "1.9.1",
6
+ "version": "1.10.0",
7
7
  "main": "./dist/index.js",
8
8
  "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
@@ -41,7 +41,7 @@
41
41
  "url": "https://github.com/edmundhung/conform/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@conform-to/dom": "1.9.1"
44
+ "@conform-to/dom": "1.10.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@babel/core": "^7.17.8",
@@ -50,7 +50,6 @@
50
50
  "@babel/preset-typescript": "^7.20.2",
51
51
  "@rollup/plugin-babel": "^5.3.1",
52
52
  "@rollup/plugin-node-resolve": "^13.3.0",
53
- "@standard-schema/spec": "^1.0.0",
54
53
  "@types/react": "^18.2.43",
55
54
  "@types/react-dom": "^18.3.5",
56
55
  "react": "^18.2.0",