@tanstack/form-core 0.42.1 → 0.43.2

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.
Files changed (53) hide show
  1. package/dist/cjs/FieldApi.cjs +17 -28
  2. package/dist/cjs/FieldApi.cjs.map +1 -1
  3. package/dist/cjs/FieldApi.d.cts +91 -79
  4. package/dist/cjs/FormApi.cjs +50 -53
  5. package/dist/cjs/FormApi.cjs.map +1 -1
  6. package/dist/cjs/FormApi.d.cts +74 -67
  7. package/dist/cjs/formOptions.cjs.map +1 -1
  8. package/dist/cjs/formOptions.d.cts +2 -3
  9. package/dist/cjs/index.cjs +2 -1
  10. package/dist/cjs/index.cjs.map +1 -1
  11. package/dist/cjs/mergeForm.cjs.map +1 -1
  12. package/dist/cjs/mergeForm.d.cts +1 -2
  13. package/dist/cjs/metaHelper.cjs.map +1 -1
  14. package/dist/cjs/metaHelper.d.cts +2 -3
  15. package/dist/cjs/standardSchemaValidator.cjs +24 -31
  16. package/dist/cjs/standardSchemaValidator.cjs.map +1 -1
  17. package/dist/cjs/standardSchemaValidator.d.cts +24 -4
  18. package/dist/cjs/types.d.cts +15 -27
  19. package/dist/cjs/util-types.d.cts +1 -0
  20. package/dist/cjs/utils.cjs +5 -1
  21. package/dist/cjs/utils.cjs.map +1 -1
  22. package/dist/cjs/utils.d.cts +4 -3
  23. package/dist/esm/FieldApi.d.ts +91 -79
  24. package/dist/esm/FieldApi.js +18 -29
  25. package/dist/esm/FieldApi.js.map +1 -1
  26. package/dist/esm/FormApi.d.ts +74 -67
  27. package/dist/esm/FormApi.js +52 -55
  28. package/dist/esm/FormApi.js.map +1 -1
  29. package/dist/esm/formOptions.d.ts +2 -3
  30. package/dist/esm/formOptions.js.map +1 -1
  31. package/dist/esm/index.js +4 -3
  32. package/dist/esm/mergeForm.d.ts +1 -2
  33. package/dist/esm/mergeForm.js.map +1 -1
  34. package/dist/esm/metaHelper.d.ts +2 -3
  35. package/dist/esm/metaHelper.js.map +1 -1
  36. package/dist/esm/standardSchemaValidator.d.ts +24 -4
  37. package/dist/esm/standardSchemaValidator.js +24 -31
  38. package/dist/esm/standardSchemaValidator.js.map +1 -1
  39. package/dist/esm/types.d.ts +15 -27
  40. package/dist/esm/util-types.d.ts +1 -0
  41. package/dist/esm/utils.d.ts +4 -3
  42. package/dist/esm/utils.js +5 -1
  43. package/dist/esm/utils.js.map +1 -1
  44. package/package.json +2 -2
  45. package/src/FieldApi.ts +803 -273
  46. package/src/FormApi.ts +613 -183
  47. package/src/formOptions.ts +26 -4
  48. package/src/mergeForm.ts +5 -7
  49. package/src/metaHelper.ts +28 -6
  50. package/src/standardSchemaValidator.ts +47 -58
  51. package/src/types.ts +39 -34
  52. package/src/util-types.ts +2 -0
  53. package/src/utils.ts +17 -11
@@ -1,9 +1,31 @@
1
- import type { Validator } from './types'
2
- import type { FormOptions } from './FormApi'
1
+ import type {
2
+ FormAsyncValidateOrFn,
3
+ FormOptions,
4
+ FormValidateOrFn,
5
+ } from './FormApi'
3
6
 
4
7
  export function formOptions<
5
8
  TFormData,
