/** * Automation2 Flow Editor - Data reference format and helpers. * All dynamic values use structured ref/value objects, not plain strings. */ /** Structured reference to another node's output (path = JSON path segments) */ export interface DataRef { type: 'ref'; nodeId: string; path: (string | number)[]; } /** Explicit static value wrapper */ export interface DataValue { type: 'value'; value: unknown; } /** Union: either a reference or a static value */ export type DynamicValue = DataRef | DataValue; /** Type guards */ export function isRef(v: unknown): v is DataRef { return ( typeof v === 'object' && v !== null && (v as DataRef).type === 'ref' && typeof (v as DataRef).nodeId === 'string' && Array.isArray((v as DataRef).path) ); } export function isValue(v: unknown): v is DataValue { return ( typeof v === 'object' && v !== null && (v as DataValue).type === 'value' ); } export function isDynamicValue(v: unknown): v is DynamicValue { return isRef(v) || isValue(v); } /** Create a reference object */ export function createRef(nodeId: string, path: (string | number)[] = []): DataRef { return { type: 'ref', nodeId, path }; } /** Create a value wrapper */ export function createValue(value: unknown): DataValue { return { type: 'value', value }; } /** Resolve a ref against nodeOutputsPreview for UI preview; returns resolved value or undefined if missing */ export function resolvePreview( ref: DataRef, nodeOutputsPreview: Record ): unknown { const root = nodeOutputsPreview[ref.nodeId]; if (root === undefined) return undefined; let current: unknown = root; for (const seg of ref.path) { if (current == null) return undefined; const key = typeof seg === 'number' ? String(seg) : seg; if (Array.isArray(current) && /^\d+$/.test(key)) { const idx = parseInt(key, 10); if (idx >= 0 && idx < current.length) current = current[idx]; else return undefined; } else if (typeof current === 'object' && key in current) { current = (current as Record)[key]; } else return undefined; } return current; } /** Format a ref for human display: "Node Title → path.segment" */ export function formatRefLabel( ref: DataRef, nodes: Array<{ id: string; title?: string }>, nodeLabelFallback?: (nodeId: string) => string ): string { const node = nodes.find((n) => n.id === ref.nodeId); const nodeLabel = node?.title?.trim() || nodeLabelFallback?.(ref.nodeId) || ref.nodeId; if (ref.path.length === 0) return nodeLabel; const pathStr = ref.path.map((p) => String(p)).join(' → '); return `${nodeLabel} → ${pathStr}`; }