Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@tanstack/form-core

Package Overview
Dependencies
Maintainers
0
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/form-core - npm Package Compare versions

Comparing version 0.23.2 to 0.23.3

221

dist/esm/FieldApi.d.ts

@@ -7,2 +7,5 @@ import { Store } from '@tanstack/store';

/**
* @private
*/
export type FieldValidateFn<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = (props: {

@@ -12,3 +15,9 @@ value: TData;

}) => ValidationError;
/**
* @private
*/
export type FieldValidateOrFn<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = TFieldValidator extends Validator<TData, infer TFN> ? TFN | FieldValidateFn<TParentData, TName, TFieldValidator, TFormValidator, TData> : TFormValidator extends Validator<TParentData, infer FFN> ? FFN | FieldValidateFn<TParentData, TName, TFieldValidator, TFormValidator, TData> : FieldValidateFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* @private
*/
export type FieldValidateAsyncFn<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = (options: {

@@ -19,51 +28,201 @@ value: TData;

}) => ValidationError | Promise<ValidationError>;
/**
* @private
*/
export type FieldAsyncValidateOrFn<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = TFieldValidator extends Validator<TData, infer TFN> ? TFN | FieldValidateAsyncFn<TParentData, TName, TFieldValidator, TFormValidator, TData> : TFormValidator extends Validator<TParentData, infer FFN> ? FFN | FieldValidateAsyncFn<TParentData, TName, TFieldValidator, TFormValidator, TData> : FieldValidateAsyncFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
export interface FieldValidators<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> {
/**
* An optional function that takes a param of `formApi` which is a generic type of `TData` and `TParentData`
*/
onMount?: FieldValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional property that takes a `ValidateFn` which is a generic of `TData` and `TParentData`.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onChange?: FieldValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional property similar to `onChange` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onChangeAsync?: FieldAsyncValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional number to represent how long the `onChangeAsync` should wait before running
*
* If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
*/
onChangeAsyncDebounceMs?: number;
/**
* An optional list of field names that should trigger this field's `onChange` and `onChangeAsync` events when its value changes
*/
onChangeListenTo?: DeepKeys<TParentData>[];
/**
* An optional function, that when run when subscribing to blur event of input.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onBlur?: FieldValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional property similar to `onBlur` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onBlurAsync?: FieldAsyncValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional number to represent how long the `onBlurAsync` should wait before running
*
* If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
*/
onBlurAsyncDebounceMs?: number;
/**
* An optional list of field names that should trigger this field's `onBlur` and `onBlurAsync` events when its value changes
*/
onBlurListenTo?: DeepKeys<TParentData>[];
/**
* An optional function, that when run when subscribing to submit event of input.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onSubmit?: FieldValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional property similar to `onSubmit` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onSubmitAsync?: FieldAsyncValidateOrFn<TParentData, TName, TFieldValidator, TFormValidator, TData>;
}
/**
* An object type representing the options for a field in a form.
*/
export interface FieldOptions<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> {
/**
* The field name. The type will be `DeepKeys<TParentData>` to ensure your name is a deep key of the parent dataset.
*/
name: TName;
/**
* An optional default value for the field.
*/
defaultValue?: NoInfer<TData>;
/**
* The default time to debounce async validation if there is not a more specific debounce time passed.
*/
asyncDebounceMs?: number;
/**
* If `true`, always run async validation, even if there are errors emitted during synchronous validation.
*/
asyncAlways?: boolean;
preserveValue?: boolean;
/**
* A validator provided by an extension, like `yupValidator` from `@tanstack/yup-form-adapter`
*/
validatorAdapter?: TFieldValidator;
/**
* A list of validators to pass to the field
*/
validators?: FieldValidators<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* An optional object with default metadata for the field.
*/
defaultMeta?: Partial<FieldMeta>;
}
/**
* An object type representing the required options for the FieldApi class.
*/
export interface FieldApiOptions<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> extends FieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData> {
form: FormApi<TParentData, TFormValidator>;
}
/**
* An object type representing the metadata of a field in a form.
*/
export type FieldMeta = {
/**
* A flag indicating whether the field has been touched.
*/
isTouched: boolean;
/**
* A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`.
*/
isPristine: boolean;
/**
* A flag that is `true` if the field's value has been modified by the user. Opposite of `isPristine`.
*/
isDirty: boolean;
/**
* An array of errors related to the touched state of the field.
*/
touchedErrors: ValidationError[];
/**
* An array of errors related to the field value.
*/
errors: ValidationError[];
/**
* A map of errors related to the field value.
*/
errorMap: ValidationErrorMap;
/**
* A flag indicating whether the field is currently being validated.
*/
isValidating: boolean;
};
/**
* An object type representing the state of a field.
*/
export type FieldState<TData> = {
/**
* The current value of the field.
*/
value: TData;
/**
* The current metadata of the field.
*/
meta: FieldMeta;
};
export type ResolveName<TParentData> = unknown extends TParentData ? string : DeepKeys<TParentData>;
/**
* A class representing the API for managing a form field.
*
* Normally, you will not need to create a new `FieldApi` instance directly.
* Instead, you will use a framework hook/function like `useField` or `createField`
* to create a new instance for you that uses your framework's reactivity model.
* However, if you need to create a new instance manually, you can do so by calling
* the `new FieldApi` constructor.
*/
export declare class FieldApi<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> {
/**
* A reference to the form API instance.
*/
form: FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>['form'];
/**
* The field name.
*/
name: DeepKeys<TParentData>;
/**
* The field options.
*/
options: FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>;
/**
* The field state store.
*/
store: Store<FieldState<TData>>;
/**
* The current field state.
*/
state: FieldState<TData>;
/**
* @private
*/
prevState: FieldState<TData>;
/**
* Initializes a new `FieldApi` instance.
*/
constructor(opts: FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>);
/**
* @private
*/
runValidator<TValue extends {

@@ -77,5 +236,17 @@ value: TData;

}): ReturnType<ReturnType<Validator<any>>[TType]>;
/**
* Mounts the field instance to the form.
*/
mount: () => () => void;
/**
* Updates the field instance with new options.
*/
update: (opts: FieldApiOptions<TParentData, TName, TFieldValidator, TFormValidator, TData>) => void;
/**
* Gets the current field value.
*/
getValue: () => TData;
/**
* Sets the field value and run the `change` validator.
*/
setValue: (updater: Updater<TData>, options?: {

@@ -85,32 +256,80 @@ touch?: boolean;

}) => void;
/**
* @private
*/
_getMeta: () => FieldMeta | undefined;
/**
* Gets the current field metadata.
*/
getMeta: () => FieldMeta;
/**
* Sets the field metadata.
*/
setMeta: (updater: Updater<FieldMeta>) => void;
/**
* Gets the field information object.
*/
getInfo: () => FieldInfo<TParentData, TFormValidator>;
/**
* Pushes a new value to the field.
*/
pushValue: (value: TData extends any[] ? TData[number] : never, opts?: {
touch?: boolean;
}) => void;
/**
* Inserts a value at the specified index, shifting the subsequent values to the right.
*/
insertValue: (index: number, value: TData extends any[] ? TData[number] : never, opts?: {
touch?: boolean;
}) => Promise<void>;
/**
* Replaces a value at the specified index.
*/
replaceValue: (index: number, value: TData extends any[] ? TData[number] : never, opts?: {
touch?: boolean;
}) => Promise<void>;
/**
* Removes a value at the specified index.
*/
removeValue: (index: number, opts?: {
touch: boolean;
}) => Promise<void>;
/**
* Swaps the values at the specified indices.
*/
swapValues: (aIndex: number, bIndex: number, opts?: {
touch?: boolean;
}) => void;
/**
* Moves the value at the first specified index to the second specified index.
*/
moveValue: (aIndex: number, bIndex: number, opts?: {
touch?: boolean;
}) => void;
/**
* @private
*/
getLinkedFields: (cause: ValidationCause) => FieldApi<any, any, any, any, any>[];
/**
* @private
*/
validateSync: (cause: ValidationCause) => {
hasErrored: boolean;
};
/**
* @private
*/
validateAsync: (cause: ValidationCause) => Promise<ValidationError[]>;
/**
* Validates the field value.
*/
validate: (cause: ValidationCause) => ValidationError[] | Promise<ValidationError[]>;
/**
* Handles the change event.
*/
handleChange: (updater: Updater<TData>) => void;
/**
* Handles the blur event.
*/
handleBlur: () => void;
}
import { Store } from "@tanstack/store";
import { getBy, getSyncValidatorArray, getAsyncValidatorArray } from "./utils.js";
class FieldApi {
/**
* Initializes a new `FieldApi` instance.
*/
constructor(opts) {

@@ -60,2 +63,5 @@ this.options = {};

}
if (opts2.defaultValue === void 0 && this.form._tempDefaultValue !== void 0 && this.form._tempDefaultValue.field === this.name) {
opts2.defaultValue = this.form._tempDefaultValue.value;
}
this.options = opts2;

@@ -288,2 +294,5 @@ };

