@tanstack/form-core 0.30.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { DeepKeys } from './util-types.js';
2
2
  export type ValidationError = undefined | false | null | string;
3
+ export type ValidationSource = 'form' | 'field';
3
4
  /**
4
5
  * If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
5
6
  * @private
@@ -7,10 +8,12 @@ export type ValidationError = undefined | false | null | string;
7
8
  export type Validator<Type, Fn = unknown> = () => {
8
9
  validate(options: {
9
10
  value: Type;
10
- }, fn: Fn): ValidationError;
11
+ validationSource: ValidationSource;
12
+ }, fn: Fn): ValidationError | FormValidationError<unknown>;
11
13
  validateAsync(options: {
12
14
  value: Type;
13
- }, fn: Fn): Promise<ValidationError>;
15
+ validationSource: ValidationSource;
16
+ }, fn: Fn): Promise<ValidationError | FormValidationError<unknown>>;
14
17
  };
15
18
  /**
16
19
  * Parameters in common for all validator adapters, making it easier to swap adapter
@@ -34,6 +37,12 @@ export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`;
34
37
  export type ValidationErrorMap = {
35
38
  [K in ValidationErrorMapKeys]?: ValidationError;
36
39
  };
40
+ /**
41
+ * @private
42
+ */
43
+ export type FormValidationErrorMap = {
44
+ [K in ValidationErrorMapKeys]?: ValidationError | FormValidationError<unknown>;
45
+ };
37
46
  /**
38
47
  * @private
39
48
  *
@@ -25,7 +25,7 @@ export declare function deleteBy(obj: any, _path: any): any;
25
25
  /**
26
26
  * @private
27
27
  */
28
- export declare function makePathArray(str: string): (string | number)[];
28
+ export declare function makePathArray(str: string | Array<string | number>): (string | number)[];
29
29
  /**
30
30
  * @private
31
31
  */
package/dist/esm/utils.js CHANGED
@@ -90,6 +90,9 @@ const reFindMultiplePeriods = /\.{2,}/gm;
90
90
  const intPrefix = "__int__";
91
91
  const intReplace = `${intPrefix}$1`;
