import type { SubBlockType } from '@sim/workflow-types/blocks' export const WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS = { type: 'subflowType', iterations: 'subflowIterations', items: 'subflowItems', condition: 'subflowCondition', batchSize: 'subflowBatchSize', } as const export type WorkflowSearchSubflowFieldId = (typeof WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS)[keyof typeof WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS] export type WorkflowSearchSubflowEditableValue = string | number interface WorkflowSearchSubflowBlock { type: string data?: { loopType?: string parallelType?: string count?: unknown batchSize?: unknown collection?: unknown whileCondition?: unknown doWhileCondition?: unknown } } export interface WorkflowSearchSubflowField { id: WorkflowSearchSubflowFieldId title: string type: SubBlockType value: string editable: boolean valueKind: 'number' | 'text' | 'enum' reason?: string } export function getWorkflowSearchSubflowFields( block: WorkflowSearchSubflowBlock ): WorkflowSearchSubflowField[] { if (block.type === 'loop') { const loopType = block.data?.loopType ?? 'for' const fields: WorkflowSearchSubflowField[] = [ { id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.type, title: 'Loop Type', type: 'combobox', value: String(loopType), editable: false, valueKind: 'enum', reason: 'Subflow type is changed from the block sidebar', }, ] if (loopType === 'for') { fields.push({ id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.iterations, title: 'Loop Iterations', type: 'short-input', value: String(block.data?.count ?? 5), editable: true, valueKind: 'number', }) } else if (loopType === 'forEach') { fields.push({ id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.items, title: 'Collection Items', type: 'code', value: String(block.data?.collection ?? ''), editable: true, valueKind: 'text', }) } else { fields.push({ id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.condition, title: 'While Condition', type: 'code', value: String( loopType === 'doWhile' ? (block.data?.doWhileCondition ?? '') : (block.data?.whileCondition ?? '') ), editable: true, valueKind: 'text', }) } return fields } if (block.type === 'parallel') { const parallelType = block.data?.parallelType ?? 'count' return [ { id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.type, title: 'Parallel Type', type: 'combobox', value: String(parallelType), editable: false, valueKind: 'enum', reason: 'Subflow type is changed from the block sidebar', }, { id: parallelType === 'count' ? WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.iterations : WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.items, title: parallelType === 'count' ? 'Parallel Iterations' : 'Parallel Items', type: parallelType === 'count' ? 'short-input' : 'code', value: parallelType === 'count' ? String(block.data?.count ?? 5) : String(block.data?.collection ?? ''), editable: true, valueKind: parallelType === 'count' ? 'number' : 'text', }, { id: WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize, title: 'Parallel Batch Size', type: 'short-input', value: String(block.data?.batchSize ?? 20), editable: true, valueKind: 'number', }, ] } return [] } export function getWorkflowSearchSubflowField( block: WorkflowSearchSubflowBlock, fieldId: WorkflowSearchSubflowFieldId ) { return getWorkflowSearchSubflowFields(block).find((field) => field.id === fieldId) } export function workflowSearchSubflowFieldMatchesExpected( block: WorkflowSearchSubflowBlock, fieldId: WorkflowSearchSubflowFieldId, expectedValue: unknown ): boolean { const field = getWorkflowSearchSubflowField(block, fieldId) return Boolean(field && String(field.value) === String(expectedValue)) } export function parseWorkflowSearchSubflowReplacement({ blockType, fieldId, replacement, }: { blockType: 'loop' | 'parallel' fieldId: WorkflowSearchSubflowFieldId replacement: string }): | { success: true; value: WorkflowSearchSubflowEditableValue } | { success: false; reason: string } { if ( fieldId !== WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.iterations && fieldId !== WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize ) { return { success: true, value: replacement } } const trimmed = replacement.trim() if (!/^\d+$/.test(trimmed)) { return { success: false, reason: 'Subflow iteration count must be a whole number' } } const count = Number.parseInt(trimmed, 10) const maxBatchSize = 20 if ( count < 1 || (fieldId === WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize && count > maxBatchSize) ) { return { success: false, reason: fieldId === WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize ? `Parallel batch size must be between 1 and ${maxBatchSize}` : 'Subflow iteration count must be greater than 0', } } return { success: true, value: count } }