diff --git a/apps/sim/executor/utils/output-filter.test.ts b/apps/sim/executor/utils/output-filter.test.ts index 9d260c5f076..be5317b73ef 100644 --- a/apps/sim/executor/utils/output-filter.test.ts +++ b/apps/sim/executor/utils/output-filter.test.ts @@ -46,4 +46,15 @@ describe('output filtering', () => { expect(output.nested).not.toHaveProperty('childTraceSpans') expect((output.nested as typeof nestedSpans).kept).toBe(nestedSpans.kept) }) + + it('keeps the file size a block completed with when hydration later updates it in place', () => { + const file = { id: 'f1', key: 'k1', url: 'u', name: 'deck.pptx', size: 10, type: 'pptx' } + const blockOutput = { file, rows: [{ id: 1 }] } + + const output = filterOutputForLog('function', blockOutput as never) + file.size = 4096 + + expect((output.file as typeof file).size).toBe(10) + expect(output.rows).toBe(blockOutput.rows) + }) }) diff --git a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts index 9e607584c7c..f1674a33e93 100644 --- a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts +++ b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts @@ -1,3 +1,4 @@ +import { isUserFile } from '@/lib/core/utils/user-file' import { groupIterationBlocks } from '@/lib/logs/execution/trace-spans/iteration-grouping' import { createSpanFromLog } from '@/lib/logs/execution/trace-spans/span-factory' import type { TraceSpan } from '@/lib/logs/types' @@ -37,7 +38,7 @@ function setFilteredValue(output: Record, key: string, value: u * returned as-is, so a block log shares structure with the block's compacted * state output instead of holding a second copy for the rest of the run. A * copy starts only at the first changed child; non-plain prototypes (Date, - * class instances, null-prototype objects) are always rebuilt. + * class instances, null-prototype objects) and file objects are always rebuilt. */ export function filterHiddenOutputKeys(value: unknown): unknown { if (value === null || value === undefined) { @@ -63,8 +64,10 @@ export function filterHiddenOutputKeys(value: unknown): unknown { if (typeof value === 'object') { const entries = Object.entries(value as Record) + // File objects are always copied: file hydration later updates `size` in + // place on the shared state object, and a log keeps the value it completed with. let filtered: Record | undefined = - Object.getPrototypeOf(value) === Object.prototype ? undefined : {} + Object.getPrototypeOf(value) === Object.prototype && !isUserFile(value) ? undefined : {} for (let index = 0; index < entries.length; index++) { const [key, val] = entries[index] const hidden = HIDDEN_OUTPUT_KEYS.has(key)