6
- TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
7
- >(defaultOpts?: FormOptions<TFormData, TFormValidator>) {
9
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
10
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
11
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
12
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
13
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
14
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
15
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
16
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
17
+ >(
18
+ defaultOpts?: FormOptions<
19
+ TFormData,
20
+ TOnMount,
21
+ TOnChange,
22
+ TOnChangeAsync,
23
+ TOnBlur,
24
+ TOnBlurAsync,
25
+ TOnSubmit,
26
+ TOnSubmitAsync,
27
+ TOnServer
28
+ >,
29
+ ) {
8
30
  return defaultOpts
9
31
  }
package/src/mergeForm.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { FormApi } from './FormApi'
2
- import type { Validator } from './types'
3
2
  import type { NoInfer } from './util-types'
4
3
 
5
4
  function isValidKey(key: string | number | symbol): boolean {
@@ -72,12 +71,11 @@ export function mutateMergeDeep(
72
71
  return target
73
72
  }
74
73
 
75
- export function mergeForm<
76
- TFormData,
77
- TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
78
- >(
79
- baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>,
80
- state: Partial<FormApi<TFormData, TFormValidator>['state']>,
74
+ export function mergeForm<TFormData>(
75
+ baseForm: FormApi<NoInfer<TFormData>, any, any, any, any, any, any, any, any>,
76
+ state: Partial<
77
+ FormApi<TFormData, any, any, any, any, any, any, any, any>['state']
78
+ >,
81
79
  ) {
82
80
  mutateMergeDeep(baseForm.state, state)
83
81
  return baseForm
package/src/metaHelper.ts CHANGED
@@ -1,14 +1,36 @@
1
- import type { FieldMeta } from './FieldApi'
2
- import type { FormApi } from './FormApi'
3
- import type { Validator } from './types'
1
+ import type {
2
+ FormApi,
3
+ FormAsyncValidateOrFn,
4
+ FormValidateOrFn,
5
+ } from './FormApi'
6
+ import type { AnyFieldMeta } from './FieldApi'
4
7
  import type { DeepKeys } from './util-types'
5
8
 
6
9
  type ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move'
7
10
 
8
11
  export function metaHelper<
9
12
  TFormData,
10
- TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
11
- >(formApi: FormApi<TFormData, TFormValidator>) {
13
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
14
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
15
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
16
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
17
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
18
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
19
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
20
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
21
+ >(
22
+ formApi: FormApi<
23
+ TFormData,
24
+ TOnMount,
25
+ TOnChange,
26
+ TOnChangeAsync,
27
+ TOnBlur,
28
+ TOnBlurAsync,
29
+ TOnSubmit,
30
+ TOnSubmitAsync,
31
+ TOnServer
32
+ >,
33
+ ) {
12
34
  function handleArrayFieldMetaShift(
13
35
  field: DeepKeys<TFormData>,
14
36
  index: number,
@@ -90,7 +112,7 @@ export function metaHelper<
90
112
  })
91
113
  }
92
114
 
93
- const getEmptyFieldMeta = (): FieldMeta => ({
115
+ const getEmptyFieldMeta = (): AnyFieldMeta => ({
94
116
  isValidating: false,
95
117
  isTouched: false,
96
118
  isBlurred: false,
@@ -1,16 +1,11 @@
1
- import type {
2
- ValidationError,
3
- Validator,
4
- ValidatorAdapterParams,
5
- } from './types'
1
+ import type { ValidationSource } from './types'
6
2
 
7
- type Params = ValidatorAdapterParams<StandardSchemaV1Issue>
8
- type TransformFn = NonNullable<Params['transformErrors']>
3
+ export type TStandardSchemaValidatorValue<TData> = {
4
+ value: TData
5
+ validationSource: ValidationSource
6
+ }
9
7
 
10
- function prefixSchemaToErrors(
11
- issues: readonly StandardSchemaV1Issue[],
12
- transformErrors: TransformFn,
13
- ) {
8
+ function prefixSchemaToErrors(issues: readonly StandardSchemaV1Issue[]) {
14
9
  const schema = new Map<string, StandardSchemaV1Issue[]>()
15
10
 
16
11
  for (const issue of issues) {
@@ -28,60 +23,54 @@ function prefixSchemaToErrors(
28
23
  schema.set(path, (schema.get(path) ?? []).concat(issue))
29
24
  }
30
25
 
31
- const transformedSchema = {} as Record<string, ValidationError>
32
-
33
- schema.forEach((value, key) => {
34
- transformedSchema[key] = transformErrors(value)
35
- })
36
-
37
- return transformedSchema
26
+ return Object.fromEntries(schema)
38
27
  }
39
28
 
40
- function defaultFormTransformer(transformErrors: TransformFn) {
41
- return (issues: readonly StandardSchemaV1Issue[]) => ({
42
- form: transformErrors(issues as StandardSchemaV1Issue[]),
43
- fields: prefixSchemaToErrors(issues, transformErrors),
44
- })
45
- }
46
-
47
- export const standardSchemaValidator =
48
- (params: Params = {}): Validator<unknown, StandardSchemaV1<any>> =>
49
- () => {
50
- const transformFieldErrors =
51
- params.transformErrors ??
52
- ((issues: StandardSchemaV1Issue[]) =>
53
- issues.map((issue) => issue.message).join(', '))
54
-
55
- const getTransformStrategy = (validationSource: 'form' | 'field') =>
56
- validationSource === 'form'
57
- ? defaultFormTransformer(transformFieldErrors)
58
- : transformFieldErrors
59
-
60
- return {
61
- validate({ value, validationSource }, fn) {
62
- const result = fn['~standard'].validate(value)
29
+ const defaultFieldTransformer = (issues: readonly StandardSchemaV1Issue[]) =>
30
+ issues
63
31
 
64
- if (result instanceof Promise) {
65
- throw new Error('async function passed to sync validator')
66
- }
67
-
68
- if (!result.issues) return
32
+ const defaultFormTransformer = (issues: readonly StandardSchemaV1Issue[]) => {
33
+ const schemaErrors = prefixSchemaToErrors(issues)
34
+ return {
35
+ form: schemaErrors,
36
+ fields: schemaErrors,
37
+ }
38
+ }
69
39
 
70
- const transformer = getTransformStrategy(validationSource)
40
+ const transformIssues = (
41
+ validationSource: 'form' | 'field',
42
+ issues: readonly StandardSchemaV1Issue[],
43
+ ) =>
44
+ validationSource === 'form'
45
+ ? defaultFormTransformer(issues)
46
+ : defaultFieldTransformer(issues)
47
+
48
+ export const standardSchemaValidators = {
49
+ validate(
50
+ { value, validationSource }: TStandardSchemaValidatorValue<unknown>,
51
+ schema: StandardSchemaV1,
52
+ ) {
53
+ const result = schema['~standard'].validate(value)
54
+
55
+ if (result instanceof Promise) {
56
+ throw new Error('async function passed to sync validator')
57
+ }
71
58
 
72
- return transformer(result.issues as StandardSchemaV1Issue[])
73
- },
74
- async validateAsync({ value, validationSource }, fn) {
75
- const result = await fn['~standard'].validate(value)
59
+ if (!result.issues) return
76
60
 
77
- if (!result.issues) return
61
+ return transformIssues(validationSource, result.issues)
62
+ },
63
+ async validateAsync(
64
+ { value, validationSource }: TStandardSchemaValidatorValue<unknown>,
65
+ schema: StandardSchemaV1,
66
+ ) {
67
+ const result = await schema['~standard'].validate(value)
78
68
 
79
- const transformer = getTransformStrategy(validationSource)
69
+ if (!result.issues) return
80
70
 
81
- return transformer(result.issues as StandardSchemaV1Issue[])
82
- },
83
- }
84
- }
71
+ return transformIssues(validationSource, result.issues)
72
+ },
73
+ }
85
74
 
86
75
  export const isStandardSchemaValidator = (
87
76
  validator: unknown,
@@ -152,7 +141,7 @@ interface StandardSchemaV1FailureResult {
152
141
  /**
153
142
  * The issue interface of the failure output.
154
143
  */
155
- interface StandardSchemaV1Issue {
144
+ export interface StandardSchemaV1Issue {
156
145
  /**
157
146
  * The error message of the issue.
158
147
  */
package/src/types.ts CHANGED
@@ -1,32 +1,9 @@
1
1
  import type { DeepKeys } from './util-types'
2
2
 
3
- export type ValidationError = undefined | false | null | string
3
+ export type ValidationError = unknown
4
4
 
5
5
  export type ValidationSource = 'form' | 'field'
6
6
 
7
- /**
8
- * If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
9
- * @private
10
- */
11
- export type Validator<Type, Fn = unknown> = () => {
12
- validate(
13
- options: { value: Type; validationSource: ValidationSource },
14
- fn: Fn,
15
- ): ValidationError | FormValidationError<unknown>
16
- validateAsync(
17
- options: { value: Type; validationSource: ValidationSource },
18
- fn: Fn,
19
- ): Promise<ValidationError | FormValidationError<unknown>>
20
- }
21
-
22
- /**
23
- * Parameters in common for all validator adapters, making it easier to swap adapter
24
- * @private
25
- */
26
- export type ValidatorAdapterParams<TError = unknown> = {
27
- transformErrors?: (errors: TError[]) => ValidationError
28
- }
29
-
30
7
  /**
31
8
  * "server" is only intended for SSR/SSG validation and should not execute anything
32
9
  * @private
@@ -41,17 +18,47 @@ export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`
41
18
  /**
42
19
  * @private
43
20
  */
44
- export type ValidationErrorMap = {
45
- [K in ValidationErrorMapKeys]?: ValidationError
21
+ export type ValidationErrorMap<
22
+ TOnMountReturn = unknown,
23
+ TOnChangeReturn = unknown,
24
+ TOnChangeAsyncReturn = unknown,
25
+ TOnBlurReturn = unknown,
26
+ TOnBlurAsyncReturn = unknown,
27
+ TOnSubmitReturn = unknown,
28
+ TOnSubmitAsyncReturn = unknown,
29
+ TOnServerReturn = unknown,
30
+ > = {
31
+ onMount?: TOnMountReturn
32
+ onChange?: TOnChangeReturn | TOnChangeAsyncReturn
33
+ onBlur?: TOnBlurReturn | TOnBlurAsyncReturn
34
+ onSubmit?: TOnSubmitReturn | TOnSubmitAsyncReturn
35
+ onServer?: TOnServerReturn
46
36
  }
47
37
 
48
38
  /**
49
39
  * @private
50
40
  */
51
- export type FormValidationErrorMap = {
52
- [K in ValidationErrorMapKeys]?: ValidationError | FormValidationError<unknown>
41
+ export type FormValidationErrorMap<
42
+ TOnMountReturn = unknown,
43
+ TOnChangeReturn = unknown,
44
+ TOnChangeAsyncReturn = unknown,
45
+ TOnBlurReturn = unknown,
46
+ TOnBlurAsyncReturn = unknown,
47
+ TOnSubmitReturn = unknown,
48
+ TOnSubmitAsyncReturn = unknown,
49
+ TOnServerReturn = unknown,
50
+ > = {
51
+ onMount?: TOnMountReturn
52
+ onChange?: TOnChangeReturn | TOnChangeAsyncReturn
53
+ onBlur?: TOnBlurReturn | TOnBlurAsyncReturn
54
+ onSubmit?: TOnSubmitReturn | TOnSubmitAsyncReturn
55
+ onServer?: TOnServerReturn
53
56
  }
54
57
 
58
+ export type FormValidationError<TFormData> =
59
+ | ValidationError
60
+ | GlobalFormValidationError<TFormData>
61
+
55
62
  /**
56
63
  * @private
57
64
  *
@@ -65,12 +72,10 @@ export type FormValidationErrorMap = {
65
72
  * }
66
73
  * ````
67
74
  */
68
- export type FormValidationError<TFormData> =
69
- | ValidationError
70
- | {
71
- form?: ValidationError
72
- fields: Partial<Record<DeepKeys<TFormData>, ValidationError>>
73
- }
75
+ export type GlobalFormValidationError<TFormData> = {
76
+ form?: ValidationError
77
+ fields: Partial<Record<DeepKeys<TFormData>, ValidationError>>
78
+ }
74
79
 
75
80
  /**
76
81
  * @private
package/src/util-types.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  type Nullable<T> = T | null
2
2
  type IsNullable<T> = [null] extends [T] ? true : false
3
3
 
4
+ export type UnwrapOneLevelOfArray<T> = T extends (infer U)[] ? U : T
5
+
4
6
  /**
5
7
  * @private
6
8
  */
package/src/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ValidationCause } from './types'
1
+ import type { GlobalFormValidationError, ValidationCause } from './types'
2
2
  import type { FormValidators } from './FormApi'
3
3
  import type { FieldValidators } from './FieldApi'
4
4
 
@@ -152,8 +152,8 @@ export function makePathArray(str: string | Array<string | number>) {
152
152
  }
153
153
 
154
154
  return str
155
- .replaceAll('[', '.')
156
- .replaceAll(']', '')
155
+ .replace(/\[/g, '.')
156
+ .replace(/\]/g, '')
157
157
  .replace(reFindNumbers0, intReplace)
158
158
  .replace(reFindNumbers1, `.${intReplace}.`)
159
159
  .replace(reFindNumbers2, `${intReplace}.`)
@@ -195,11 +195,11 @@ export interface AsyncValidator<T> {
195
195
  export function getAsyncValidatorArray<T>(
196
196
  cause: ValidationCause,
197
197
  options: AsyncValidatorArrayPartialOptions<T>,
198
- ): T extends FieldValidators<any, any>
198
+ ): T extends FieldValidators<any, any, any, any, any, any, any, any, any, any>
199
199
  ? Array<
200
200
  AsyncValidator<T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']>
201
201
  >
202
- : T extends FormValidators<any, any>
202
+ : T extends FormValidators<any, any, any, any, any, any, any, any>
203
203
  ? Array<
204
204
  AsyncValidator<
205
205
  T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']
@@ -214,8 +214,8 @@ export function getAsyncValidatorArray<T>(
214
214
  onBlurAsyncDebounceMs,
215
215
  onChangeAsyncDebounceMs,
216
216
  } = (options.validators || {}) as
217
- | FieldValidators<any, any>
218
- | FormValidators<any, any>
217
+ | FieldValidators<any, any, any, any, any, any, any, any, any, any>
218
+ | FormValidators<any, any, any, any, any, any, any, any>
219
219
 
220
220
  const defaultDebounceMs = asyncDebounceMs ?? 0
221
221
 
@@ -279,11 +279,11 @@ export interface SyncValidator<T> {
279
279
  export function getSyncValidatorArray<T>(
280
280
  cause: ValidationCause,
281
281
  options: SyncValidatorArrayPartialOptions<T>,
282
- ): T extends FieldValidators<any, any>
282
+ ): T extends FieldValidators<any, any, any, any, any, any, any, any, any, any>
283
283
  ? Array<
284
284
  SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit'] | T['onMount']>
285
285
  >
286
- : T extends FormValidators<any, any>
286
+ : T extends FormValidators<any, any, any, any, any, any, any, any>
287
287
  ? Array<
288
288
  SyncValidator<
289
289
  T['onChange'] | T['onBlur'] | T['onSubmit'] | T['onMount']
@@ -291,8 +291,8 @@ export function getSyncValidatorArray<T>(
291
291
  >
292
292
  : never {
293
293
  const { onChange, onBlur, onSubmit, onMount } = (options.validators || {}) as
294
- | FieldValidators<any, any>
295
- | FormValidators<any, any>
294
+ | FieldValidators<any, any, any, any, any, any, any, any, any, any>
295
+ | FormValidators<any, any, any, any, any, any, any, any>
296
296
 
297
297
  const changeValidator = { cause: 'change', validate: onChange } as const
298
298
  const blurValidator = { cause: 'blur', validate: onBlur } as const
@@ -325,6 +325,12 @@ export function getSyncValidatorArray<T>(
325
325
  }
326
326
  }
327
327
 
328
+ export const isGlobalFormValidationError = (
329
+ error: unknown,
330
+ ): error is GlobalFormValidationError<unknown> => {
331
+ return !!error && typeof error === 'object' && 'fields' in error
332
+ }
333
+
328
334
  export function shallow<T>(objA: T, objB: T) {
329
335
  if (Object.is(objA, objB)) {
330
336
  return true