92
92
  function makePathArray(str) {
93
+ if (Array.isArray(str)) {
94
+ return [...str];
95
+ }
93
96
  if (typeof str !== "string") {
94
97
  throw new Error("Path must be a string.");
95
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { ValidationCause } from './types'\nimport type { FormValidators } from './FormApi'\nimport type { FieldValidators } from './FieldApi'\n\nexport type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\n/**\n * @private\n */\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n * @private\n */\nexport function getBy(obj: any, path: any) {\n const pathObj = makePathArray(path)\n return pathObj.reduce((current: any, pathPart: any) => {\n if (current === null) return null\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n * @private\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n if (parent === null) {\n parent = {}\n }\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (Array.isArray(parent) && key !== undefined) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n return doSet(obj)\n}\n\n/**\n * Delete a field on an object using a path, including dot notation.\n * @private\n */\nexport function deleteBy(obj: any, _path: any) {\n const path = makePathArray(_path)\n\n function doDelete(parent: any): any {\n if (!parent) return\n if (path.length === 1) {\n const finalPath = path[0]!\n if (Array.isArray(parent) && typeof finalPath === 'number') {\n return parent.filter((_, i) => i !== finalPath)\n }\n const { [finalPath]: remove, ...rest } = parent\n return rest\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doDelete(parent[key]),\n }\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n if (key >= parent.length) {\n return parent\n }\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doDelete(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n }\n\n throw new Error('It seems we have created an infinite loop in deleteBy. ')\n }\n\n return doDelete(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\n/**\n * @private\n */\nexport function makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replaceAll('[', '.')\n .replaceAll(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\n/**\n * @private\n */\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\ninterface AsyncValidatorArrayPartialOptions<T> {\n validators?: T\n asyncDebounceMs?: number\n}\n\n/**\n * @private\n */\nexport interface AsyncValidator<T> {\n cause: ValidationCause\n validate: T\n debounceMs: number\n}\n\n/**\n * @private\n */\nexport function getAsyncValidatorArray<T>(\n cause: ValidationCause,\n options: AsyncValidatorArrayPartialOptions<T>,\n): T extends FieldValidators<any, any>\n ? Array<\n AsyncValidator<T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']>\n >\n : T extends FormValidators<any, any>\n ? Array<\n AsyncValidator<\n T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']\n >\n >\n : never {\n const { asyncDebounceMs } = options\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = (options.validators || {}) as\n | FieldValidators<any, any>\n | FormValidators<any, any>\n\n const defaultDebounceMs = asyncDebounceMs ?? 0\n\n const changeValidator = {\n cause: 'change',\n validate: onChangeAsync,\n debounceMs: onChangeAsyncDebounceMs ?? defaultDebounceMs,\n } as const\n\n const blurValidator = {\n cause: 'blur',\n validate: onBlurAsync,\n debounceMs: onBlurAsyncDebounceMs ?? defaultDebounceMs,\n } as const\n\n const submitValidator = {\n cause: 'submit',\n validate: onSubmitAsync,\n debounceMs: 0,\n } as const\n\n const noopValidator = (\n validator:\n | typeof changeValidator\n | typeof blurValidator\n | typeof submitValidator,\n ) => ({ ...validator, debounceMs: 0 }) as const\n\n switch (cause) {\n case 'submit':\n return [\n noopValidator(changeValidator),\n noopValidator(blurValidator),\n submitValidator,\n ] as never\n case 'blur':\n return [blurValidator] as never\n case 'change':\n return [changeValidator] as never\n case 'server':\n default:\n return [] as never\n }\n}\n\ninterface SyncValidatorArrayPartialOptions<T> {\n validators?: T\n}\n\n/**\n * @private\n */\nexport interface SyncValidator<T> {\n cause: ValidationCause\n validate: T\n}\n\n/**\n * @private\n */\nexport function getSyncValidatorArray<T>(\n cause: ValidationCause,\n options: SyncValidatorArrayPartialOptions<T>,\n): T extends FieldValidators<any, any>\n ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>>\n : T extends FormValidators<any, any>\n ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>>\n : never {\n const { onChange, onBlur, onSubmit } = (options.validators || {}) as\n | FieldValidators<any, any>\n | FormValidators<any, any>\n\n const changeValidator = { cause: 'change', validate: onChange } as const\n const blurValidator = { cause: 'blur', validate: onBlur } as const\n const submitValidator = { cause: 'submit', validate: onSubmit } as const\n\n // Allows us to clear onServer errors\n const serverValidator = {\n cause: 'server',\n validate: () => undefined,\n } as const\n\n switch (cause) {\n case 'submit':\n return [\n changeValidator,\n blurValidator,\n submitValidator,\n serverValidator,\n ] as never\n case 'server':\n return [serverValidator] as never\n case 'blur':\n return [blurValidator, serverValidator] as never\n case 'change':\n default:\n return [changeValidator, serverValidator] as never\n }\n}\n"],"names":[],"mappings":"AAagB,SAAA,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAMgB,SAAA,MAAM,KAAU,MAAW;AACnC,QAAA,UAAU,cAAc,IAAI;AAClC,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACjD,QAAA,YAAY,KAAa,QAAA;AACzB,QAAA,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACO,WAAA;AAAA,KACN,GAAG;AACR;AAMgB,SAAA,MAAM,KAAU,OAAY,SAAuB;AAC3D,QAAA,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAC5B,QAAA,CAAC,KAAK,QAAQ;AACT,aAAA,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEM,UAAA,MAAM,KAAK;AAEb,QAAA,OAAO,QAAQ,UAAU;AACvB,UAAA,OAAO,WAAW,UAAU;AAC9B,YAAI,WAAW,MAAM;AACnB,mBAAS,CAAA;AAAA,QACX;AACO,eAAA;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAAA;AAAA,MAE5B;AACO,aAAA;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MAAA;AAAA,IAEjB;AAEA,QAAI,MAAM,QAAQ,MAAM,KAAK,QAAQ,QAAW;AAC9C,YAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAC3B,aAAA;AAAA,QACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,QAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,QACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,MAAA;AAAA,IAE3B;AACA,WAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAO,CAAA;AAAA,EACpC;AAEA,SAAO,MAAM,GAAG;AAClB;AAMgB,SAAA,SAAS,KAAU,OAAY;AACvC,QAAA,OAAO,cAAc,KAAK;AAEhC,WAAS,SAAS,QAAkB;AAClC,QAAI,CAAC,OAAQ;AACT,QAAA,KAAK,WAAW,GAAG;AACf,YAAA,YAAY,KAAK,CAAC;AACxB,UAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,cAAc,UAAU;AAC1D,eAAO,OAAO,OAAO,CAAC,GAAG,MAAM,MAAM,SAAS;AAAA,MAChD;AACA,YAAM,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,SAAS;AAClC,aAAA;AAAA,IACT;AAEM,UAAA,MAAM,KAAK;AAEb,QAAA,OAAO,QAAQ,UAAU;AACvB,UAAA,OAAO,WAAW,UAAU;AACvB,eAAA;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,SAAS,OAAO,GAAG,CAAC;AAAA,QAAA;AAAA,MAE/B;AAAA,IACF;AAEI,QAAA,OAAO,QAAQ,UAAU;AACvB,UAAA,MAAM,QAAQ,MAAM,GAAG;AACrB,YAAA,OAAO,OAAO,QAAQ;AACjB,iBAAA;AAAA,QACT;AACA,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAC3B,eAAA;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,SAAS,OAAO,GAAG,CAAC;AAAA,UACpB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QAAA;AAAA,MAE3B;AAAA,IACF;AAEM,UAAA,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,SAAO,SAAS,GAAG;AACrB;AAEA,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAE9B,MAAM,YAAY;AAClB,MAAM,aAAa,GAAG,SAAS;AAKxB,SAAS,cAAc,KAAa;AACrC,MAAA,OAAO,QAAQ,UAAU;AACrB,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,WAAW,KAAK,GAAG,EACnB,WAAW,KAAK,EAAE,EAClB,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACO,WAAA;AAAA,EAAA,CACR;AACL;AAKO,SAAS,gBAAgB,KAAU;AACxC,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW;AAChD;AAmBgB,SAAA,uBACd,OACA,SAWU;AACJ,QAAA,EAAE,gBAAoB,IAAA;AACtB,QAAA;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACG,QAAQ,cAAc;AAI3B,QAAM,oBAAoB,mBAAmB;AAE7C,QAAM,kBAAkB;AAAA,IACtB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY,2BAA2B;AAAA,EAAA;AAGzC,QAAM,gBAAgB;AAAA,IACpB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY,yBAAyB;AAAA,EAAA;AAGvC,QAAM,kBAAkB;AAAA,IACtB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAGd,QAAM,gBAAgB,CACpB,eAII,EAAE,GAAG,WAAW,YAAY,EAAE;AAEpC,UAAQ,OAAO;AAAA,IACb,KAAK;AACI,aAAA;AAAA,QACL,cAAc,eAAe;AAAA,QAC7B,cAAc,aAAa;AAAA,QAC3B;AAAA,MAAA;AAAA,IAEJ,KAAK;AACH,aAAO,CAAC,aAAa;AAAA,IACvB,KAAK;AACH,aAAO,CAAC,eAAe;AAAA,IACzB,KAAK;AAAA,IACL;AACE,aAAO;EACX;AACF;AAiBgB,SAAA,sBACd,OACA,SAKU;AACV,QAAM,EAAE,UAAU,QAAQ,SAAc,IAAA,QAAQ,cAAc;AAI9D,QAAM,kBAAkB,EAAE,OAAO,UAAU,UAAU,SAAS;AAC9D,QAAM,gBAAgB,EAAE,OAAO,QAAQ,UAAU,OAAO;AACxD,QAAM,kBAAkB,EAAE,OAAO,UAAU,UAAU,SAAS;AAG9D,QAAM,kBAAkB;AAAA,IACtB,OAAO;AAAA,IACP,UAAU,MAAM;AAAA,EAAA;AAGlB,UAAQ,OAAO;AAAA,IACb,KAAK;AACI,aAAA;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ,KAAK;AACH,aAAO,CAAC,eAAe;AAAA,IACzB,KAAK;AACI,aAAA,CAAC,eAAe,eAAe;AAAA,IACxC,KAAK;AAAA,IACL;AACS,aAAA,CAAC,iBAAiB,eAAe;AAAA,EAC5C;AACF;"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import type { ValidationCause } from './types'\nimport type { FormValidators } from './FormApi'\nimport type { FieldValidators } from './FieldApi'\n\nexport type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\n/**\n * @private\n */\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n * @private\n */\nexport function getBy(obj: any, path: any) {\n const pathObj = makePathArray(path)\n return pathObj.reduce((current: any, pathPart: any) => {\n if (current === null) return null\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n * @private\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n if (parent === null) {\n parent = {}\n }\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (Array.isArray(parent) && key !== undefined) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n return doSet(obj)\n}\n\n/**\n * Delete a field on an object using a path, including dot notation.\n * @private\n */\nexport function deleteBy(obj: any, _path: any) {\n const path = makePathArray(_path)\n\n function doDelete(parent: any): any {\n if (!parent) return\n if (path.length === 1) {\n const finalPath = path[0]!\n if (Array.isArray(parent) && typeof finalPath === 'number') {\n return parent.filter((_, i) => i !== finalPath)\n }\n const { [finalPath]: remove, ...rest } = parent\n return rest\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doDelete(parent[key]),\n }\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n if (key >= parent.length) {\n return parent\n }\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doDelete(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n }\n\n throw new Error('It seems we have created an infinite loop in deleteBy. ')\n }\n\n return doDelete(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\n/**\n * @private\n */\nexport function makePathArray(str: string | Array<string | number>) {\n if (Array.isArray(str)) {\n return [...str]\n }\n\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replaceAll('[', '.')\n .replaceAll(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\n/**\n * @private\n */\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\ninterface AsyncValidatorArrayPartialOptions<T> {\n validators?: T\n asyncDebounceMs?: number\n}\n\n/**\n * @private\n */\nexport interface AsyncValidator<T> {\n cause: ValidationCause\n validate: T\n debounceMs: number\n}\n\n/**\n * @private\n */\nexport function getAsyncValidatorArray<T>(\n cause: ValidationCause,\n options: AsyncValidatorArrayPartialOptions<T>,\n): T extends FieldValidators<any, any>\n ? Array<\n AsyncValidator<T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']>\n >\n : T extends FormValidators<any, any>\n ? Array<\n AsyncValidator<\n T['onChangeAsync'] | T['onBlurAsync'] | T['onSubmitAsync']\n >\n >\n : never {\n const { asyncDebounceMs } = options\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = (options.validators || {}) as\n | FieldValidators<any, any>\n | FormValidators<any, any>\n\n const defaultDebounceMs = asyncDebounceMs ?? 0\n\n const changeValidator = {\n cause: 'change',\n validate: onChangeAsync,\n debounceMs: onChangeAsyncDebounceMs ?? defaultDebounceMs,\n } as const\n\n const blurValidator = {\n cause: 'blur',\n validate: onBlurAsync,\n debounceMs: onBlurAsyncDebounceMs ?? defaultDebounceMs,\n } as const\n\n const submitValidator = {\n cause: 'submit',\n validate: onSubmitAsync,\n debounceMs: 0,\n } as const\n\n const noopValidator = (\n validator:\n | typeof changeValidator\n | typeof blurValidator\n | typeof submitValidator,\n ) => ({ ...validator, debounceMs: 0 }) as const\n\n switch (cause) {\n case 'submit':\n return [\n noopValidator(changeValidator),\n noopValidator(blurValidator),\n submitValidator,\n ] as never\n case 'blur':\n return [blurValidator] as never\n case 'change':\n return [changeValidator] as never\n case 'server':\n default:\n return [] as never\n }\n}\n\ninterface SyncValidatorArrayPartialOptions<T> {\n validators?: T\n}\n\n/**\n * @private\n */\nexport interface SyncValidator<T> {\n cause: ValidationCause\n validate: T\n}\n\n/**\n * @private\n */\nexport function getSyncValidatorArray<T>(\n cause: ValidationCause,\n options: SyncValidatorArrayPartialOptions<T>,\n): T extends FieldValidators<any, any>\n ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>>\n : T extends FormValidators<any, any>\n ? Array<SyncValidator<T['onChange'] | T['onBlur'] | T['onSubmit']>>\n : never {\n const { onChange, onBlur, onSubmit } = (options.validators || {}) as\n | FieldValidators<any, any>\n | FormValidators<any, any>\n\n const changeValidator = { cause: 'change', validate: onChange } as const\n const blurValidator = { cause: 'blur', validate: onBlur } as const\n const submitValidator = { cause: 'submit', validate: onSubmit } as const\n\n // Allows us to clear onServer errors\n const serverValidator = {\n cause: 'server',\n validate: () => undefined,\n } as const\n\n switch (cause) {\n case 'submit':\n return [\n changeValidator,\n blurValidator,\n submitValidator,\n serverValidator,\n ] as never\n case 'server':\n return [serverValidator] as never\n case 'blur':\n return [blurValidator, serverValidator] as never\n case 'change':\n default:\n return [changeValidator, serverValidator] as never\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/form-core",
3
- "version": "0.30.0",
3
+ "version": "0.33.0",
4
4
  "description": "Powerful, type-safe, framework agnostic forms.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/FieldApi.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  ValidationCause,
7
7
  ValidationError,
8
8
  ValidationErrorMap,
9
+ ValidationSource,
9
10
  Validator,
10
11
  } from './types'
11
12
  import type { AsyncValidator, SyncValidator, Updater } from './utils'
@@ -335,6 +336,10 @@ export type FieldMeta = {
335
336
  * A flag indicating whether the field has been touched.
336
337
  */
337
338
  isTouched: boolean
339
+ /**
340
+ * A flag indicating whether the field has been blurred.
341
+ */
342
+ isBlurred: boolean
338
343
  /**
339
344
  * A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`.
340
345
  */
@@ -456,6 +461,7 @@ export class FieldApi<
456
461
  meta: this._getMeta() ?? {
457
462
  isValidating: false,
458
463
  isTouched: false,
464
+ isBlurred: false,
459
465
  isDirty: false,
460
466
  isPristine: true,
461
467
  errors: [],
@@ -488,7 +494,11 @@ export class FieldApi<
488
494
  * @private
489
495
  */
490
496
  runValidator<
491
- TValue extends { value: TData; fieldApi: FieldApi<any, any, any, any> },
497
+ TValue extends {
498
+ value: TData
499
+ fieldApi: FieldApi<any, any, any, any>
500
+ validationSource: ValidationSource
501
+ },
492
502
  TType extends 'validate' | 'validateAsync',
493
503
  >(props: {
494
504
  validate: TType extends 'validate'
@@ -496,7 +506,8 @@ export class FieldApi<
496
506
  : FieldAsyncValidateOrFn<any, any, any, any>
497
507
  value: TValue
498
508
  type: TType
499
- }): ReturnType<ReturnType<Validator<any>>[TType]> {
509
+ // When `api` is 'field', the return type cannot be `FormValidationError`
510
+ }): TType extends 'validate' ? ValidationError : Promise<ValidationError> {
500
511
  const adapters = [
501
512
  this.form.options.validatorAdapter,
502
513
  this.options.validatorAdapter,
@@ -543,6 +554,7 @@ export class FieldApi<
543
554
  value: {
544
555
  value: this.state.value,
545
556
  fieldApi: this,
557
+ validationSource: 'field',
546
558
  },
547
559
  type: 'validate',
548
560
  })
@@ -625,6 +637,7 @@ export class FieldApi<
625
637
  ({
626
638
  isValidating: false,
627
639
  isTouched: false,
640
+ isBlurred: false,
628
641
  isDirty: false,
629
642
  isPristine: true,
630
643
  errors: [],
@@ -757,7 +770,11 @@ export class FieldApi<
757
770
  ? normalizeError(
758
771
  field.runValidator({
759
772
  validate: validateObj.validate,
760
- value: { value: field.getValue(), fieldApi: field },
773
+ value: {
774
+ value: field.getValue(),
775
+ validationSource: 'field',
776
+ fieldApi: field,
777
+ },
761
778
  type: 'validate',
762
779
  }),
763
780
  )
@@ -884,6 +901,7 @@ export class FieldApi<
884
901
  value: field.getValue(),
885
902
  fieldApi: field,
886
903
  signal: controller.signal,
904
+ validationSource: 'field',
887
905
  },
888
906
  type: 'validateAsync',
889
907
  }),
@@ -1001,6 +1019,9 @@ export class FieldApi<
1001
1019
  this.setMeta((prev) => ({ ...prev, isTouched: true }))
1002
1020
  this.validate('change')
1003
1021
  }
1022
+ if (!this.state.meta.isBlurred) {
1023
+ this.setMeta((prev) => ({ ...prev, isBlurred: true }))
1024
+ }
1004
1025
  this.validate('blur')
1005
1026
  }
1006
1027
 
package/src/FormApi.ts CHANGED
@@ -13,11 +13,13 @@ import type { DeepKeys, DeepValue } from './util-types'
13
13
  import type { FieldApi, FieldMeta } from './FieldApi'
14
14
  import type {
15
15
  FormValidationError,
16
+ FormValidationErrorMap,
16
17
  UpdateMetaOptions,
17
18
  ValidationCause,
18
19
  ValidationError,
19
20
  ValidationErrorMap,
20
21
  ValidationErrorMapKeys,
22
+ ValidationSource,
21
23
  Validator,
22
24
  } from './types'
23
25
 
@@ -234,7 +236,7 @@ export type FormState<TFormData> = {
234
236
  /**
235
237
  * The error map for the form itself.
236
238
  */
237
- errorMap: ValidationErrorMap
239
+ errorMap: FormValidationErrorMap
238
240
  /**
239
241
  * An internal mechanism used for keeping track of validation logic in a form.
240
242
  */
@@ -259,6 +261,10 @@ export type FormState<TFormData> = {
259
261
  * A boolean indicating if any of the form fields have been touched.
260
262
  */
261
263
  isTouched: boolean
264
+ /**
265
+ * A boolean indicating if any of the form fields have been blurred.
266
+ */
267
+ isBlurred: boolean
262
268
  /**
263
269
  * 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`.
264
270
  */
@@ -305,6 +311,7 @@ function getDefaultFormState<TFormData>(
305
311
  isSubmitted: defaultState.isSubmitted ?? false,
306
312
  isSubmitting: defaultState.isSubmitting ?? false,
307
313
  isTouched: defaultState.isTouched ?? false,
314
+ isBlurred: defaultState.isBlurred ?? false,
308
315
  isPristine: defaultState.isPristine ?? true,
309
316
  isDirty: defaultState.isDirty ?? false,
310
317
  isValid: defaultState.isValid ?? false,
@@ -320,6 +327,12 @@ function getDefaultFormState<TFormData>(
320
327
  }
321
328
  }
322
329
 
330
+ const isFormValidationError = (
331
+ error: unknown,
332
+ ): error is FormValidationError<unknown> => {
333
+ return typeof error === 'object'
334
+ }
335
+
323
336
  /**
324
337
  * A class representing the Form API. It handles the logic and interactions with the form state.
325
338
  *
@@ -388,14 +401,24 @@ export class FormApi<
388
401
  )
389
402
 
390
403
  const isTouched = fieldMetaValues.some((field) => field?.isTouched)
404
+ const isBlurred = fieldMetaValues.some((field) => field?.isBlurred)
391
405
 
392
406
  const isDirty = fieldMetaValues.some((field) => field?.isDirty)
393
407
  const isPristine = !isDirty
394
408
 
395
409
  const isValidating = isFieldsValidating || state.isFormValidating
396
- state.errors = Object.values(state.errorMap).filter(
397
- (val: unknown) => val !== undefined,
398
- )
410
+ state.errors = Object.values(state.errorMap).reduce((prev, curr) => {
411
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
412
+ if (curr === undefined) return prev
413
+ if (typeof curr === 'string') {
414
+ prev.push(curr)
415
+ return prev
416
+ } else if (curr && isFormValidationError(curr)) {
417
+ prev.push(curr.form)
418
+ return prev
419
+ }
420
+ return prev
421
+ }, [] as ValidationError[])
399
422
  const isFormValid = state.errors.length === 0
400
423
  const isValid = isFieldsValid && isFormValid
401
424
  const canSubmit =
@@ -410,6 +433,7 @@ export class FormApi<
410
433
  isValid,
411
434
  canSubmit,
412
435
  isTouched,
436
+ isBlurred,
413
437
  isPristine,
414
438
  isDirty,
415
439
  }
@@ -442,7 +466,11 @@ export class FormApi<
442
466
  * @private
443
467
  */
444
468
  runValidator<
445
- TValue extends { value: TFormData; formApi: FormApi<any, any> },
469
+ TValue extends {
470
+ value: TFormData
471
+ formApi: FormApi<any, any>
472
+ validationSource: ValidationSource
473
+ },
446
474
  TType extends 'validate' | 'validateAsync',
447
475
  >(props: {
448
476
  validate: TType extends 'validate'
@@ -467,6 +495,7 @@ export class FormApi<
467
495
  value: {
468
496
  value: this.state.values,
469
497
  formApi: this,
498
+ validationSource: 'form',
470
499
  },
471
500
  type: 'validate',
472
501
  })
@@ -553,6 +582,12 @@ export class FormApi<
553
582
  // Mark them as touched
554
583
  field.instance.setMeta((prev) => ({ ...prev, isTouched: true }))
555
584
  }
585
+
586
+ // If any fields are not blurred
587
+ if (!field.instance.state.meta.isBlurred) {
588
+ // Mark them as blurred
589
+ field.instance.setMeta((prev) => ({ ...prev, isBlurred: true }))
590
+ }
556
591
  })
557
592
  })
558
593
 
@@ -616,6 +651,12 @@ export class FormApi<
616
651
  fieldInstance.setMeta((prev) => ({ ...prev, isTouched: true }))
617
652
  }
