UNPKG

@conform-to/dom

Version:

A set of opinionated helpers built on top of the Constraint Validation API

402 lines 16.3 kB
import type { FormError, FormValue, FieldName, JsonPrimitive, CustomSerialize, Submission, SubmissionResult, UnknownObject, Serialize, StandardSchemaError, CustomError } from './types'; export declare const DEFAULT_INTENT_NAME = "__INTENT__"; /** * Returns whether an error payload contains a meaningful value. * Empty strings and empty arrays are treated as no error. */ export declare function hasError<ErrorShape>(error: ErrorShape | null | undefined): error is ErrorShape; /** * Normalizes a form error object by removing empty error payloads such as * empty strings and empty arrays. * * Returns `null` when no form-level or field-level errors remain. */ export declare function normalizeFormError<ErrorShape>(error: CustomError<ErrorShape> | null): FormError<ErrorShape> | null; /** * Construct a form data with the submitter value. * It utilizes the submitter argument on the FormData constructor from modern browsers * with fallback to append the submitter value in case it is not unsupported. * * See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters */ export declare function getFormData(form: HTMLFormElement, submitter?: HTMLElement | null): FormData; /** * Convert a string path into an array of segments. * * **Example:** * ```js * parsePath("object.key"); // → ['object', 'key'] * parsePath("array[0].content"); // → ['array', 0, 'content'] * parsePath("todos[]"); // → ['todos', ''] * ``` */ export declare function parsePath(path: string | undefined): Array<string | number>; /** * Returns a formatted name from the path segments based on the dot and bracket notation. * * **Example:** * ```js * formatPath(['object', 'key']); // → "object.key" * formatPath(['array', 0, 'content']); // → "array[0].content" * formatPath(['todos', '']); // → "todos[]" * ``` */ export declare function formatPath(segments: Readonly<Array<string | number>>): string; /** * Append one more segment onto an existing path string. * * - segment = `undefined` ⇒ no-op * - segment = `""` ⇒ empty brackets "[]" * - segment = `number` ⇒ bracket notation "[n]" * - segment = `string` ⇒ dot-notation ".prop" */ export declare function appendPath(path: string | undefined, segment: string | number | undefined): string; /** * Returns true if `prefix` is a valid leading path of `name`. * * **Example:** * ```js * isPathPrefix("foo.bar.baz", "foo.bar") // → true * isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true * isPathPrefix("foo.bar[3].baz", "foo.bar") // → true * isPathPrefix("foo.bar[3].baz", "foo.baz") // → false * isPathPrefix("foo", "foo.bar") // → false * ``` */ export declare function isPathPrefix(name: string, prefix: string): boolean; /** * Return the segments of `fullPath` that come after the `basePath` prefix. * * @param fullPath Full path as a dot/bracket string or array of segments * @param basePath Base path as a dot/bracket string or array of segments * @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath` * * **Example:** * ```js * getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"] * getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"] * getRelativePath("foo", ["foo","bar"]) // → null * ``` */ export declare function getRelativePath(fullPath: string | Array<string | number>, basePath: string | Array<string | number>): Array<string | number> | null; /** * Assign a value to a target object by following the path segments. */ export declare function setPathValue<T extends Record<string, any>>(target: T, pathOrSegments: string | Array<string | number>, valueOrFn: unknown | ((current: unknown) => unknown), options?: { clone?: boolean; silent?: boolean; }): T; /** * Retrive the value from a target object by following the path segments. */ export declare function getPathValue(target: unknown, pathOrSegments: string | Array<string | number>): unknown; /** * Parse `FormData` or `URLSearchParams` into a submission object. * This function structures the form values based on the naming convention. * It also includes all the field names and extracts the intent from the submission. * * See https://conform.guide/api/react/future/parseSubmission * * **Example:** * ```ts * const formData = new FormData(); * * formData.append('email', 'test@example.com'); * formData.append('password', 'secret'); * * parseSubmission(formData) * // { * // payload: { email: 'test@example.com', password: 'secret' }, * // fields: ['email', 'password'], * // intent: null, * // } * * // If you have an intent field * formData.append('intent', 'login'); * parseSubmission(formData, { intentName: 'intent' }) * // { * // payload: { email: 'test@example.com', password: 'secret' }, * // fields: ['email', 'password'], * // intent: 'login', * // } * ``` */ export declare function parseSubmission(formData: FormData | URLSearchParams, options?: { /** * The name of the submit button field that indicates the submission intent. * Defaults to `__INTENT__`. */ intentName?: string | undefined; /** * A function to exclude specific form fields from being parsed. * Return `true` to skip the entry. */ skipEntry?: ((name: string) => boolean) | undefined; /** * Whether to strip empty values (empty strings, empty files, arrays with all empty values) * from the submission payload. Defaults to `false`. * * @deprecated This option will be removed in a future minor release. * If you are using a schema library like Zod or Valibot, our integration * already strips empty values for you. There is no need to use this option. */ stripEmptyValues?: boolean | undefined; }): Submission; /** * Creates a SubmissionResult object from a submission, adding validation results and target values. * This function will remove all files in the submission payload by default since * file inputs cannot be initialized with files. * You can specify `keepFiles: true` to keep the files if needed. * * See https://conform.guide/api/react/future/report * * **Example:** * ```ts * // Report the submission with the field errors * report(submission, { * error: { * fieldErrors: { * email: ['Invalid email format'], * password: ['Password is required'], * }, * }) * * // Report the submission with a form error * report(submission, { * error: { * formErrors: ['Invalid credentials'], * }, * }) * * // Reset the form * report(submission, { * reset: true, * }) * ``` */ export declare function report(submission: Submission, options?: { keepFiles?: false; error?: null; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<never, Exclude<JsonPrimitive | FormDataEntryValue, File>>; export declare function report(submission: Submission, options: { keepFiles: true; error?: null; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<never>; export declare function report<ErrorShape>(submission: Submission, options: { keepFiles?: false; error: CustomError<ErrorShape> | null; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<ErrorShape, Exclude<JsonPrimitive | FormDataEntryValue, File>>; export declare function report<ErrorShape>(submission: Submission, options: { keepFiles: true; error: CustomError<ErrorShape> | null; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<ErrorShape>; export declare function report(submission: Submission, options: { keepFiles?: false; error: StandardSchemaError; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<string[], Exclude<JsonPrimitive | FormDataEntryValue, File>>; export declare function report(submission: Submission, options: { keepFiles: true; error: StandardSchemaError; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<string[]>; export declare function report<ErrorShape>(submission: Submission, options?: { keepFiles?: boolean; error?: CustomError<ErrorShape> | null; value?: Record<string, FormValue> | null; hideFields?: string[]; reset?: boolean; }): SubmissionResult<ErrorShape>; /** * A utility function that checks whether the current form data differs from the default values. * * See https://conform.guide/api/react/future/isDirty * * **Example: Enable a submit button only if the form is dirty** * * ```tsx * const dirty = useFormData( * formRef, * (formData) => isDirty(formData, { defaultValue }) ?? false, * ); * * return ( * <button type="submit" disabled={!dirty}> * Save changes * </button> * ); * ``` */ export declare function isDirty( /** * The current form data to compare. It can be: * * - A `FormData` object * - A `URLSearchParams` object * - A plain object that was parsed from form data (i.e. `submission.payload`) */ formData: FormData | URLSearchParams | FormValue | null, options?: { /** * An object representing the default values of the form to compare against. * Defaults to an empty object if not provided. */ defaultValue?: unknown | undefined; /** * The name of the submit button that triggered the submission. * It will be excluded from the dirty comparison. */ intentName?: string | undefined; /** * A function to serialize values in defaultValue before comparing them to the form data. * If not provided, a default serializer is used that behaves as follows: * * - string / File: * - Returned as-is * - boolean: * - true'on' * - falsenull * - number / bigint: * - Converted to string using `.toString()` * - Date: * - Converted to UTC datetime string without trailing `Z` (e.g. `2026-01-01T12:00:00.000`) */ serialize?: CustomSerialize | undefined; /** * A function to exclude specific fields from the comparison. * Useful for ignoring hidden inputs like CSRF tokens or internal fields added by frameworks * (e.g. Next.js uses hidden inputs to support server actions). * * **Example:** * ```ts * isDirty(formData, { * skipEntry: (name) => name === 'csrf-token', * }); * ``` */ skipEntry?: ((name: string) => boolean) | undefined; }): boolean | undefined; /** * Convert an unknown value into something acceptable for HTML form submission. * Returns `undefined` when the value cannot be represented in form data. * * Input -> Output: * - string -> string * - null -> '' (empty string) * - boolean -> 'on' | '' (checked semantics) * - number | bigint -> value.toString() * - Date -> value.toISOString() without trailing `Z` * - File -> File * - FileList -> File[] * - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined * - anything else -> undefined */ export declare function defaultSerialize(value: unknown): ReturnType<Serialize>; /** * Recursively serializes a value using the provided serialize function, * collapsing empty leaves (`null`, `''`, empty files) to `undefined` * and removing empty containers (objects with no remaining keys, empty arrays). * * When serialize returns `undefined` for a value (i.e. it can't be represented * as form data), the raw value is kept and recursed into if it's an object or array. * * Single-element arrays where the element is a string or undefined are unwrapped * to handle the case where a multi-value field (e.g. checkboxes) has only one value. */ export declare function normalize(value: unknown, serialize?: Serialize, name?: string): unknown; /** * Retrieve a field value from FormData with optional type guards. * * **Example:** * * ```ts * // Basic field access: return `unknown` * const email = getFieldValue(formData, 'email'); * // String type: returns `string` * const name = getFieldValue(formData, 'name', { type: 'string' }); * // File type: returns `File` * const avatar = getFieldValue(formData, 'avatar', { type: 'file' }); * // Object type: returns { city: unknown, ... } * const address = getFieldValue<Address>(formData, 'address', { type: 'object' }); * // Array: returns `unknown[]` * const tags = getFieldValue(formData, 'tags', { array: true }); * // Array with object type: returns `Array<{ name: unknown, ... }>` * const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true }); * // Optional string type: returns `string | undefined` * const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true }); * ``` */ export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'object'; array: true; optional: true; }): FieldShape extends Array<infer Item extends Record<string, unknown>> ? Array<UnknownObject<Item>> | undefined : never; export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'object'; array: true; }): FieldShape extends Array<infer Item extends Record<string, unknown>> ? Array<UnknownObject<Item>> : never; export declare function getFieldValue<FieldShape extends Record<string, unknown> = Record<string, unknown>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'object'; optional: true; }): UnknownObject<FieldShape> | undefined; export declare function getFieldValue<FieldShape extends Record<string, unknown> = Record<string, unknown>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'object'; }): UnknownObject<FieldShape>; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'string'; array: true; optional: true; }): string[] | undefined; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'string'; array: true; }): string[]; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'string'; optional: true; }): string | undefined; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'string'; }): string; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'file'; array: true; optional: true; }): File[] | undefined; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'file'; array: true; }): File[]; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'file'; optional: true; }): File | undefined; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { type: 'file'; }): File; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { array: true; optional: true; }): Array<unknown> | undefined; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: { array: true; }): Array<unknown>; export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options?: { optional?: boolean; }): unknown; //# sourceMappingURL=formdata.d.ts.map