/** * Generic workflow-graph types — mirrors the StartSimpli backend * (apps.workflows) n8n-style JSON graph definition. * * The graph is "behavior as data": a directed graph of typed nodes * connected by edges. The shapes here are intentionally generic — node * `type` is just a slug and `category` is a free string token resolved * against the category theme map (theme/categories.ts). * * NOTE: `connections` is the LOCKED backend persistence format. See * serialization.ts for the canonical shape and round-trip guarantees. */ // ── Status enums (match backend TextChoices verbatim) ── /** NodeExecution.Status — per-node runtime status (+ "idle" for editor). */ export type NodeStatus = | 'idle' | 'pending' | 'running' | 'success' | 'error' | 'skipped'; /** WorkflowExecution.Status — overall run status. */ export type RunStatus = | 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'waiting'; // ── Graph definition ── /** * A single node in a workflow graph. * * Matches a backend `Workflow.nodes[]` entry: * `{ id, name, type, parameters, ... }`. Extra backend fields * (`disabled`, `continue_on_fail`) are preserved opaquely. */ export interface WfNode { /** Stable node id — referenced by connections. */ id: string; /** Human-readable name. */ name: string; /** Node type slug, e.g. "condition.if", "trigger.event". */ type: string; /** Node parameters (executor config). */ parameters?: Record; /** * Optional explicit canvas position. When present, auto-layout * preserves it instead of computing one. */ position?: WfPosition; /** Whether the node is disabled (skipped at runtime). */ disabled?: boolean; /** Whether downstream traversal continues if this node fails. */ continueOnFail?: boolean; /** Any additional opaque node fields. */ [key: string]: unknown; } export interface WfPosition { x: number; y: number; } /** * A logical edge in the graph. * * `sourceOutput` is the source node's output index (n8n `main[i]`): * 0 for a single output, 0/1 for if/else, 0..N-1 for switch. * `targetInput` is the target node's input index (the `index` field * stored on each backend connection — almost always 0). */ export interface WfEdge { /** Source node id. */ source: string; /** Target node id. */ target: string; /** Source output index (default 0). */ sourceOutput?: number; /** Target input index (default 0). */ targetInput?: number; /** Connection type — n8n uses "main"; default "main". */ type?: string; } /** * Definition of a node type (catalog entry). Mirrors backend NodeType. */ export interface NodeTypeDef { /** Unique slug, e.g. "condition.if". */ slug: string; /** Human-readable name. */ name: string; /** Category token (resolved via theme/categories.ts). */ category: string; /** Number of outputs (1 for most, 2 for if/else, N for switch). */ outputCount?: number; /** Labels for each output, e.g. ["true", "false"]. */ outputLabels?: string[]; /** Optional description. */ description?: string; /** * JSON-Schema (object schema) describing the node's `parameters`. Drives the * editor's NodeInspector form. Intentionally typed loosely — only the subset * the inspector understands (`type`, `properties`, `title`, `enum`, * `description`) is consumed; unknown keywords are ignored. */ parameterSchema?: JsonSchema; } /** Minimal JSON-Schema subset consumed by the NodeInspector form. */ export interface JsonSchema { type?: 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array'; title?: string; description?: string; /** For object schemas: per-property sub-schemas. */ properties?: Record; /** Required property names (object schemas). */ required?: string[]; /** Enumerated allowed values (renders a select). */ enum?: (string | number)[]; /** Default value. */ default?: unknown; } /** * A complete workflow graph in the editor's working shape. * * `connections` (the backend LOCKED format) is intentionally NOT stored * here — instead edges are kept as a flat list and converted to/from the * nested backend shape by serialization.ts. */ export interface WorkflowGraph { /** Optional workflow id. */ id?: string; /** Optional workflow name. */ name?: string; /** Graph nodes. */ nodes: WfNode[]; /** Graph edges (flat). */ edges: WfEdge[]; } // ── Backend persistence (LOCKED n8n connections shape) ── /** A single connection target: `{ node, type, index }`. */ export interface BackendConnection { /** Target node id. */ node: string; /** Connection type — "main". */ type: string; /** Target input index. */ index: number; } /** * The LOCKED backend connections map: * * ```json * { * "sourceNodeId": { * "main": [ * [ { "node": "targetId", "type": "main", "index": 0 } ] * ] * } * } * ``` * * Outer array index = source output index. Each inner array is the list * of parallel targets fanning out from that output. */ export type BackendConnections = Record< string, Record >; /** Backend node shape (snake_case fields as persisted). */ export interface BackendNode { id: string; name: string; type: string; parameters?: Record; position?: WfPosition; disabled?: boolean; continue_on_fail?: boolean; [key: string]: unknown; } // ── Execution views (read-only overlays for run visualization) ── /** Per-node execution status overlay. Mirrors backend NodeExecution. */ export interface NodeExecView { nodeId: string; status: NodeStatus; /** * Display name of the node. Optional because the bare status overlay * (keyed by node id) may be merged with the graph's node names at the * call site; the execution monitor renders it when present. */ name?: string; /** Node type slug (e.g. "condition.if"), shown in the detail panel. */ type?: string; /** * Position of this node in the run's execution sequence. The timeline * renders rows sorted by this value (ascending). */ executionOrder?: number; startedAt?: string | null; completedAt?: string | null; executionTimeMs?: number | null; error?: string; /** Input payload fed to the node (pretty-printed in the detail panel). */ inputData?: Record; outputData?: Record; } /** Whole-run execution overlay. Mirrors backend WorkflowExecution. */ export interface WfExecutionView { executionId?: string; status: RunStatus; startedAt?: string | null; completedAt?: string | null; errorMessage?: string; /** Per-node status keyed by node id. */ nodes: Record; }