From 03b17285f16d55a7ae0f1b845f4caf0dbf306f28 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:19:29 +0000 Subject: [PATCH 1/3] fix(export-workbook): ensure all schema fields are exported even when record values are missing The export plugin was iterating over Object.keys(record.values) which only included fields that were present in the record values. This caused columns defined in the sheet schema but missing from record values to be omitted from the export. Changed to iterate over sheet.config.fields instead, ensuring all schema fields are exported. Missing values are now exported as empty cells. Fixes: IA-294777 Co-Authored-By: christopher.harrison@flatfile.io --- plugins/export-workbook/src/plugin.ts | 53 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/plugins/export-workbook/src/plugin.ts b/plugins/export-workbook/src/plugin.ts index f5ed3303..da4c7e80 100644 --- a/plugins/export-workbook/src/plugin.ts +++ b/plugins/export-workbook/src/plugin.ts @@ -73,31 +73,42 @@ export const exportRecords = async ( const processedRecords = await Promise.all( records.map(async (record: Flatfile.RecordWithLinks) => { const { id: recordId, values: row } = record + + const formatCell = (cellValue?: Flatfile.CellValue) => { + if (!cellValue) { + return { + t: 's', + v: '', + c: [], + } as XLSX.CellObject + } + + const { value, messages } = cellValue + const cell: XLSX.CellObject = { + t: 's', + v: Array.isArray(value) ? value.join(', ') : value, + c: [], + } + if (options.excludeMessages) { + cell.c = [] + } else if (messages.length > 0) { + cell.c = messages.map((m) => ({ + a: 'Flatfile', + t: `[${m.type.toUpperCase()}]: ${m.message}`, + T: true, + })) + cell.c.hidden = true + } + + return cell + } + const rowEntries = await Promise.all( - Object.keys(row).map(async (colName: string) => { + sheet.config.fields.map(async (field) => { + const colName = field.key if (options.excludeFields?.includes(colName)) { return null } - const formatCell = (cellValue: Flatfile.CellValue) => { - const { value, messages } = cellValue - const cell: XLSX.CellObject = { - t: 's', - v: Array.isArray(value) ? value.join(', ') : value, - c: [], - } - if (options.excludeMessages) { - cell.c = [] - } else if (messages.length > 0) { - cell.c = messages.map((m) => ({ - a: 'Flatfile', - t: `[${m.type.toUpperCase()}]: ${m.message}`, - T: true, - })) - cell.c.hidden = true - } - - return cell - } const transformedColName = await columnNameTransformer( colName, From 3f477e27aa8a9da8f179aad5031afd895881ff3e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:31:29 +0000 Subject: [PATCH 2/3] fix: preserve additional fields while ensuring all schema fields are exported Updated implementation to export the union of: 1. All schema fields (fixes missing columns issue) 2. Additional fields present in record values (preserves allowAdditionalFields) This ensures schema columns always appear while maintaining backward compatibility for users with additional fields. Co-Authored-By: christopher.harrison@flatfile.io --- plugins/export-workbook/src/plugin.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/export-workbook/src/plugin.ts b/plugins/export-workbook/src/plugin.ts index da4c7e80..79904cfd 100644 --- a/plugins/export-workbook/src/plugin.ts +++ b/plugins/export-workbook/src/plugin.ts @@ -103,9 +103,20 @@ export const exportRecords = async ( return cell } + const schemaKeys = new Set( + sheet.config.fields.map((field) => field.key) + ) + const additionalKeys = Object.keys(row).filter( + (key) => !schemaKeys.has(key) + ) + + const allKeys = [ + ...sheet.config.fields.map((field) => field.key), + ...additionalKeys, + ] + const rowEntries = await Promise.all( - sheet.config.fields.map(async (field) => { - const colName = field.key + allKeys.map(async (colName) => { if (options.excludeFields?.includes(colName)) { return null } From 953e496d8ec4c364332d4e279afc005a57a86add Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:37:20 +0000 Subject: [PATCH 3/3] perf: move schema keys computation outside record loop Optimized performance by computing schemaKeys and schemaFieldKeys once per sheet instead of once per record. This reduces O(n*m) overhead to O(n) where n is number of records and m is number of fields. Co-Authored-By: christopher.harrison@flatfile.io --- plugins/export-workbook/src/plugin.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/plugins/export-workbook/src/plugin.ts b/plugins/export-workbook/src/plugin.ts index 79904cfd..a8e3be71 100644 --- a/plugins/export-workbook/src/plugin.ts +++ b/plugins/export-workbook/src/plugin.ts @@ -66,6 +66,9 @@ export const exportRecords = async ( } : async (name: string) => name + const schemaKeys = new Set(sheet.config.fields.map((field) => field.key)) + const schemaFieldKeys = sheet.config.fields.map((field) => field.key) + try { let results = await processRecords( sheet.id, @@ -103,17 +106,11 @@ export const exportRecords = async ( return cell } - const schemaKeys = new Set( - sheet.config.fields.map((field) => field.key) - ) const additionalKeys = Object.keys(row).filter( (key) => !schemaKeys.has(key) ) - const allKeys = [ - ...sheet.config.fields.map((field) => field.key), - ...additionalKeys, - ] + const allKeys = [...schemaFieldKeys, ...additionalKeys] const rowEntries = await Promise.all( allKeys.map(async (colName) => {