618
653
 
654
+ // If the field is not blurred (same logic as in validateAllFields)
655
+ if (!fieldInstance.state.meta.isBlurred) {
656
+ // Mark it as blurred
657
+ fieldInstance.setMeta((prev) => ({ ...prev, isBlurred: true }))
658
+ }
659
+
619
660
  return fieldInstance.validate(cause)
620
661
  }
621
662
 
@@ -643,6 +684,7 @@ export class FormApi<
643
684
  value: {
644
685
  value: this.state.values,
645
686
  formApi: this,
687
+ validationSource: 'form',
646
688
  },
647
689
  type: 'validate',
648
690
  })
@@ -748,7 +790,10 @@ export class FormApi<
748
790
 
749
791
  promises.push(
750
792
  new Promise<ValidationPromiseResult<TFormData>>(async (resolve) => {
751
- let rawError!: ValidationError | undefined
793
+ let rawError!:
794
+ | ValidationError
795
+ | FormValidationError<unknown>
796
+ | undefined
752
797
  try {
753
798
  rawError = await new Promise((rawResolve, rawReject) => {
754
799
  setTimeout(async () => {
@@ -760,6 +805,7 @@ export class FormApi<
760
805
  value: {
761
806
  value: this.state.values,
762
807
  formApi: this,
808
+ validationSource: 'form',
763
809
  signal: controller.signal,
764
810
  },
765
811
  type: 'validateAsync',
@@ -983,6 +1029,7 @@ export class FormApi<
983
1029
  acc[fieldKey] = {
984
1030
  isValidating: false,
985
1031
  isTouched: false,
1032
+ isBlurred: false,
986
1033
  isDirty: false,
987
1034
  isPristine: true,
988
1035
  errors: [],
@@ -1009,6 +1056,7 @@ export class FormApi<
1009
1056
  this.setFieldMeta(field, (prev) => ({
1010
1057
  ...prev,
1011
1058
  isTouched: true,
1059
+ isBlurred: true,
1012
1060
  isDirty: true,
1013
1061
  }))
1014
1062
  }
@@ -1207,7 +1255,7 @@ export class FormApi<
1207
1255
  }
1208
1256
  }
1209
1257
 
1210
- function normalizeError<TFormData>(rawError?: FormValidationError<TFormData>): {
1258
+ function normalizeError<TFormData>(rawError?: FormValidationError<unknown>): {
1211
1259
  formError: ValidationError
1212
1260
  fieldErrors?: Partial<Record<DeepKeys<TFormData>, ValidationError>>
1213
1261
  } {
@@ -1215,7 +1263,7 @@ function normalizeError<TFormData>(rawError?: FormValidationError<TFormData>): {
1215
1263
  if (typeof rawError === 'object') {
1216
1264
  const formError = normalizeError(rawError.form).formError
1217
1265
  const fieldErrors = rawError.fields
1218
- return { formError, fieldErrors }
1266
+ return { formError, fieldErrors } as never
1219
1267
  }
1220
1268
 
1221
1269
  if (typeof rawError !== 'string') {
package/src/types.ts CHANGED
@@ -1,14 +1,22 @@
1
- import { type DeepKeys } from './util-types'
1
+ import type { DeepKeys } from './util-types'
2
2
 
3
3
  export type ValidationError = undefined | false | null | string
4
4
 
5
+ export type ValidationSource = 'form' | 'field'
6
+
5
7
  /**
6
8
  * If/when TypeScript supports higher-kinded types, this should not be `unknown` anymore
7
9
  * @private
8
10
  */
9
11
  export type Validator<Type, Fn = unknown> = () => {
10
- validate(options: { value: Type }, fn: Fn): ValidationError
11
- validateAsync(options: { value: Type }, fn: Fn): Promise<ValidationError>
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>>
12
20
  }
13
21
 
14
22
  /**
@@ -37,6 +45,13 @@ export type ValidationErrorMap = {
37
45
  [K in ValidationErrorMapKeys]?: ValidationError
38
46
  }
39
47
 
48
+ /**
49
+ * @private
50
+ */
51
+ export type FormValidationErrorMap = {
52
+ [K in ValidationErrorMapKeys]?: ValidationError | FormValidationError<unknown>
53
+ }
54
+
40
55
  /**
41
56
  * @private
42
57
  *
package/src/utils.ts CHANGED
@@ -139,7 +139,11 @@ const intReplace = `${intPrefix}$1`
139
139
  /**
140
140
  * @private
141
141
  */
142
- export function makePathArray(str: string) {
142
+ export function makePathArray(str: string | Array<string | number>) {
143
+ if (Array.isArray(str)) {
144
+ return [...str]
145
+ }
146
+
143
147
  if (typeof str !== 'string') {
144
148
  throw new Error('Path must be a string.')
145
149
  }