this.name = opts.name;
if (opts.defaultValue === void 0 && this.form._tempDefaultValue !== void 0 && this.form._tempDefaultValue.field === this.name) {
opts.defaultValue = this.form._tempDefaultValue.value;
}
if (opts.defaultValue !== void 0) {

@@ -316,2 +325,7 @@ this.form.setFieldValue(this.name, opts.defaultValue);

this.state = state;
setTimeout(() => {
if (this.form._tempDefaultValue !== void 0 && this.form._tempDefaultValue.field === this.name) {
this.form._tempDefaultValue = void 0;
}
}, 0);
}

@@ -324,2 +338,5 @@ }

}
/**
* @private
*/
runValidator(props) {

@@ -326,0 +343,0 @@ const adapters = [

@@ -7,2 +7,5 @@ import { Store } from '@tanstack/store';

/**
* @private
*/
export type FormValidateFn<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = (props: {

@@ -12,3 +15,9 @@ value: TFormData;

}) => ValidationError;
/**
* @private
*/
export type FormValidateOrFn<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = TFormValidator extends Validator<TFormData, infer TFN> ? TFN : FormValidateFn<TFormData, TFormValidator>;
/**
* @private
*/
export type FormValidateAsyncFn<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = (props: {

@@ -19,10 +28,34 @@ value: TFormData;

}) => ValidationError | Promise<ValidationError>;
/**
* @private
*/
export type FormAsyncValidateOrFn<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = TFormValidator extends Validator<TFormData, infer FFN> ? FFN | FormValidateAsyncFn<TFormData, TFormValidator> : FormValidateAsyncFn<TFormData, TFormValidator>;
export interface FormValidators<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> {
/**
* Optional function that fires as soon as the component mounts.
*/
onMount?: FormValidateOrFn<TFormData, TFormValidator>;
/**
* Optional function that checks the validity of your data whenever a value changes
*/
onChange?: FormValidateOrFn<TFormData, TFormValidator>;
/**
* Optional onChange asynchronous counterpart to onChange. Useful for more complex validation logic that might involve server requests.
*/
onChangeAsync?: FormAsyncValidateOrFn<TFormData, TFormValidator>;
/**
* The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds.
*/
onChangeAsyncDebounceMs?: number;
/**
* Optional function that validates the form data when a field loses focus, returns a ValidationError
*/
onBlur?: FormValidateOrFn<TFormData, TFormValidator>;
/**
* Optional onBlur asynchronous validation method for when a field loses focus return a `ValidationError` or a promise of `Promise<ValidationError>`
*/
onBlurAsync?: FormAsyncValidateOrFn<TFormData, TFormValidator>;
/**
* The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds.
*/
onBlurAsyncDebounceMs?: number;

@@ -32,2 +65,5 @@ onSubmit?: FormValidateOrFn<TFormData, TFormValidator>;

}
/**
* @private
*/
export interface FormTransform<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> {

@@ -37,9 +73,33 @@ fn: (formBase: FormApi<TFormData, TFormValidator>) => FormApi<TFormData, TFormValidator>;

}
/**
* An object representing the options for a form.
*/
export interface FormOptions<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> {
/**
* Set initial values for your form.
*/
defaultValues?: TFormData;
/**
* The default state for the form.
*/
defaultState?: Partial<FormState<TFormData>>;
/**
* If true, always run async validation, even when sync validation has produced an error. Defaults to undefined.
*/
asyncAlways?: boolean;
/**
* Optional time in milliseconds if you want to introduce a delay before firing off an async action.
*/
asyncDebounceMs?: number;
/**
* A validator adapter to support usage of extra validation types (IE: Zod, Yup, or Valibot usage)
*/
validatorAdapter?: TFormValidator;
/**
* A list of validators to pass to the form
*/
validators?: FormValidators<TFormData, TFormValidator>;
/**
* A function to be called when the form is submitted, what should happen once the user submits a valid form returns `any` or a promise `Promise<any>`
*/
onSubmit?: (props: {

@@ -49,2 +109,5 @@ value: TFormData;

}) => any | Promise<any>;
/**
* Specify an action for scenarios where the user tries to submit an invalid form.
*/
onSubmitInvalid?: (props: {

@@ -56,36 +119,150 @@ value: TFormData;

}
/**
* An object representing the validation metadata for a field. Not intended for public usage.
*/
export type ValidationMeta = {
/**
* An abort controller stored in memory to cancel previous async validation attempts.
*/
lastAbortController: AbortController;
};
/**
* An object representing the field information for a specific field within the form.
*/
export type FieldInfo<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> = {
/**
* An instance of the FieldAPI.
*/
instance: FieldApi<TFormData, any, Validator<unknown, unknown> | undefined, TFormValidator> | null;
/**
* A record of field validation internal handling.
*/
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>;
};
/**
* An object representing the current state of the form.
*/
export type FormState<TFormData> = {
/**
* The current values of the form fields.
*/
values: TFormData;
/**
* A boolean indicating if the form is currently validating.
*/
isFormValidating: boolean;
/**
* A boolean indicating if the form is valid.
*/
isFormValid: boolean;
/**
* The error array for the form itself.
*/
errors: ValidationError[];
/**
* The error map for the form itself.
*/
errorMap: ValidationErrorMap;
/**
* An internal mechanism used for keeping track of validation logic in a form.
*/
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>;
/**
* A record of field metadata for each field in the form.
*/
fieldMeta: Record<DeepKeys<TFormData>, FieldMeta>;
/**
* A boolean indicating if any of the form fields are currently validating.
*/
isFieldsValidating: boolean;
/**
* A boolean indicating if all the form fields are valid.
*/
isFieldsValid: boolean;
/**
* A boolean indicating if the form is currently submitting.
*/
isSubmitting: boolean;
/**
* A boolean indicating if any of the form fields have been touched.
*/
isTouched: boolean;
/**
* A boolean indicating if any of the form's fields' values have been modified by the user. `True` if the user have modified at least one of the fields. Opposite of `isPristine`.
*/
isDirty: boolean;
/**
* A boolean indicating if none of the form's fields' values have been modified by the user. `True` if the user have not modified any of the fields. Opposite of `isDirty`.
*/
isPristine: boolean;
/**
* A boolean indicating if the form has been submitted.
*/
isSubmitted: boolean;
/**
* A boolean indicating if the form or any of its fields are currently validating.
*/
isValidating: boolean;
/**
* A boolean indicating if the form and all its fields are valid.
*/
isValid: boolean;
/**
* A boolean indicating if the form can be submitted based on its current state.
*/
canSubmit: boolean;
/**
* A counter for tracking the number of submission attempts.
*/
submissionAttempts: number;
};
/**
* A class representing the Form API. It handles the logic and interactions with the form state.
*
* Normally, you will not need to create a new `FormApi` instance directly. Instead, you will use a framework
* hook/function like `useForm` or `createForm` to create a new instance for you that uses your framework's reactivity model.
* However, if you need to create a new instance manually, you can do so by calling the `new FormApi` constructor.
*/
export declare class FormApi<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined> {
/**
* The options for the form.
*/
options: FormOptions<TFormData, TFormValidator>;
/**
* A [TanStack Store instance](https://tanstack.com/store/latest/docs/reference/Store) that keeps track of the form's state.
*/
store: Store<FormState<TFormData>>;
/**
* The current state of the form.
*
* **Note:**
* Do not use `state` directly, as it is not reactive.
* Please use form.useStore() utility to subscribe to state
*/
state: FormState<TFormData>;
/**
* A record of field information for each field in the form.
*/
fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, TFormValidator>>;
/**
* @private
*/
prevTransformArray: unknown[];
/**
* @private
* Used to handle the edgecase of `pushFieldValue` not adding a `defaultValue` to the child `FieldAPI`s that are
* subsequently generated from the `pushFieldValue` (and friends)
* @see https://github.com/TanStack/form/issues/704#issuecomment-2184080607
*/
_tempDefaultValue: undefined | {
field: string;
value: unknown;
};
/**
* Constructs a new `FormApi` instance with the given form options.
*/
constructor(opts?: FormOptions<TFormData, TFormValidator>);
/**
* @private
*/
runValidator<TValue extends {

@@ -100,18 +277,61 @@ value: TFormData;

mount: () => void;
/**
* Updates the form options and form state.
*/
update: (options?: FormOptions<TFormData, TFormValidator>) => void;
/**
* Resets the form state to the default values.
*/
reset: () => void;
/**
* Validates all fields in the form using the correct handlers for a given validation type.
*/
validateAllFields: (cause: ValidationCause) => Promise<ValidationError[]>;
/**
* Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.
*/
validateArrayFieldsStartingFrom: <TField extends DeepKeys<TFormData>>(field: TField, index: number, cause: ValidationCause) => Promise<ValidationError[]>;
/**
* Validates a specified field in the form using the correct handlers for a given validation type.
*/
validateField: <TField extends DeepKeys<TFormData>>(field: TField, cause: ValidationCause) => ValidationError[] | Promise<ValidationError[]>;
/**
* TODO: This code is copied from FieldApi, we should refactor to share
* @private
*/
validateSync: (cause: ValidationCause) => {
hasErrored: boolean;
};
/**
* @private
*/
validateAsync: (cause: ValidationCause) => Promise<ValidationError[]>;
/**
* @private
*/
validate: (cause: ValidationCause) => ValidationError[] | Promise<ValidationError[]>;
/**
* Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
*/
handleSubmit: () => Promise<void>;
/**
* Gets the value of the specified field.
*/
getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>;
/**
* Gets the metadata of the specified field.
*/
getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined;
/**
* Gets the field info of the specified field.
*/
getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => FieldInfo<TFormData, TFormValidator>;
/**
* Updates the metadata of the specified field.
*/
setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void;
resetFieldMeta: <TField extends DeepKeys<TFormData>>(fieldMeta: Record<TField, FieldMeta>) => Record<TField, FieldMeta>;
/**
* Sets the value of the specified field and optionally updates the touched state.
*/
setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {

@@ -121,17 +341,35 @@ touch?: boolean;

deleteField: <TField extends DeepKeys<TFormData>>(field: TField) => void;
/**
* Pushes a value into an array field.
*/
pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
touch?: boolean;
}) => void;
/**
* Inserts a value into an array field at the specified index, shifting the subsequent values to the right.
*/
insertFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
touch?: boolean;
}) => Promise<void>;
/**
* Replaces a value into an array field at the specified index.
*/
replaceFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
touch?: boolean;
}) => Promise<void>;
/**
* Removes a value from an array field at the specified index.
*/
removeFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: {
touch?: boolean;
}) => Promise<void>;
/**
* Swaps the values at the specified indices within an array field.
*/
swapFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number, opts?: {
touch?: boolean;
}) => void;
/**
* Moves the value at the first specified index to the second specified index within an array field.
*/
moveFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number, opts?: {

@@ -138,0 +376,0 @@ touch?: boolean;

@@ -32,2 +32,5 @@ import { Store } from "@tanstack/store";

class FormApi {
/**
* Constructs a new `FormApi` instance with the given form options.
*/
constructor(opts) {

@@ -38,2 +41,3 @@ var _a;

this.prevTransformArray = [];
this._tempDefaultValue = void 0;
this.mount = () => {

@@ -378,2 +382,7 @@ const { onMount } = this.options.validators || {};

this.pushFieldValue = (field, value, opts2) => {
const fieldVal = this.getFieldValue(field) ?? [];
this._tempDefaultValue = {
value,
field: `${field}[${fieldVal.length}]`
};
this.setFieldValue(

@@ -387,2 +396,3 @@ field,

this.insertFieldValue = async (field, index, value, opts2) => {
this._tempDefaultValue = { value, field: `${field}[${index}]` };
this.setFieldValue(

@@ -402,2 +412,3 @@ field,

this.replaceFieldValue = async (field, index, value, opts2) => {
this._tempDefaultValue = { value, field: `${field}[${index}]` };
this.setFieldValue(

@@ -517,2 +528,5 @@ field,

}
/**
* @private
*/
runValidator(props) {

@@ -519,0 +533,0 @@ const adapter = this.options.validatorAdapter;

@@ -5,3 +5,6 @@ import { FormApi } from './FormApi.js';

/**
* @private
*/
export declare function mutateMergeDeep(target: object, source: object): object;
export declare function mergeForm<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined>(baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>, state: Partial<FormApi<TFormData, TFormValidator>['state']>): FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>;
export type ValidationError = undefined | false | null | string;
/**
* If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
* @private
*/
export type Validator<Type, Fn = unknown> = () => {

@@ -10,6 +14,16 @@ validate(options: {

};
/**
* "server" is only intended for SSR/SSG validation and should not execute anything
* @private
*/
export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount' | 'server';
/**
* @private
*/
export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`;
/**
* @private
*/
export type ValidationErrorMap = {
[K in ValidationErrorMapKeys]?: ValidationError;
};
type Nullable<T> = T | null;
type IsNullable<T> = [null] extends [T] ? true : false;
/**
* @private
*/
export type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

@@ -8,5 +11,15 @@ type Narrowable = string | number | bigint | boolean;

};
/**
* @private
*/
export type NoInfer<T> = [T][T extends any ? 0 : never];
/**
* @private
*/
export type Narrow<A> = Try<A, [], NarrowRaw<A>>;
type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch;
/**
* Hack to get TypeScript to show simplified types in error messages
* @private
*/
export type Pretty<T> = {

@@ -30,9 +43,11 @@ [K in keyof T]: T[K];

}[keyof T];
/**
* The keys of an object or array, deeply nested.
*/
export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5 ? never : unknown extends T ? PrefixFromDepth<string, TDepth> : T extends readonly any[] & IsTuple<T> ? PrefixTupleAccessor<T, AllowedIndexes<T>, TDepth> : T extends any[] ? PrefixArrayAccessor<T, [...TDepth, any]> : T extends Date ? never : T extends object ? PrefixObjectAccessor<T, TDepth> : T extends string | number | boolean | bigint ? '' : never;
type PrefixFromDepth<T extends string | number, TDepth extends any[]> = TDepth['length'] extends 0 ? T : `.${T}`;
/**
Infer the type of a deeply nested property within an object or an array.
See the tests in `util-types.test-d.ts` for usage.
*/
* Infer the type of a deeply nested property within an object or an array.
*/
export type DeepValue<TValue, TAccessor, TNullable extends boolean = IsNullable<TValue>> = unknown extends TValue ? TValue : TValue extends ReadonlyArray<any> ? TAccessor extends `[${infer TBrackets}].${infer TAfter}` ? DeepValue<DeepValue<TValue, TBrackets>, TAfter> : TAccessor extends `[${infer TBrackets}]` ? DeepValue<TValue, TBrackets> : TAccessor extends keyof TValue ? TValue[TAccessor] : TValue[TAccessor & number] : TValue extends Record<string | number, any> ? TAccessor extends `${infer TBefore}[${infer TEverythingElse}` ? DeepValue<DeepValue<TValue, TBefore>, `[${TEverythingElse}`> : TAccessor extends `[${infer TBrackets}]` ? DeepValue<TValue, TBrackets> : TAccessor extends `${infer TBefore}.${infer TAfter}` ? DeepValue<DeepValue<TValue, TBefore>, TAfter> : TAccessor extends string ? TNullable extends true ? Nullable<TValue[TAccessor]> : TValue[TAccessor] : never : never;
export {};

@@ -7,5 +7,9 @@ import { ValidationCause } from './types.js';

export type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>;
/**
* @private
*/
export declare function functionalUpdate<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput;
/**
* Get a value from an object using a path, including dot notation.
* @private
*/

@@ -15,2 +19,3 @@ export declare function getBy(obj: any, path: any): any;

* Set a value on an object using a path, including dot notation.
* @private
*/

@@ -20,5 +25,12 @@ export declare function setBy(obj: any, _path: any, updater: Updater<any>): any;

* Delete a field on an object using a path, including dot notation.
* @private
*/
export declare function deleteBy(obj: any, _path: any): any;
/**
* @private
*/
export declare function makePathArray(str: string): (string | number)[];
/**
* @private
*/
export declare function isNonEmptyArray(obj: any): boolean;

@@ -29,2 +41,5 @@ interface AsyncValidatorArrayPartialOptions<T> {

}
/**
* @private
*/
export interface AsyncValidator<T> {

@@ -35,2 +50,5 @@ cause: ValidationCause;

}
/**
* @private
*/
export declare function getAsyncValidatorArray<T>(cause: ValidationCause, options: AsyncValidatorArrayPartialOptions<T>): T extends FieldValidators<any, any> ? Array<AsyncValidator<T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']>> : T extends FormValidators<any, any> ? Array<AsyncValidator<T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']>> : never;

@@ -40,2 +58,5 @@ interface SyncValidatorArrayPartialOptions<T> {

}
/**
* @private
*/
export interface SyncValidator<T> {

@@ -45,3 +66,6 @@ cause: ValidationCause;

}
/**
* @private
*/
export declare function getSyncValidatorArray<T>(cause: ValidationCause, options: SyncValidatorArrayPartialOptions<T>): T extends FieldValidators<any, any> ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>> : T extends FormValidators<any, any> ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>> : never;
export {};

2

package.json
{
"name": "@tanstack/form-core",
"version": "0.23.2",
"version": "0.23.3",
"description": "Powerful, type-safe, framework agnostic forms.",

@@ -5,0 +5,0 @@ "author": "tannerlinsley",

@@ -13,2 +13,5 @@ import { Store } from '@tanstack/store'

/**
* @private
*/
export type FieldValidateFn<

@@ -29,2 +32,5 @@ TParentData,

/**
* @private
*/
export type FieldValidateOrFn<

@@ -68,2 +74,5 @@ TParentData,

/**
* @private
*/
export type FieldValidateAsyncFn<

@@ -85,2 +94,5 @@ TParentData,

/**
* @private
*/
export type FieldAsyncValidateOrFn<

@@ -135,2 +147,5 @@ TParentData,

> {
/**
* An optional function that takes a param of `formApi` which is a generic type of `TData` and `TParentData`
*/
onMount?: FieldValidateOrFn<

@@ -143,2 +158,8 @@ TParentData,

>
/**
* An optional property that takes a `ValidateFn` which is a generic of `TData` and `TParentData`.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onChange?: FieldValidateOrFn<

@@ -151,2 +172,8 @@ TParentData,

>
/**
* An optional property similar to `onChange` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onChangeAsync?: FieldAsyncValidateOrFn<

@@ -159,4 +186,18 @@ TParentData,

>
/**
* An optional number to represent how long the `onChangeAsync` should wait before running
*
* If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
*/
onChangeAsyncDebounceMs?: number
/**
* An optional list of field names that should trigger this field's `onChange` and `onChangeAsync` events when its value changes
*/
onChangeListenTo?: DeepKeys<TParentData>[]
/**
* An optional function, that when run when subscribing to blur event of input.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onBlur?: FieldValidateOrFn<

@@ -169,2 +210,8 @@ TParentData,

>
/**
* An optional property similar to `onBlur` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onBlurAsync?: FieldAsyncValidateOrFn<

@@ -177,4 +224,19 @@ TParentData,

>
/**
* An optional number to represent how long the `onBlurAsync` should wait before running
*
* If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
*/
onBlurAsyncDebounceMs?: number
/**
* An optional list of field names that should trigger this field's `onBlur` and `onBlurAsync` events when its value changes
*/
onBlurListenTo?: DeepKeys<TParentData>[]
/**
* An optional function, that when run when subscribing to submit event of input.
* If `validatorAdapter` is passed, this may also accept a property from the respective adapter
*
* @example `z.string().min(1)` if `zodAdapter` is passed
*/
onSubmit?: FieldValidateOrFn<

@@ -187,2 +249,8 @@ TParentData,

>
/**
* An optional property similar to `onSubmit` but async validation. If `validatorAdapter`
* is passed, this may also accept a property from the respective adapter
*
* @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
*/
onSubmitAsync?: FieldAsyncValidateOrFn<

@@ -197,2 +265,5 @@ TParentData,

/**
* An object type representing the options for a field in a form.
*/
export interface FieldOptions<

@@ -209,8 +280,26 @@ TParentData,

> {
/**
* The field name. The type will be `DeepKeys<TParentData>` to ensure your name is a deep key of the parent dataset.
*/
name: TName
/**
* An optional default value for the field.
*/
defaultValue?: NoInfer<TData>
/**
* The default time to debounce async validation if there is not a more specific debounce time passed.
*/
asyncDebounceMs?: number
/**
* If `true`, always run async validation, even if there are errors emitted during synchronous validation.
*/
asyncAlways?: boolean
preserveValue?: boolean
/**
* A validator provided by an extension, like `yupValidator` from `@tanstack/yup-form-adapter`
*/
validatorAdapter?: TFieldValidator
/**
* A list of validators to pass to the field
*/
validators?: FieldValidators<

@@ -223,5 +312,11 @@ TParentData,

>
/**
* An optional object with default metadata for the field.
*/
defaultMeta?: Partial<FieldMeta>
}
/**
* An object type representing the required options for the FieldApi class.
*/
export interface FieldApiOptions<

@@ -247,21 +342,59 @@ TParentData,

/**
* An object type representing the metadata of a field in a form.
*/
export type FieldMeta = {
/**
* A flag indicating whether the field has been touched.
*/
isTouched: boolean
/**
* A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`.
*/
isPristine: boolean
/**
* A flag that is `true` if the field's value has been modified by the user. Opposite of `isPristine`.
*/
isDirty: boolean
/**
* An array of errors related to the touched state of the field.
*/
touchedErrors: ValidationError[]
/**
* An array of errors related to the field value.
*/
errors: ValidationError[]
/**
* A map of errors related to the field value.
*/
errorMap: ValidationErrorMap
/**
* A flag indicating whether the field is currently being validated.
*/
isValidating: boolean
}
/**
* An object type representing the state of a field.
*/
export type FieldState<TData> = {
/**
* The current value of the field.
*/
value: TData
/**
* The current metadata of the field.
*/
meta: FieldMeta
}
export type ResolveName<TParentData> = unknown extends TParentData
? string
: DeepKeys<TParentData>
/**
* A class representing the API for managing a form field.
*
* Normally, you will not need to create a new `FieldApi` instance directly.
* Instead, you will use a framework hook/function like `useField` or `createField`
* to create a new instance for you that uses your framework's reactivity model.
* However, if you need to create a new instance manually, you can do so by calling
* the `new FieldApi` constructor.
*/
export class FieldApi<

@@ -278,2 +411,5 @@ TParentData,

> {
/**
* A reference to the form API instance.
*/
form: FieldApiOptions<

@@ -286,3 +422,9 @@ TParentData,

>['form']
/**
* The field name.
*/
name!: DeepKeys<TParentData>
/**
* The field options.
*/
options: FieldApiOptions<

@@ -295,6 +437,18 @@ TParentData,

> = {} as any
/**
* The field state store.
*/
store!: Store<FieldState<TData>>
/**
* The current field state.
*/
state!: FieldState<TData>
/**
* @private
*/
prevState!: FieldState<TData>
/**
* Initializes a new `FieldApi` instance.
*/
constructor(

@@ -312,2 +466,10 @@ opts: FieldApiOptions<

if (
opts.defaultValue === undefined &&
this.form._tempDefaultValue !== undefined &&
this.form._tempDefaultValue.field === this.name
) {
opts.defaultValue = this.form._tempDefaultValue.value as never
}
if (opts.defaultValue !== undefined) {

@@ -348,2 +510,13 @@ this.form.setFieldValue(this.name, opts.defaultValue as never)

this.state = state
// Cleanup the temp value after this "tick"
// (Everything occurs sync otherwise)
setTimeout(() => {
if (
this.form._tempDefaultValue !== undefined &&
this.form._tempDefaultValue.field === this.name
) {
this.form._tempDefaultValue = undefined
}
}, 0)
},

@@ -358,2 +531,5 @@ },

/**
* @private
*/
runValidator<

@@ -385,2 +561,5 @@ TValue extends { value: TData; fieldApi: FieldApi<any, any, any, any> },

/**
* Mounts the field instance to the form.
*/
mount = () => {

@@ -434,2 +613,5 @@ const info = this.getInfo()

/**
* Updates the field instance with new options.
*/
update = (

@@ -461,5 +643,16 @@ opts: FieldApiOptions<

if (
opts.defaultValue === undefined &&
this.form._tempDefaultValue !== undefined &&
this.form._tempDefaultValue.field === this.name
) {
opts.defaultValue = this.form._tempDefaultValue.value as never
}
this.options = opts as never
}
/**
* Gets the current field value.
*/
getValue = (): TData => {

@@ -469,2 +662,5 @@ return this.form.getFieldValue(this.name) as TData

/**
* Sets the field value and run the `change` validator.
*/
setValue = (

@@ -478,3 +674,10 @@ updater: Updater<TData>,

/**
* @private
*/
_getMeta = () => this.form.getFieldMeta(this.name)
/**
* Gets the current field metadata.
*/
getMeta = () =>

@@ -493,7 +696,16 @@ this._getMeta() ??

/**
* Sets the field metadata.
*/
setMeta = (updater: Updater<FieldMeta>) =>
this.form.setFieldMeta(this.name, updater)
/**
* Gets the field information object.
*/
getInfo = () => this.form.getFieldInfo(this.name)
/**
* Pushes a new value to the field.
*/
pushValue = (

@@ -504,2 +716,5 @@ value: TData extends any[] ? TData[number] : never,

/**
* Inserts a value at the specified index, shifting the subsequent values to the right.
*/
insertValue = (

@@ -511,2 +726,5 @@ index: number,

/**
* Replaces a value at the specified index.
*/
replaceValue = (

@@ -518,11 +736,23 @@ index: number,

/**
* Removes a value at the specified index.
*/
removeValue = (index: number, opts?: { touch: boolean }) =>
this.form.removeFieldValue(this.name, index, opts)
/**
* Swaps the values at the specified indices.
*/
swapValues = (aIndex: number, bIndex: number, opts?: { touch?: boolean }) =>
this.form.swapFieldValues(this.name, aIndex, bIndex, opts)
/**
* Moves the value at the first specified index to the second specified index.
*/
moveValue = (aIndex: number, bIndex: number, opts?: { touch?: boolean }) =>
this.form.moveFieldValues(this.name, aIndex, bIndex, opts)
/**
* @private
*/
getLinkedFields = (cause: ValidationCause) => {

@@ -553,2 +783,5 @@ const fields = Object.values(this.form.fieldInfo) as FieldInfo<

/**
* @private
*/
validateSync = (cause: ValidationCause) => {

@@ -631,2 +864,5 @@ const validates = getSyncValidatorArray(cause, this.options)

/**
* @private
*/
validateAsync = async (cause: ValidationCause) => {

@@ -752,2 +988,5 @@ const validates = getAsyncValidatorArray(cause, this.options)

/**
* Validates the field value.
*/
validate = (

@@ -773,2 +1012,5 @@ cause: ValidationCause,

/**
* Handles the change event.
*/
handleChange = (updater: Updater<TData>) => {

@@ -778,2 +1020,5 @@ this.setValue(updater, { touch: true })

/**
* Handles the blur event.
*/
handleBlur = () => {

@@ -780,0 +1025,0 @@ const prevTouched = this.state.meta.isTouched

@@ -22,2 +22,5 @@ import { Store } from '@tanstack/store'

/**
* @private
*/
export type FormValidateFn<

@@ -31,2 +34,5 @@ TFormData,

/**
* @private
*/
export type FormValidateOrFn<

@@ -39,2 +45,5 @@ TFormData,

/**
* @private
*/
export type FormValidateAsyncFn<

@@ -49,2 +58,5 @@ TFormData,

/**
* @private
*/
export type FormAsyncValidateOrFn<

@@ -61,8 +73,29 @@ TFormData,

> {
/**
* Optional function that fires as soon as the component mounts.
*/
onMount?: FormValidateOrFn<TFormData, TFormValidator>
/**
* Optional function that checks the validity of your data whenever a value changes
*/
onChange?: FormValidateOrFn<TFormData, TFormValidator>
/**
* Optional onChange asynchronous counterpart to onChange. Useful for more complex validation logic that might involve server requests.
*/
onChangeAsync?: FormAsyncValidateOrFn<TFormData, TFormValidator>
/**
* The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds.
*/
onChangeAsyncDebounceMs?: number
/**
* Optional function that validates the form data when a field loses focus, returns a ValidationError
*/
onBlur?: FormValidateOrFn<TFormData, TFormValidator>
/**
* Optional onBlur asynchronous validation method for when a field loses focus return a `ValidationError` or a promise of `Promise<ValidationError>`
*/
onBlurAsync?: FormAsyncValidateOrFn<TFormData, TFormValidator>
/**
* The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds.
*/
onBlurAsyncDebounceMs?: number

@@ -73,2 +106,5 @@ onSubmit?: FormValidateOrFn<TFormData, TFormValidator>

/**
* @private
*/
export interface FormTransform<

@@ -84,2 +120,5 @@ TFormData,

/**
* An object representing the options for a form.
*/
export interface FormOptions<

@@ -89,8 +128,29 @@ TFormData,

> {
/**
* Set initial values for your form.
*/
defaultValues?: TFormData
/**
* The default state for the form.
*/
defaultState?: Partial<FormState<TFormData>>
/**
* If true, always run async validation, even when sync validation has produced an error. Defaults to undefined.
*/
asyncAlways?: boolean
/**
* Optional time in milliseconds if you want to introduce a delay before firing off an async action.
*/
asyncDebounceMs?: number
/**
* A validator adapter to support usage of extra validation types (IE: Zod, Yup, or Valibot usage)
*/
validatorAdapter?: TFormValidator
/**
* A list of validators to pass to the form
*/
validators?: FormValidators<TFormData, TFormValidator>
/**
* A function to be called when the form is submitted, what should happen once the user submits a valid form returns `any` or a promise `Promise<any>`
*/
onSubmit?: (props: {

@@ -100,2 +160,5 @@ value: TFormData

}) => any | Promise<any>
/**
* Specify an action for scenarios where the user tries to submit an invalid form.
*/
onSubmitInvalid?: (props: {

@@ -108,6 +171,15 @@ value: TFormData

/**
* An object representing the validation metadata for a field. Not intended for public usage.
*/
export type ValidationMeta = {
/**
* An abort controller stored in memory to cancel previous async validation attempts.
*/
lastAbortController: AbortController
}
/**
* An object representing the field information for a specific field within the form.
*/
export type FieldInfo<

@@ -117,2 +189,5 @@ TFormData,

> = {
/**
* An instance of the FieldAPI.
*/
instance: FieldApi<

@@ -124,26 +199,83 @@ TFormData,

> | null
/**
* A record of field validation internal handling.
*/
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>
}
/**
* An object representing the current state of the form.
*/
export type FormState<TFormData> = {
/**
* The current values of the form fields.
*/
values: TFormData
// Form Validation
/**
* A boolean indicating if the form is currently validating.
*/
isFormValidating: boolean
/**
* A boolean indicating if the form is valid.
*/
isFormValid: boolean
/**
* The error array for the form itself.
*/
errors: ValidationError[]
/**
* The error map for the form itself.
*/
errorMap: ValidationErrorMap
/**
* An internal mechanism used for keeping track of validation logic in a form.
*/
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>
// Fields
/**
* A record of field metadata for each field in the form.
*/
fieldMeta: Record<DeepKeys<TFormData>, FieldMeta>
/**
* A boolean indicating if any of the form fields are currently validating.
*/
isFieldsValidating: boolean
/**
* A boolean indicating if all the form fields are valid.
*/
isFieldsValid: boolean
/**
* A boolean indicating if the form is currently submitting.
*/
isSubmitting: boolean
// General
/**
* A boolean indicating if any of the form fields have been touched.
*/
isTouched: boolean
/**
* A boolean indicating if any of the form's fields' values have been modified by the user. `True` if the user have modified at least one of the fields. Opposite of `isPristine`.
*/
isDirty: boolean
/**
* A boolean indicating if none of the form's fields' values have been modified by the user. `True` if the user have not modified any of the fields. Opposite of `isDirty`.
*/
isPristine: boolean
/**
* A boolean indicating if the form has been submitted.
*/
isSubmitted: boolean
/**
* A boolean indicating if the form or any of its fields are currently validating.
*/
isValidating: boolean
/**
* A boolean indicating if the form and all its fields are valid.
*/
isValid: boolean
/**
* A boolean indicating if the form can be submitted based on its current state.
*/
canSubmit: boolean
/**
* A counter for tracking the number of submission attempts.
*/
submissionAttempts: number

@@ -183,2 +315,9 @@ }

/**
* A class representing the Form API. It handles the logic and interactions with the form state.
*
* Normally, you will not need to create a new `FormApi` instance directly. Instead, you will use a framework
* hook/function like `useForm` or `createForm` to create a new instance for you that uses your framework's reactivity model.
* However, if you need to create a new instance manually, you can do so by calling the `new FormApi` constructor.
*/
export class FormApi<

@@ -188,13 +327,45 @@ TFormData,

> {
/**
* The options for the form.
*/
options: FormOptions<TFormData, TFormValidator> = {}
/**
* A [TanStack Store instance](https://tanstack.com/store/latest/docs/reference/Store) that keeps track of the form's state.
*/
store!: Store<FormState<TFormData>>
// Do not use __state directly, as it is not reactive.
// Please use form.useStore() utility to subscribe to state
/**
* The current state of the form.
*
* **Note:**
* Do not use `state` directly, as it is not reactive.
* Please use form.useStore() utility to subscribe to state
*/
state!: FormState<TFormData>
// // This carries the context for nested fields
/**
* A record of field information for each field in the form.
*/
fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, TFormValidator>> =
{} as any
/**
* @private
*/
prevTransformArray: unknown[] = []
/**
* @private
* Used to handle the edgecase of `pushFieldValue` not adding a `defaultValue` to the child `FieldAPI`s that are
* subsequently generated from the `pushFieldValue` (and friends)
* @see https://github.com/TanStack/form/issues/704#issuecomment-2184080607
*/
_tempDefaultValue:
| undefined
| {
field: string
value: unknown
} = undefined
/**
* Constructs a new `FormApi` instance with the given form options.
*/
constructor(opts?: FormOptions<TFormData, TFormValidator>) {

@@ -277,2 +448,5 @@ this.store = new Store<FormState<TFormData>>(

/**
* @private
*/
runValidator<

@@ -315,2 +489,5 @@ TValue extends { value: TFormData; formApi: FormApi<any, any> },

/**
* Updates the form options and form state.
*/
update = (options?: FormOptions<TFormData, TFormValidator>) => {

@@ -353,2 +530,5 @@ if (!options) return

/**
* Resets the form state to the default values.
*/
reset = () => {

@@ -366,2 +546,5 @@ const { fieldMeta: currentFieldMeta } = this.state

/**
* Validates all fields in the form using the correct handlers for a given validation type.
*/
validateAllFields = async (cause: ValidationCause) => {

@@ -391,2 +574,5 @@ const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any

/**
* Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.
*/
validateArrayFieldsStartingFrom = async <TField extends DeepKeys<TFormData>>(

@@ -428,2 +614,5 @@ field: TField,

/**
* Validates a specified field in the form using the correct handlers for a given validation type.
*/
validateField = <TField extends DeepKeys<TFormData>>(

@@ -446,3 +635,6 @@ field: TField,

// TODO: This code is copied from FieldApi, we should refactor to share
/**
* TODO: This code is copied from FieldApi, we should refactor to share
* @private
*/
validateSync = (cause: ValidationCause) => {

@@ -504,2 +696,5 @@ const validates = getSyncValidatorArray(cause, this.options)

/**
* @private
*/
validateAsync = async (

@@ -586,2 +781,5 @@ cause: ValidationCause,

/**
* @private
*/
validate = (

@@ -601,8 +799,6 @@ cause: ValidationCause,

/**
* Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
*/
handleSubmit = async () => {
// Check to see that the form and all fields have been touched
// If they have not, touch them all and run validation
// Run form validation
// Submit the form
this.store.setState((old) => ({

@@ -664,2 +860,5 @@ ...old,

/**
* Gets the value of the specified field.
*/
getFieldValue = <TField extends DeepKeys<TFormData>>(

@@ -669,2 +868,5 @@ field: TField,

/**
* Gets the metadata of the specified field.
*/
getFieldMeta = <TField extends DeepKeys<TFormData>>(

@@ -676,2 +878,5 @@ field: TField,

/**
* Gets the field info of the specified field.
*/
getFieldInfo = <TField extends DeepKeys<TFormData>>(

@@ -693,2 +898,5 @@ field: TField,

/**
* Updates the metadata of the specified field.
*/
setFieldMeta = <TField extends DeepKeys<TFormData>>(

@@ -730,2 +938,5 @@ field: TField,

/**
* Sets the value of the specified field and optionally updates the touched state.
*/
setFieldValue = <TField extends DeepKeys<TFormData>>(

@@ -767,2 +978,5 @@ field: TField,

/**
* Pushes a value into an array field.
*/
pushFieldValue = <TField extends DeepKeys<TFormData>>(

@@ -775,2 +989,8 @@ field: TField,

) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const fieldVal = (this.getFieldValue(field) as unknown[]) ?? []
this._tempDefaultValue = {
value,
field: `${field}[${fieldVal.length}]`,
} as never
this.setFieldValue(

@@ -784,2 +1004,5 @@ field,

/**
* Inserts a value into an array field at the specified index, shifting the subsequent values to the right.
*/
insertFieldValue = async <TField extends DeepKeys<TFormData>>(

@@ -793,2 +1016,3 @@ field: TField,

) => {
this._tempDefaultValue = { value, field: `${field}[${index}]` } as never
this.setFieldValue(

@@ -810,2 +1034,5 @@ field,

/**
* Replaces a value into an array field at the specified index.
*/
replaceFieldValue = async <TField extends DeepKeys<TFormData>>(

@@ -819,2 +1046,3 @@ field: TField,

) => {
this._tempDefaultValue = { value, field: `${field}[${index}]` } as never
this.setFieldValue(

@@ -835,2 +1063,5 @@ field,

/**
* Removes a value from an array field at the specified index.
*/
removeFieldValue = async <TField extends DeepKeys<TFormData>>(

@@ -872,2 +1103,5 @@ field: TField,

/**
* Swaps the values at the specified indices within an array field.
*/
swapFieldValues = <TField extends DeepKeys<TFormData>>(

@@ -896,2 +1130,5 @@ field: TField,

/**
* Moves the value at the first specified index to the second specified index within an array field.
*/
moveFieldValues = <TField extends DeepKeys<TFormData>>(

@@ -898,0 +1135,0 @@ field: TField,

@@ -5,2 +5,5 @@ import type { FormApi } from './FormApi'

/**
* @private
*/
export function mutateMergeDeep(target: object, source: object): object {

@@ -7,0 +10,0 @@ const targetKeys = Object.keys(target)

export type ValidationError = undefined | false | null | string
// If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
/**
* If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
* @private
*/
export type Validator<Type, Fn = unknown> = () => {

@@ -9,9 +12,18 @@ validate(options: { value: Type }, fn: Fn): ValidationError

// "server" is only intended for SSR/SSG validation and should not execute anything
/**
* "server" is only intended for SSR/SSG validation and should not execute anything
* @private
*/
export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount' | 'server'
/**
* @private
*/
export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`
/**
* @private
*/
export type ValidationErrorMap = {
[K in ValidationErrorMapKeys]?: ValidationError
}
type Nullable<T> = T | null
type IsNullable<T> = [null] extends [T] ? true : false
/**
* @private
*/
export type RequiredByKey<T, K extends keyof T> = Omit<T, K> &

@@ -16,4 +19,10 @@ Required<Pick<T, K>>

/**
* @private
*/
export type NoInfer<T> = [T][T extends any ? 0 : never]
/**
* @private
*/
export type Narrow<A> = Try<A, [], NarrowRaw<A>>

@@ -23,3 +32,6 @@

// Hack to get TypeScript to show simplified types in error messages
/**
* Hack to get TypeScript to show simplified types in error messages
* @private
*/
export type Pretty<T> = { [K in keyof T]: T[K] } & {}

@@ -72,2 +84,5 @@

/**
* The keys of an object or array, deeply nested.
*/
export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5

@@ -95,5 +110,4 @@ ? never

/**
Infer the type of a deeply nested property within an object or an array.
See the tests in `util-types.test-d.ts` for usage.
*/
* Infer the type of a deeply nested property within an object or an array.
*/
export type DeepValue<

@@ -100,0 +114,0 @@ // The object or array in which we have the property whose type we're trying to infer

@@ -11,2 +11,5 @@ import type { ValidationCause } from './types'

/**
* @private
*/
export function functionalUpdate<TInput, TOutput = TInput>(

@@ -23,2 +26,3 @@ updater: Updater<TInput, TOutput>,

* Get a value from an object using a path, including dot notation.
* @private
*/

@@ -38,2 +42,3 @@ export function getBy(obj: any, path: any) {

* Set a value on an object using a path, including dot notation.
* @private
*/

@@ -81,2 +86,3 @@ export function setBy(obj: any, _path: any, updater: Updater<any>) {

* Delete a field on an object using a path, including dot notation.
* @private
*/

@@ -137,2 +143,5 @@ export function deleteBy(obj: any, _path: any) {

/**
* @private
*/
export function makePathArray(str: string) {

@@ -160,2 +169,5 @@ if (typeof str !== 'string') {

/**
* @private
*/
export function isNonEmptyArray(obj: any) {

@@ -170,2 +182,5 @@ return !(Array.isArray(obj) && obj.length === 0)

/**
* @private
*/
export interface AsyncValidator<T> {

@@ -177,2 +192,5 @@ cause: ValidationCause

/**
* @private
*/
export function getAsyncValidatorArray<T>(

@@ -251,2 +269,5 @@ cause: ValidationCause,

/**
* @private
*/
export interface SyncValidator<T> {

@@ -257,2 +278,5 @@ cause: ValidationCause

/**
* @private
*/
export function getSyncValidatorArray<T>(

@@ -259,0 +283,0 @@ cause: ValidationCause,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc