New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@tanstack/form-core

Package Overview
Dependencies
Maintainers
2
Versions
129
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.2.0 to 0.3.0

2

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

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

@@ -1,6 +0,6 @@

import type { DeepKeys, DeepValue, Updater } from './utils'
import type { FormApi, ValidationError } from './FormApi'
import { type DeepKeys, type DeepValue, type Updater } from './utils'
import type { FormApi, ValidationError, ValidationErrorMap } from './FormApi'
import { Store } from '@tanstack/store'
export type ValidationCause = 'change' | 'blur' | 'submit'
export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'

@@ -55,4 +55,5 @@ type ValidateFn<TData, TFormData> = (

isTouched: boolean
touchedError?: ValidationError
error?: ValidationError
touchedErrors: ValidationError[]
errors: ValidationError[]
errorMap: ValidationErrorMap
isValidating: boolean

@@ -114,2 +115,5 @@ }

isTouched: false,
touchedErrors: [],
errors: [],
errorMap: {},
...opts.defaultMeta,

@@ -122,5 +126,5 @@ },

state.meta.touchedError = state.meta.isTouched
? state.meta.error
: undefined
state.meta.touchedErrors = state.meta.isTouched
? state.meta.errors
: []

@@ -209,2 +213,5 @@ this.prevState = state

isTouched: false,
touchedErrors: [],
errors: [],
errorMap: {},
...this.options.defaultMeta,

@@ -246,3 +253,2 @@ } as FieldMeta)

cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur
if (!validate) return

@@ -255,12 +261,16 @@

const error = normalizeError(validate(value as never, this as never))
if (this.state.meta.error !== error) {
const errorMapKey = getErrorMapKey(cause)
if (error && this.state.meta.errorMap[errorMapKey] !== error) {
this.setMeta((prev) => ({
...prev,
error,
errors: [...prev.errors, error],
errorMap: {
...prev.errorMap,
[getErrorMapKey(cause)]: error,
},
}))
}
// If a sync error is encountered, cancel any async validation
if (this.state.meta.error) {
// If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation
if (this.state.meta.errorMap[errorMapKey]) {
this.cancelValidateAsync()

@@ -302,5 +312,3 @@ }

: onBlurAsync
if (!validate) return
if (!validate) return []
const debounceMs =

@@ -338,5 +346,5 @@ cause === 'submit'

if (checkLatest()) {
const prevErrors = this.getMeta().errors
try {
const rawError = await validate(value as never, this as never)
if (checkLatest()) {

@@ -347,9 +355,13 @@ const error = normalizeError(rawError)

isValidating: false,
error,
errors: [...prev.errors, error],
errorMap: {
...prev.errorMap,
[getErrorMapKey(cause)]: error,
},
}))
this.getInfo().validationResolve?.(error)
this.getInfo().validationResolve?.([...prevErrors, error])
}
} catch (error) {
if (checkLatest()) {
this.getInfo().validationReject?.(error)
this.getInfo().validationReject?.([...prevErrors, error])
throw error

@@ -366,3 +378,3 @@ }

// Always return the latest validation promise to the caller
return this.getInfo().validationPromise
return this.getInfo().validationPromise ?? []
}

@@ -373,16 +385,15 @@

