Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions apps/sim/executor/utils/start-block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,115 @@ describe('start-block utilities', () => {

expect(output.customField).toBe('defaultValue')
})

it.concurrent('preserves coerced types for unified start payload', () => {
const block = createBlock('start_trigger', 'start', {
subBlocks: {
inputFormat: {
value: [
{ name: 'conversation_id', type: 'number' },
{ name: 'sender', type: 'object' },
{ name: 'is_active', type: 'boolean' },
],
},
},
})

const resolution = {
blockId: 'start',
block,
path: StartBlockPath.UNIFIED,
} as const

const output = buildStartBlockOutput({
resolution,
workflowInput: {
conversation_id: '149',
sender: '{"id":10,"email":"user@example.com"}',
is_active: 'true',
},
})

expect(output.conversation_id).toBe(149)
expect(output.sender).toEqual({ id: 10, email: 'user@example.com' })
expect(output.is_active).toBe(true)
})

it.concurrent(
'prefers coerced inputFormat values over duplicated top-level workflowInput keys',
() => {
const block = createBlock('start_trigger', 'start', {
subBlocks: {
inputFormat: {
value: [
{ name: 'conversation_id', type: 'number' },
{ name: 'sender', type: 'object' },
{ name: 'is_active', type: 'boolean' },
],
},
},
})

const resolution = {
blockId: 'start',
block,
path: StartBlockPath.UNIFIED,
} as const

const output = buildStartBlockOutput({
resolution,
workflowInput: {
input: {
conversation_id: '149',
sender: '{"id":10,"email":"user@example.com"}',
is_active: 'false',
},
conversation_id: '150',
sender: '{"id":99,"email":"wrong@example.com"}',
is_active: 'true',
extra: 'keep-me',
},
})

expect(output.conversation_id).toBe(149)
expect(output.sender).toEqual({ id: 10, email: 'user@example.com' })
expect(output.is_active).toBe(false)
expect(output.extra).toBe('keep-me')
}
)
})

describe('EXTERNAL_TRIGGER path', () => {
it.concurrent('preserves coerced types for integration trigger payload', () => {
const block = createBlock('webhook', 'start', {
subBlocks: {
inputFormat: {
value: [
{ name: 'count', type: 'number' },
{ name: 'payload', type: 'object' },
],
},
},
})

const resolution = {
blockId: 'start',
block,
path: StartBlockPath.EXTERNAL_TRIGGER,
} as const

const output = buildStartBlockOutput({
resolution,
workflowInput: {
count: '5',
payload: '{"event":"push"}',
extra: 'untouched',
},
})

expect(output.count).toBe(5)
expect(output.payload).toEqual({ event: 'push' })
expect(output.extra).toBe('untouched')
})
})
})
6 changes: 6 additions & 0 deletions apps/sim/executor/utils/start-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ function buildUnifiedStartOutput(
hasStructured: boolean
): NormalizedBlockOutput {
const output: NormalizedBlockOutput = {}
const structuredKeys = hasStructured ? new Set(Object.keys(structuredInput)) : null

if (hasStructured) {
for (const [key, value] of Object.entries(structuredInput)) {
Expand All @@ -272,6 +273,9 @@ function buildUnifiedStartOutput(
if (isPlainObject(workflowInput)) {
for (const [key, value] of Object.entries(workflowInput)) {
if (key === 'onUploadError') continue
// Skip keys already set by schema-coerced structuredInput to
// prevent raw workflowInput strings from overwriting typed values.
if (structuredKeys?.has(key)) continue
// Runtime values override defaults (except undefined/null which mean "not provided")
if (value !== undefined && value !== null) {
output[key] = value
Expand Down Expand Up @@ -384,6 +388,7 @@ function buildIntegrationTriggerOutput(
hasStructured: boolean
): NormalizedBlockOutput {
const output: NormalizedBlockOutput = {}
const structuredKeys = hasStructured ? new Set(Object.keys(structuredInput)) : null

if (hasStructured) {
for (const [key, value] of Object.entries(structuredInput)) {
Expand All @@ -393,6 +398,7 @@ function buildIntegrationTriggerOutput(

if (isPlainObject(workflowInput)) {
for (const [key, value] of Object.entries(workflowInput)) {
if (structuredKeys?.has(key)) continue
if (value !== undefined && value !== null) {
output[key] = value
} else if (!Object.hasOwn(output, key)) {
Expand Down