1- import type { BlockState , WorkflowState } from '@/stores/workflows/workflow/types'
2- import { SYSTEM_SUBBLOCK_IDS , TRIGGER_RUNTIME_SUBBLOCK_IDS } from '@/triggers/constants'
1+ import type { WorkflowState } from '@/stores/workflows/workflow/types'
32import {
3+ extractBlockFieldsForComparison ,
4+ extractSubBlockRest ,
5+ filterSubBlockIds ,
46 normalizedStringify ,
57 normalizeEdge ,
68 normalizeLoop ,
79 normalizeParallel ,
10+ normalizeSubBlockValue ,
811 normalizeValue ,
912 normalizeVariables ,
10- sanitizeInputFormat ,
11- sanitizeTools ,
1213 sanitizeVariable ,
1314} from './normalize'
1415
15- /** Block with optional diff markers added by copilot */
16- type BlockWithDiffMarkers = BlockState & {
17- is_diff ?: string
18- field_diffs ?: Record < string , unknown >
19- }
20-
21- /** SubBlock with optional diff marker */
22- type SubBlockWithDiffMarker = {
23- id : string
24- type : string
25- value : unknown
26- is_diff ?: string
27- }
28-
2916/**
3017 * Compare the current workflow state with the deployed state to detect meaningful changes.
3118 * Uses generateWorkflowDiffSummary internally to ensure consistent change detection.
@@ -125,36 +112,21 @@ export function generateWorkflowDiffSummary(
125112 for ( const id of currentBlockIds ) {
126113 if ( ! previousBlockIds . has ( id ) ) continue
127114
128- const currentBlock = currentBlocks [ id ] as BlockWithDiffMarkers
129- const previousBlock = previousBlocks [ id ] as BlockWithDiffMarkers
115+ const currentBlock = currentBlocks [ id ]
116+ const previousBlock = previousBlocks [ id ]
130117 const changes : FieldChange [ ] = [ ]
131118
132- // Compare block-level properties (excluding visual-only fields )
119+ // Use shared helpers for block field extraction (single source of truth )
133120 const {
134- position : _currentPos ,
135- subBlocks : currentSubBlocks = { } ,
136- layout : _currentLayout ,
137- height : _currentHeight ,
138- outputs : _currentOutputs ,
139- is_diff : _currentIsDiff ,
140- field_diffs : _currentFieldDiffs ,
141- ...currentRest
142- } = currentBlock
143-
121+ blockRest : currentRest ,
122+ normalizedData : currentDataRest ,
123+ subBlocks : currentSubBlocks ,
124+ } = extractBlockFieldsForComparison ( currentBlock )
144125 const {
145- position : _previousPos ,
146- subBlocks : previousSubBlocks = { } ,
147- layout : _previousLayout ,
148- height : _previousHeight ,
149- outputs : _previousOutputs ,
150- is_diff : _previousIsDiff ,
151- field_diffs : _previousFieldDiffs ,
152- ...previousRest
153- } = previousBlock
154-
155- // Exclude width/height from data object (container dimensions from autolayout)
156- const { width : _cw , height : _ch , ...currentDataRest } = currentRest . data || { }
157- const { width : _pw , height : _ph , ...previousDataRest } = previousRest . data || { }
126+ blockRest : previousRest ,
127+ normalizedData : previousDataRest ,
128+ subBlocks : previousSubBlocks ,
129+ } = extractBlockFieldsForComparison ( previousBlock )
158130
159131 const normalizedCurrentBlock = { ...currentRest , data : currentDataRest , subBlocks : undefined }
160132 const normalizedPreviousBlock = {
@@ -179,10 +151,11 @@ export function generateWorkflowDiffSummary(
179151 newValue : currentBlock . enabled ,
180152 } )
181153 }
182- // Check other block properties
154+ // Check other block properties (boolean fields)
155+ // Use !! to normalize: null/undefined/false are all equivalent (falsy)
183156 const blockFields = [ 'horizontalHandles' , 'advancedMode' , 'triggerMode' ] as const
184157 for ( const field of blockFields ) {
185- if ( currentBlock [ field ] !== previousBlock [ field ] ) {
158+ if ( ! ! currentBlock [ field ] !== ! ! previousBlock [ field ] ) {
186159 changes . push ( {
187160 field,
188161 oldValue : previousBlock [ field ] ,
@@ -195,42 +168,27 @@ export function generateWorkflowDiffSummary(
195168 }
196169 }
197170
198- // Compare subBlocks
199- const allSubBlockIds = [
171+ // Compare subBlocks using shared helper for filtering (single source of truth)
172+ const allSubBlockIds = filterSubBlockIds ( [
200173 ...new Set ( [ ...Object . keys ( currentSubBlocks ) , ...Object . keys ( previousSubBlocks ) ] ) ,
201- ]
202- . filter (
203- ( subId ) =>
204- ! TRIGGER_RUNTIME_SUBBLOCK_IDS . includes ( subId ) && ! SYSTEM_SUBBLOCK_IDS . includes ( subId )
205- )
206- . sort ( )
174+ ] )
207175
208176 for ( const subId of allSubBlockIds ) {
209- const currentSub = currentSubBlocks [ subId ]
210- const previousSub = previousSubBlocks [ subId ]
177+ const currentSub = currentSubBlocks [ subId ] as Record < string , unknown > | undefined
178+ const previousSub = previousSubBlocks [ subId ] as Record < string , unknown > | undefined
211179
212180 if ( ! currentSub || ! previousSub ) {
213181 changes . push ( {
214182 field : subId ,
215- oldValue : previousSub ?. value ?? null ,
216- newValue : currentSub ?. value ?? null ,
183+ oldValue : ( previousSub as Record < string , unknown > | undefined ) ?. value ?? null ,
184+ newValue : ( currentSub as Record < string , unknown > | undefined ) ?. value ?? null ,
217185 } )
218186 continue
219187 }
220188
221- // Compare subBlock values with sanitization
222- let currentValue : unknown = currentSub . value ?? null
223- let previousValue : unknown = previousSub . value ?? null
224-
225- if ( subId === 'tools' && Array . isArray ( currentValue ) && Array . isArray ( previousValue ) ) {
226- currentValue = sanitizeTools ( currentValue )
227- previousValue = sanitizeTools ( previousValue )
228- }
229-
230- if ( subId === 'inputFormat' && Array . isArray ( currentValue ) && Array . isArray ( previousValue ) ) {
231- currentValue = sanitizeInputFormat ( currentValue )
232- previousValue = sanitizeInputFormat ( previousValue )
233- }
189+ // Use shared helper for subBlock value normalization (single source of truth)
190+ const currentValue = normalizeSubBlockValue ( subId , currentSub . value )
191+ const previousValue = normalizeSubBlockValue ( subId , previousSub . value )
234192
235193 // For string values, compare directly to catch even small text changes
236194 if ( typeof currentValue === 'string' && typeof previousValue === 'string' ) {
@@ -245,11 +203,9 @@ export function generateWorkflowDiffSummary(
245203 }
246204 }
247205
248- // Compare subBlock REST properties (type, id, etc. - excluding value and is_diff)
249- const currentSubWithDiff = currentSub as SubBlockWithDiffMarker
250- const previousSubWithDiff = previousSub as SubBlockWithDiffMarker
251- const { value : _cv , is_diff : _cd , ...currentSubRest } = currentSubWithDiff
252- const { value : _pv , is_diff : _pd , ...previousSubRest } = previousSubWithDiff
206+ // Use shared helper for subBlock REST extraction (single source of truth)
207+ const currentSubRest = extractSubBlockRest ( currentSub )
208+ const previousSubRest = extractSubBlockRest ( previousSub )
253209
254210 if ( normalizedStringify ( currentSubRest ) !== normalizedStringify ( previousSubRest ) ) {
255211 changes . push ( {
0 commit comments