value?: typeof this._tdata,
): ValidationError | Promise<ValidationError> => {
): ValidationError[] | Promise<ValidationError[]> => {
// If the field is pristine and validatePristine is false, do not validate
if (!this.state.meta.isTouched) return
if (!this.state.meta.isTouched) return []
// Attempt to sync validate first
this.validateSync(value, cause)
// If there is an error, return it, do not attempt async validation
if (this.state.meta.error) {
const errorMapKey = getErrorMapKey(cause)
// If there is an error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation
if (this.getMeta().errorMap[errorMapKey]) {
if (!this.options.asyncAlways) {
return this.state.meta.error
return this.state.meta.errors
}
}
// No error? Attempt async validation

@@ -417,1 +428,14 @@ return this.validateAsync(value, cause)

}
function getErrorMapKey(cause: ValidationCause) {
switch (cause) {
case 'submit':
return 'onSubmit'
case 'change':
return 'onChange'
case 'blur':
return 'onBlur'
case 'mount':
return 'onMount'
}
}
import { Store } from '@tanstack/store'
//
import type { DeepKeys, DeepValue, Updater } from './utils'
import { functionalUpdate, getBy, setBy } from './utils'
import { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'
import type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'

@@ -40,5 +40,5 @@

validationAsyncCount?: number
validationPromise?: Promise<ValidationError>
validationResolve?: (error: ValidationError) => void
validationReject?: (error: unknown) => void
validationPromise?: Promise<ValidationError[]>
validationResolve?: (errors: ValidationError[]) => void
validationReject?: (errors: unknown) => void
}

@@ -48,2 +48,8 @@

export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`
export type ValidationErrorMap = {
[K in ValidationErrorMapKeys]?: ValidationError
}
export type FormState<TData> = {

@@ -122,3 +128,5 @@ values: TData

const isFieldsValid = !fieldMetaValues.some((field) => field?.error)
const isFieldsValid = !fieldMetaValues.some((field) =>
isNonEmptyArray(field?.errors),
)

@@ -198,4 +206,3 @@ const isTouched = fieldMetaValues.some((field) => field?.isTouched)

validateAllFields = async (cause: ValidationCause) => {
const fieldValidationPromises: Promise<ValidationError>[] = [] as any
const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any
this.store.batch(() => {

@@ -202,0 +209,0 @@ void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach(

@@ -33,2 +33,5 @@ import { expect } from 'vitest'

isValidating: false,
touchedErrors: [],
errors: [],
errorMap: {},
})

@@ -48,2 +51,5 @@ })

isValidating: false,
touchedErrors: [],
errors: [],
errorMap: {},
})

@@ -198,5 +204,8 @@ })

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onChange: 'Please enter a different value',
})
})

@@ -225,6 +234,9 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })
await vi.runAllTimersAsync()
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onChange: 'Please enter a different value',
})
})

@@ -255,3 +267,3 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })

@@ -262,3 +274,6 @@ field.setValue('other')

expect(sleepMock).toHaveBeenCalledTimes(1)
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onChange: 'Please enter a different value',
})
})

@@ -289,3 +304,3 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })

@@ -296,3 +311,6 @@ field.setValue('other')

expect(sleepMock).toHaveBeenCalledTimes(1)
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onChange: 'Please enter a different value',
})
})

@@ -320,3 +338,6 @@

field.validate('blur')
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onBlur: 'Please enter a different value',
})
})

@@ -345,7 +366,10 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })
field.validate('blur')
await vi.runAllTimersAsync()
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onBlur: 'Please enter a different value',
})
})

@@ -376,3 +400,3 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })

@@ -384,3 +408,6 @@ field.validate('blur')

expect(sleepMock).toHaveBeenCalledTimes(1)
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onBlur: 'Please enter a different value',
})
})

@@ -411,3 +438,3 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })

@@ -419,3 +446,6 @@ field.validate('blur')

expect(sleepMock).toHaveBeenCalledTimes(1)
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onBlur: 'Please enter a different value',
})
})

@@ -444,9 +474,46 @@

expect(field.getMeta().error).toBeUndefined()
expect(field.getMeta().errors.length).toBe(0)
field.setValue('other', { touch: true })
field.validate('submit')
await vi.runAllTimersAsync()
expect(field.getMeta().error).toBe('Please enter a different value')
expect(field.getMeta().errors).toContain('Please enter a different value')
expect(field.getMeta().errorMap).toMatchObject({
onSubmit: 'Please enter a different value',
})
})
it('should contain multiple errors when running validation onBlur and onChange', () => {
const form = new FormApi({
defaultValues: {
name: 'other',
},
})
const field = new FieldApi({
form,
name: 'name',
onBlur: (value) => {
if (value === 'other') return 'Please enter a different value'
return
},
onChange: (value) => {
if (value === 'other') return 'Please enter a different value'
return
},
})
field.mount()
field.setValue('other', { touch: true })
field.validate('blur')
expect(field.getMeta().errors).toStrictEqual([
'Please enter a different value',
'Please enter a different value',
])
expect(field.getMeta().errorMap).toEqual({
onBlur: 'Please enter a different value',
onChange: 'Please enter a different value',
})
})
it('should handle default value on field using state.value', async () => {

@@ -453,0 +520,0 @@ interface Form {

@@ -104,2 +104,6 @@ export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput

export function isNonEmptyArray(obj: any) {
return !(Array.isArray(obj) && obj.length === 0)
}
export type RequiredByKey<T, K extends keyof T> = Omit<T, K> &

@@ -106,0 +110,0 @@ Required<Pick<T, K>>

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