From 647857552b94b870c7199bf6aa100877fd442d05 Mon Sep 17 00:00:00 2001 From: Jim Burbridge Date: Tue, 2 Sep 2025 11:52:57 -0700 Subject: [PATCH] fix(arktype-utils): Ensures keys are set on the correct object in an array --- .changeset/popular-falcons-fly.md | 5 +++++ packages/arktype-utils/CHANGELOG.md | 14 ++++++------ packages/arktype-utils/src/formData.test.ts | 17 +++++++++++++++ packages/arktype-utils/src/formData.ts | 24 ++++++++++++++++----- 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 .changeset/popular-falcons-fly.md diff --git a/.changeset/popular-falcons-fly.md b/.changeset/popular-falcons-fly.md new file mode 100644 index 0000000..7147592 --- /dev/null +++ b/.changeset/popular-falcons-fly.md @@ -0,0 +1,5 @@ +--- +"@jhecht/arktype-utils": patch +--- + +Fixes bug not grouping items correctly diff --git a/packages/arktype-utils/CHANGELOG.md b/packages/arktype-utils/CHANGELOG.md index 9beba20..5678523 100644 --- a/packages/arktype-utils/CHANGELOG.md +++ b/packages/arktype-utils/CHANGELOG.md @@ -19,17 +19,17 @@ ```ts // Test nested objects with dot notation const fd = new FormData(); - fd.append("payments.id1.location", "England"); - fd.append("payments.id1.age", "37"); - fd.append("payments.id2.location", "New York"); + fd.append('payments.id1.location', 'England'); + fd.append('payments.id1.age', '37'); + fd.append('payments.id2.location', 'New York'); const obj = formDataToObject(fd); // Test nested arrays with bracket notation const fd2 = new FormData(); - fd2.append("locations[].name", "England"); - fd2.append("locations[].members[]", "John"); - fd2.append("locations[].members[]", "Joe"); - fd2.append("locations[].name", "France"); + fd2.append('locations[].name', 'England'); + fd2.append('locations[].members[]', 'John'); + fd2.append('locations[].members[]', 'Joe'); + fd2.append('locations[].name', 'France'); const obj2 = formDataToObject(fd2); ``` diff --git a/packages/arktype-utils/src/formData.test.ts b/packages/arktype-utils/src/formData.test.ts index a8df270..a2a1022 100644 --- a/packages/arktype-utils/src/formData.test.ts +++ b/packages/arktype-utils/src/formData.test.ts @@ -118,6 +118,23 @@ describe('formDataToObject', () => { const obj = formDataToObject(fd); expect(obj.file).toBeInstanceOf(File); }); + + it('Should work with complex arrays / object combinations', () => { + const fd = new FormData(); + fd.append('bills[].id', 'id1'); + fd.append('bills[].name', 'Rent'); + fd.append('bills[].id', 'id2'); + fd.append('bills[].name', 'Electricity'); + + const obj = formDataToObject(fd); + + expect(obj).toStrictEqual({ + bills: [ + { id: 'id1', name: 'Rent' }, + { id: 'id2', name: 'Electricity' }, + ], + }); + }); }); describe('formDataToObject - nested keys', () => { diff --git a/packages/arktype-utils/src/formData.ts b/packages/arktype-utils/src/formData.ts index 948b8a1..e937e74 100644 --- a/packages/arktype-utils/src/formData.ts +++ b/packages/arktype-utils/src/formData.ts @@ -171,7 +171,11 @@ export function formDataToObject( // For grouping array objects by their base path const arrayGroups = new Map< string, - { current: Record; arr: Record[] } + { + current: Record; + arr: Record[]; + lastField?: string; + } >(); // For collecting repeated simple keys and top-level arrays const simpleArrays = new Map(); @@ -191,13 +195,23 @@ export function formDataToObject( const arrayIdx = path.findIndex((p) => p === ''); if (arrayIdx !== -1) { const basePath = path.slice(0, arrayIdx).join('.'); - if (!arrayGroups.has(basePath)) - arrayGroups.set(basePath, { current: {}, arr: [] }); + if (!arrayGroups.has(basePath)) { + arrayGroups.set(basePath, { + current: {}, + arr: [], + lastField: undefined, + }); + } const group = arrayGroups.get(basePath)!; - // If this is a new object in the array, push current and start new + const thisField = String(path[arrayIdx + 1]); + // Only push current object when a non-array field repeats + const isArrayField = + (path.length === arrayIdx + 3 && path[arrayIdx + 2] === '') || + String(path[arrayIdx + 1]).endsWith('[]'); if ( Object.keys(group.current).length > 0 && - path[arrayIdx + 1] === 'name' + Object.prototype.hasOwnProperty.call(group.current, thisField) && + !isArrayField ) { group.arr.push(group.current); group.current = {};