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,51 +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)
133- 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-
119+ // Use shared helpers for block field extraction (single source of truth)
144120 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 from data object:
156- // - width/height: container dimensions from autolayout
157- // - nodes: subflow node membership (derived/runtime for parallel/loop blocks)
158- // - distribution: parallel distribution (derived/runtime)
121+ blockRest : currentRest ,
122+ normalizedData : currentDataRest ,
123+ subBlocks : currentSubBlocks ,
124+ } = extractBlockFieldsForComparison ( currentBlock )
159125 const {
160- width : _cw ,
161- height : _ch ,
162- nodes : _cn ,
163- distribution : _cd ,
164- ...currentDataRest
165- } = ( currentRest . data || { } ) as Record < string , unknown >
166- const {
167- width : _pw ,
168- height : _ph ,
169- nodes : _pn ,
170- distribution : _pd ,
171- ...previousDataRest
172- } = ( previousRest . data || { } ) as Record < string , unknown >
126+ blockRest : previousRest ,
127+ normalizedData : previousDataRest ,
128+ subBlocks : previousSubBlocks ,
129+ } = extractBlockFieldsForComparison ( previousBlock )
173130
174131 const normalizedCurrentBlock = { ...currentRest , data : currentDataRest , subBlocks : undefined }
175132 const normalizedPreviousBlock = {
@@ -194,10 +151,11 @@ export function generateWorkflowDiffSummary(
194151 newValue : currentBlock . enabled ,
195152 } )
196153 }
197- // Check other block properties
154+ // Check other block properties (boolean fields)
155+ // Use !! to normalize: null/undefined/false are all equivalent (falsy)
198156 const blockFields = [ 'horizontalHandles' , 'advancedMode' , 'triggerMode' ] as const
199157 for ( const field of blockFields ) {
200- if ( currentBlock [ field ] !== previousBlock [ field ] ) {
158+ if ( ! ! currentBlock [ field ] !== ! ! previousBlock [ field ] ) {
201159 changes . push ( {
202160 field,
203161 oldValue : previousBlock [ field ] ,
@@ -210,42 +168,27 @@ export function generateWorkflowDiffSummary(
210168 }
211169 }
212170
213- // Compare subBlocks
214- const allSubBlockIds = [
171+ // Compare subBlocks using shared helper for filtering (single source of truth)
172+ const allSubBlockIds = filterSubBlockIds ( [
215173 ...new Set ( [ ...Object . keys ( currentSubBlocks ) , ...Object . keys ( previousSubBlocks ) ] ) ,
216- ]
217- . filter (
218- ( subId ) =>
219- ! TRIGGER_RUNTIME_SUBBLOCK_IDS . includes ( subId ) && ! SYSTEM_SUBBLOCK_IDS . includes ( subId )
220- )
221- . sort ( )
174+ ] )
222175
223176 for ( const subId of allSubBlockIds ) {
224- const currentSub = currentSubBlocks [ subId ]
225- const previousSub = previousSubBlocks [ subId ]
177+ const currentSub = currentSubBlocks [ subId ] as Record < string , unknown > | undefined
178+ const previousSub = previousSubBlocks [ subId ] as Record < string , unknown > | undefined
226179
227180 if ( ! currentSub || ! previousSub ) {
228181 changes . push ( {
229182 field : subId ,
230- oldValue : previousSub ?. value ?? null ,
231- newValue : currentSub ?. value ?? null ,
183+ oldValue : ( previousSub as Record < string , unknown > | undefined ) ?. value ?? null ,
184+ newValue : ( currentSub as Record < string , unknown > | undefined ) ?. value ?? null ,
232185 } )
233186 continue
234187 }
235188
236- // Compare subBlock values with sanitization
237- let currentValue : unknown = currentSub . value ?? null
238- let previousValue : unknown = previousSub . value ?? null
239-
240- if ( subId === 'tools' && Array . isArray ( currentValue ) && Array . isArray ( previousValue ) ) {
241- currentValue = sanitizeTools ( currentValue )
242- previousValue = sanitizeTools ( previousValue )
243- }
244-
245- if ( subId === 'inputFormat' && Array . isArray ( currentValue ) && Array . isArray ( previousValue ) ) {
246- currentValue = sanitizeInputFormat ( currentValue )
247- previousValue = sanitizeInputFormat ( previousValue )
248- }
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 )
249192
250193 // For string values, compare directly to catch even small text changes
251194 if ( typeof currentValue === 'string' && typeof previousValue === 'string' ) {
@@ -260,11 +203,9 @@ export function generateWorkflowDiffSummary(
260203 }
261204 }
262205
263- // Compare subBlock REST properties (type, id, etc. - excluding value and is_diff)
264- const currentSubWithDiff = currentSub as SubBlockWithDiffMarker
265- const previousSubWithDiff = previousSub as SubBlockWithDiffMarker
266- const { value : _cv , is_diff : _cd , ...currentSubRest } = currentSubWithDiff
267- 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 )
268209
269210 if ( normalizedStringify ( currentSubRest ) !== normalizedStringify ( previousSubRest ) ) {
270211 changes . push ( {
0 commit comments