diff --git a/apps/sim/lib/workflows/blocks/index.ts b/apps/sim/lib/workflows/blocks/index.ts deleted file mode 100644 index 7f9067cc0..000000000 --- a/apps/sim/lib/workflows/blocks/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { BlockSchemaResolver, blockSchemaResolver } from './schema-resolver' -export type { - ResolvedBlock, - ResolvedOption, - ResolvedOutput, - ResolvedSubBlock, -} from './schema-types' diff --git a/apps/sim/lib/workflows/blocks/schema-resolver.ts b/apps/sim/lib/workflows/blocks/schema-resolver.ts deleted file mode 100644 index cd02992e7..000000000 --- a/apps/sim/lib/workflows/blocks/schema-resolver.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { createLogger } from '@sim/logger' -import { getAllBlocks, getBlock } from '@/blocks/registry' -import type { BlockConfig, SubBlockConfig } from '@/blocks/types' -import type { - ResolvedBlock, - ResolvedOption, - ResolvedOutput, - ResolvedSubBlock, -} from './schema-types' - -const logger = createLogger('BlockSchemaResolver') - -/** - * BlockSchemaResolver provides typed access to block configurations. - * - * It wraps the raw block registry and returns resolved, typed schemas - * that consumers can use without any type assertions. - */ -export class BlockSchemaResolver { - private cache = new Map() - - /** Resolve a single block by type */ - resolveBlock(type: string): ResolvedBlock | null { - const cached = this.cache.get(type) - if (cached) return cached - - const config = getBlock(type) - if (!config) return null - - const resolved = this.buildResolvedBlock(config) - this.cache.set(type, resolved) - return resolved - } - - /** Resolve all available blocks */ - resolveAllBlocks(options?: { includeHidden?: boolean }): ResolvedBlock[] { - const configs = getAllBlocks() - return configs - .filter((config) => options?.includeHidden || !config.hideFromToolbar) - .map((config) => this.resolveBlock(config.type)) - .filter((block): block is ResolvedBlock => block !== null) - } - - /** Clear the cache (call when block registry changes) */ - clearCache(): void { - this.cache.clear() - } - - private buildResolvedBlock(config: BlockConfig): ResolvedBlock { - return { - type: config.type, - name: config.name, - description: config.description, - category: config.category, - icon: config.icon as unknown as ResolvedBlock['icon'], - isTrigger: this.isTriggerBlock(config), - hideFromToolbar: config.hideFromToolbar ?? false, - subBlocks: config.subBlocks.map((subBlock) => this.resolveSubBlock(subBlock)), - outputs: this.resolveOutputs(config), - supportsTriggerMode: this.supportsTriggerMode(config), - hasAdvancedMode: config.subBlocks.some((subBlock) => subBlock.mode === 'advanced'), - raw: config, - } - } - - private resolveSubBlock(sb: SubBlockConfig): ResolvedSubBlock { - const resolved: ResolvedSubBlock = { - id: sb.id, - type: sb.type, - label: sb.title, - placeholder: sb.placeholder, - required: typeof sb.required === 'boolean' ? sb.required : undefined, - password: sb.password, - hasCondition: Boolean(sb.condition), - defaultValue: sb.defaultValue, - validation: { - min: sb.min, - max: sb.max, - pattern: this.resolvePattern(sb), - }, - } - - const condition = this.resolveCondition(sb) - if (condition) { - resolved.condition = condition - } - - const options = this.resolveOptions(sb) - if (options.length > 0) { - resolved.options = options - } - - if (!resolved.validation?.min && !resolved.validation?.max && !resolved.validation?.pattern) { - resolved.validation = undefined - } - - return resolved - } - - private resolveCondition(sb: SubBlockConfig): ResolvedSubBlock['condition'] | undefined { - try { - const condition = typeof sb.condition === 'function' ? sb.condition() : sb.condition - if (!condition || typeof condition !== 'object') { - return undefined - } - - return { - field: String(condition.field), - value: condition.value, - } - } catch (error) { - logger.warn('Failed to resolve sub-block condition', { - subBlockId: sb.id, - error: error instanceof Error ? error.message : String(error), - }) - return undefined - } - } - - private resolveOptions(sb: SubBlockConfig): ResolvedOption[] { - try { - if (Array.isArray(sb.options)) { - return sb.options.map((opt) => { - if (typeof opt === 'string') { - return { label: opt, value: opt } - } - - const label = String(opt.label || opt.id || '') - const value = String(opt.id || opt.label || '') - - return { - label, - value, - id: opt.id, - } - }) - } - - // For function-based or dynamic options, return empty. - // Consumers can evaluate these options if they need runtime resolution. - return [] - } catch (error) { - logger.warn('Failed to resolve sub-block options', { - subBlockId: sb.id, - error: error instanceof Error ? error.message : String(error), - }) - return [] - } - } - - private resolveOutputs(config: BlockConfig): ResolvedOutput[] { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const blockOutputs = require('@/lib/workflows/blocks/block-outputs') as { - getBlockOutputPaths: ( - blockType: string, - subBlocks?: Record, - triggerMode?: boolean - ) => string[] - } - - const paths = blockOutputs.getBlockOutputPaths(config.type, {}, false) - return paths.map((path) => ({ - name: path, - type: 'string', - })) - } catch (error) { - logger.warn('Failed to resolve block outputs, using fallback', { - blockType: config.type, - error: error instanceof Error ? error.message : String(error), - }) - return [{ name: 'result', type: 'string' }] - } - } - - private isTriggerBlock(config: BlockConfig): boolean { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const triggerUtils = require('@/lib/workflows/triggers/input-definition-triggers') as { - isInputDefinitionTrigger: (blockType: string) => boolean - } - return triggerUtils.isInputDefinitionTrigger(config.type) - } catch (error) { - logger.warn('Failed to detect trigger block, using fallback', { - blockType: config.type, - error: error instanceof Error ? error.message : String(error), - }) - return config.type === 'starter' - } - } - - private supportsTriggerMode(config: BlockConfig): boolean { - return Boolean( - config.triggerAllowed || - config.subBlocks.some( - (subBlock) => subBlock.id === 'triggerMode' || subBlock.mode === 'trigger' - ) - ) - } - - private resolvePattern(sb: SubBlockConfig): string | undefined { - const maybePattern = (sb as SubBlockConfig & { pattern?: string }).pattern - return typeof maybePattern === 'string' ? maybePattern : undefined - } -} - -/** Singleton resolver instance */ -export const blockSchemaResolver = new BlockSchemaResolver() diff --git a/apps/sim/lib/workflows/blocks/schema-types.ts b/apps/sim/lib/workflows/blocks/schema-types.ts deleted file mode 100644 index 068b3b937..000000000 --- a/apps/sim/lib/workflows/blocks/schema-types.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { LucideIcon } from 'lucide-react' - -/** A fully resolved block schema with all sub-blocks expanded */ -export interface ResolvedBlock { - type: string - name: string - description?: string - category: string - icon?: LucideIcon - isTrigger: boolean - hideFromToolbar: boolean - - /** Resolved sub-blocks with options, conditions, and validation info */ - subBlocks: ResolvedSubBlock[] - - /** Block-level outputs */ - outputs: ResolvedOutput[] - - /** Whether this block supports trigger mode */ - supportsTriggerMode: boolean - - /** Whether this block has advanced mode */ - hasAdvancedMode: boolean - - /** Raw config reference for consumers that need it */ - raw: unknown -} - -/** A resolved sub-block with options and metadata */ -export interface ResolvedSubBlock { - id: string - type: string - label?: string - placeholder?: string - required?: boolean - password?: boolean - - /** Resolved options (for dropdowns/selectors, etc.) */ - options?: ResolvedOption[] - - /** Whether this sub-block has a condition that controls visibility */ - hasCondition: boolean - - /** Condition details if present */ - condition?: { - field: string - value: unknown - /** Whether condition is currently met (if evaluable statically) */ - met?: boolean - } - - /** Validation constraints */ - validation?: { - min?: number - max?: number - pattern?: string - } - - /** Default value */ - defaultValue?: unknown -} - -/** A resolved option for dropdowns/selectors */ -export interface ResolvedOption { - label: string - value: string - id?: string -} - -/** A resolved output definition */ -export interface ResolvedOutput { - name: string - type: string - description?: string -}