Files
simstudioai--sim/apps/sim/lib/workflows/search-replace/subflow-fields.ts
T
wehub-resource-sync d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

188 lines
5.2 KiB
TypeScript

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 }
}