From f461a647c080868cef04f9466d5845c115d31d12 Mon Sep 17 00:00:00 2001 From: joaoGabriel55 Date: Mon, 15 Dec 2025 15:14:22 -0300 Subject: [PATCH 1/5] fix: Add support for file values with uuid in setFieldValue --- packages/form-core/src/FormApi.ts | 7 +++++- packages/form-core/tests/FormApi.spec.ts | 31 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 2247e0590..cfd5e050c 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2249,6 +2249,7 @@ export class FormApi< const dontUpdateMeta = opts?.dontUpdateMeta ?? false const dontRunListeners = opts?.dontRunListeners ?? false const dontValidate = opts?.dontValidate ?? false + const isFile = updater instanceof File batch(() => { if (!dontUpdateMeta) { @@ -2267,7 +2268,11 @@ export class FormApi< this.baseStore.setState((prev) => { return { ...prev, - values: setBy(prev.values, field, updater), + values: setBy( + prev.values, + field, + isFile ? { file: updater, uuid: uuid() } : updater, + ), } }) }) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index dd418d727..ce127b2bd 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -4096,6 +4096,37 @@ it('should generate a formId if not provided', () => { expect(form.formId.length).toBeGreaterThan(1) }) +it('it should set a file value with uuid when setting a field value with File', () => { + const form = new FormApi({ + defaultValues: { + avatar: undefined, + } as { avatar: unknown }, + }) + + form.mount() + + const firstFile = new File(['first'], 'first.png', { type: 'image/png' }) + form.setFieldValue('avatar', firstFile) + + const firstValue = form.state.values.avatar as { file: File; uuid: string } + + expect(firstValue).toBeDefined() + expect(firstValue.file instanceof File).toBe(true) + expect(firstValue.file.name).toBe('first.png') + expect(typeof firstValue.uuid).toBe('string') + expect(firstValue.uuid.length > 0).toBe(true) + + const secondFile = new File(['second'], 'second.png', { type: 'image/png' }) + form.setFieldValue('avatar', secondFile) + + const secondValue = form.state.values.avatar as { file: File; uuid: string } + + expect(secondValue.file.name).toBe('second.png') + expect(typeof secondValue.uuid).toBe('string') + expect(secondValue.uuid.length > 0).toBe(true) + expect(secondValue.uuid).not.toBe(firstValue.uuid) +}) + describe('form api event client', () => { it('should have debug disabled', () => { const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) From 52899a3546c977d8fddbb42d3ffbef1557308e53 Mon Sep 17 00:00:00 2001 From: joaoGabriel55 Date: Mon, 15 Dec 2025 16:44:26 -0300 Subject: [PATCH 2/5] test: Fix test descriptions and assertions for file value with uuid --- packages/form-core/tests/FormApi.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index ce127b2bd..e77bdc1c2 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -4096,7 +4096,7 @@ it('should generate a formId if not provided', () => { expect(form.formId.length).toBeGreaterThan(1) }) -it('it should set a file value with uuid when setting a field value with File', () => { +it('should set a file value with uuid when setting a field value with File', () => { const form = new FormApi({ defaultValues: { avatar: undefined, @@ -4114,7 +4114,7 @@ it('it should set a file value with uuid when setting a field value with File', expect(firstValue.file instanceof File).toBe(true) expect(firstValue.file.name).toBe('first.png') expect(typeof firstValue.uuid).toBe('string') - expect(firstValue.uuid.length > 0).toBe(true) + expect(firstValue.uuid.length).toBeGreaterThan(0) const secondFile = new File(['second'], 'second.png', { type: 'image/png' }) form.setFieldValue('avatar', secondFile) @@ -4123,7 +4123,7 @@ it('it should set a file value with uuid when setting a field value with File', expect(secondValue.file.name).toBe('second.png') expect(typeof secondValue.uuid).toBe('string') - expect(secondValue.uuid.length > 0).toBe(true) + expect(secondValue.uuid.length).toBeGreaterThan(0) expect(secondValue.uuid).not.toBe(firstValue.uuid) }) From e52c1a66ed40db2944a6b61dc55a1f2b7698db8a Mon Sep 17 00:00:00 2001 From: joaoGabriel55 Date: Tue, 16 Dec 2025 11:17:02 -0300 Subject: [PATCH 3/5] Move file handling logic from FormApi to setBy utility --- packages/form-core/src/FormApi.ts | 3 +-- packages/form-core/src/utils.ts | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index cfd5e050c..c2bd1067c 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2249,7 +2249,6 @@ export class FormApi< const dontUpdateMeta = opts?.dontUpdateMeta ?? false const dontRunListeners = opts?.dontRunListeners ?? false const dontValidate = opts?.dontValidate ?? false - const isFile = updater instanceof File batch(() => { if (!dontUpdateMeta) { @@ -2271,7 +2270,7 @@ export class FormApi< values: setBy( prev.values, field, - isFile ? { file: updater, uuid: uuid() } : updater, + updater ), } }) diff --git a/packages/form-core/src/utils.ts b/packages/form-core/src/utils.ts index 788c7c14b..bb0299970 100644 --- a/packages/form-core/src/utils.ts +++ b/packages/form-core/src/utils.ts @@ -48,11 +48,16 @@ export function getBy(obj: unknown, path: string | (string | number)[]): any { * @private */ export function setBy(obj: any, _path: any, updater: Updater) { + const isFile = updater instanceof File + const path = makePathArray(_path) function doSet(parent?: any): any { if (!path.length) { - return functionalUpdate(updater, parent) + return functionalUpdate( + isFile ? { file: updater, uuid: uuid() } : updater, + parent, + ) } const key = path.shift() @@ -422,6 +427,15 @@ export const isGlobalFormValidationError = ( } export function evaluate(objA: T, objB: T) { + if (objA instanceof File && objB instanceof File) { + return ( + objA.name === objB.name && + objA.size === objB.size && + objA.type === objB.type && + objA.lastModified === objB.lastModified + ) + } + if (Object.is(objA, objB)) { return true } From 9145eca72da52074fecf19b8da32bf9d5172fcff Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 17:26:46 +0000 Subject: [PATCH 4/5] ci: apply automated fixes and generate docs --- packages/form-core/src/FormApi.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index c2bd1067c..2247e0590 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2267,11 +2267,7 @@ export class FormApi< this.baseStore.setState((prev) => { return { ...prev, - values: setBy( - prev.values, - field, - updater - ), + values: setBy(prev.values, field, updater), } }) }) From 45c3e2abddcaf2a84106afe12826b79a8c55cce9 Mon Sep 17 00:00:00 2001 From: joaoGabriel55 Date: Thu, 18 Dec 2025 11:11:04 -0300 Subject: [PATCH 5/5] patch: Add isFile utility and use it for file value checks --- packages/form-core/src/utils.ts | 18 +++++++++++++++--- packages/form-core/tests/FormApi.spec.ts | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/form-core/src/utils.ts b/packages/form-core/src/utils.ts index bb0299970..38b888fa1 100644 --- a/packages/form-core/src/utils.ts +++ b/packages/form-core/src/utils.ts @@ -43,19 +43,31 @@ export function getBy(obj: unknown, path: string | (string | number)[]): any { }, obj) } +/** + * Check if an object is a File. + * @private + */ +export function isFile(obj: any) { + return ( + obj && + typeof obj === 'object' && + 'name' in obj && + 'size' in obj && + 'type' in obj + ) +} + /** * Set a value on an object using a path, including dot notation. * @private */ export function setBy(obj: any, _path: any, updater: Updater) { - const isFile = updater instanceof File - const path = makePathArray(_path) function doSet(parent?: any): any { if (!path.length) { return functionalUpdate( - isFile ? { file: updater, uuid: uuid() } : updater, + isFile(updater) ? { file: updater, uuid: uuid() } : updater, parent, ) } diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index e77bdc1c2..fb0c3bf8b 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { z } from 'zod' -import { FieldApi, FormApi, formEventClient } from '../src/index' +import { FieldApi, FormApi, isFile } from '../src/index' import { sleep } from './utils' import type { AnyFieldApi, AnyFormApi } from '../src/index' @@ -4111,7 +4111,7 @@ it('should set a file value with uuid when setting a field value with File', () const firstValue = form.state.values.avatar as { file: File; uuid: string } expect(firstValue).toBeDefined() - expect(firstValue.file instanceof File).toBe(true) + expect(isFile(firstValue.file)).toBe(true) expect(firstValue.file.name).toBe('first.png') expect(typeof firstValue.uuid).toBe('string') expect(firstValue.uuid.length).toBeGreaterThan(0)