frontend_nyla/src/components/Automation2FlowEditor/nodes/shared/dataRef.ts

91 lines
2.6 KiB
TypeScript

/**
* 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<string, unknown>
): 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<string, unknown>)[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}`;
}