@pipelex/mthds-ui 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/graph/validateGraphSpec.ts","../src/graph/graphAnalysis.ts","../src/graph/pipeCardPayload.ts","../src/graph/graphBuilders.ts","../src/graph/graphFolds.ts","../src/graph/graphValidation.ts","../src/graph/stuffLookup.ts","../src/graph/elkGraphBuilder.ts","../src/graph/graphLayout.ts","../src/graph/edgeRoutes.ts","../src/graph/graphControllers.ts","../src/graph/styles/graphStyles.ts","../src/graph/styles/humanize.ts","../src/graph/styles/textMetrics.ts","../src/graph/styles/simpleStyle.ts","../src/graph/styles/stylePipelines.ts"],"sourcesContent":["/**\n * Single boundary validator for GraphSpec JSON.\n *\n * `validateGraphSpec()` runs once when a spec is loaded from an untyped source\n * (embedded JSON, library consumer input). After it returns, internal code\n * trusts the data and uses the strict types in `types.ts` — no inline\n * defensive checks scattered across modules.\n *\n * The pipelex runtime guarantees specific fields are always present and\n * non-null. This validator enforces those guarantees and fails loudly when\n * they are violated, instead of hiding upstream bugs behind fabricated values.\n *\n * Note: `validateGraphSpec` normalizes the input **in place** — see its doc\n * comment.\n */\nimport type {\n CardNode,\n GraphSpec,\n GraphSpecEdgeKind,\n GraphSpecMode,\n GraphSpecNode,\n NodeKind,\n PipeCallNode,\n PipeStatus,\n} from \"./types\";\nimport { BINDING_STEP_TYPE, KNOWN_PIPE_TYPES } from \"./types\";\n\n/**\n * Thrown by `validateGraphSpec` when a spec violates a pipelex runtime\n * guarantee. `path` points at the offending location (e.g.\n * `nodes[3].io.inputs[0].name`) and `name` is the stable, greppable\n * `\"GraphSpecValidationError\"`.\n */\nexport class GraphSpecValidationError extends Error {\n readonly path: string;\n\n constructor(path: string, detail: string) {\n const location = path === \"\" ? \"<root>\" : path;\n super(`GraphSpec validation failed at ${location}: ${detail}`);\n this.name = \"GraphSpecValidationError\";\n this.path = path;\n }\n}\n\nconst PIPE_STATUSES: ReadonlySet<string> = new Set<PipeStatus>([\n \"succeeded\",\n \"failed\",\n \"running\",\n \"scheduled\",\n \"skipped\",\n \"canceled\",\n]);\n\nconst GRAPH_SPEC_MODES: ReadonlySet<string> = new Set<GraphSpecMode>([\"dry\", \"live\", \"static\"]);\n\n/** The node kinds a spec may carry: the pipe-call kinds, and the binding kind. */\nconst DRAWN_NODE_KINDS: ReadonlySet<string> = new Set<NodeKind>([\n \"controller\",\n \"operator\",\n \"binding\",\n]);\n\nconst EDGE_KINDS: ReadonlySet<string> = new Set<GraphSpecEdgeKind>([\n \"contains\",\n \"data\",\n \"control\",\n \"selected_outcome\",\n \"batch_item\",\n \"batch_aggregate\",\n \"parallel_combine\",\n]);\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\nfunction fail(path: string, detail: string): never {\n throw new GraphSpecValidationError(path, detail);\n}\n\nfunction requireNonEmptyString(value: unknown, path: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n fail(path, `expected a non-empty string, got ${describe(value)}`);\n }\n return value;\n}\n\nfunction validateIoItem(item: unknown, path: string): void {\n if (!isPlainObject(item)) {\n fail(path, `expected an object, got ${describe(item)}`);\n }\n // `name` is guaranteed by pipelex; `concept` and `digest` stay tolerant.\n requireNonEmptyString(item.name, `${path}.name`);\n // `multiplicity` may be absent (a producer predating it, or a single value),\n // but a present one must be the registry's encoding: a malformed marker would\n // otherwise read as single without a word, which is the drop it exists to end.\n const multiplicity = item.multiplicity;\n if (\n multiplicity !== undefined &&\n multiplicity !== null &&\n typeof multiplicity !== \"boolean\" &&\n !(Number.isInteger(multiplicity) && (multiplicity as number) >= 1)\n ) {\n const got = typeof multiplicity === \"number\" ? String(multiplicity) : describe(multiplicity);\n fail(`${path}.multiplicity`, `expected a boolean, a positive integer or null, got ${got}`);\n }\n}\n\nfunction requireNumber(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n fail(path, `expected a finite number, got ${describe(value)}`);\n }\n return value;\n}\n\n/**\n * A cost, which may arrive absent where its producer wrote `null`, normalized to `null`\n * when — and only when — the rated-call count says `null` is what it was.\n *\n * Some hosts drop `null` values from the JSON they relay (ChatGPT does, on the way to\n * an MCP App view), so every unrated cost reaches the renderer as a missing key. Absence\n * alone is ambiguous: a dropped `null`, or a cost the producer failed to write. Invariant\n * 2 on `GraphSpecNodeUsage` settles it: `cost` is `null` exactly when no call was rated.\n * With no rated call the value can only have been `null`. With one, a missing cost is a\n * real gap, and reading it as unrated would understate what the run spent, so it fails.\n */\nfunction normalizeCost(value: unknown, ratedCalls: number, path: string): number | null {\n if (value === null) return null;\n if (value === undefined) {\n if (ratedCalls === 0) return null;\n fail(path, `expected a finite number for ${ratedCalls} rated call(s), got undefined`);\n }\n return requireNumber(value, path);\n}\n\n/**\n * A nullable number that may also be absent, normalized to `null`.\n *\n * For fields added after specs were already being produced. The validator's job is\n * to reject malformed data, not to insist the producer is the latest version: an\n * absent key is unambiguous (the producer predates the field), while a wrong-typed\n * one is not and still fails.\n */\nfunction normalizeNullableNumber(value: unknown, path: string): number | null {\n if (value === undefined || value === null) return null;\n return requireNumber(value, path);\n}\n\nfunction requireNumberRecord(value: unknown, path: string): void {\n if (!isPlainObject(value)) {\n fail(path, `expected an object, got ${describe(value)}`);\n }\n for (const [key, entry] of Object.entries(value)) {\n requireNumber(entry, `${path}.${key}`);\n }\n}\n\n/**\n * Validate a usage object (node-level or graph-level).\n *\n * This is the whole point of the boundary: types alone are a compile-time\n * fiction, so a malformed `usage` from a stale or hostile spec would otherwise\n * flow through as trusted data and be rendered as a dollar figure. In\n * particular `cost` MUST come out as `number | null` and nothing else — a string\n * cost, or an absent one on calls that were rated, silently becoming \"unrated\"\n * would understate what a run spent. The counts stay required: an absent count\n * is not a relayed `null`, since the producer never writes one.\n */\nfunction validateUsage(usage: unknown, path: string): void {\n if (!isPlainObject(usage)) {\n fail(path, `expected an object, got ${describe(usage)}`);\n }\n requireNumber(usage.inference_calls, `${path}.inference_calls`);\n const ratedCalls = requireNumber(usage.rated_inference_calls, `${path}.rated_inference_calls`);\n requireNumberRecord(usage.nb_tokens_by_category, `${path}.nb_tokens_by_category`);\n requireNumber(usage.total_tokens, `${path}.total_tokens`);\n usage.cost = normalizeCost(usage.cost, ratedCalls, `${path}.cost`);\n usage.cost_input = normalizeNullableNumber(usage.cost_input, `${path}.cost_input`);\n usage.cost_output = normalizeNullableNumber(usage.cost_output, `${path}.cost_output`);\n requireNumber(usage.subtree_inference_calls, `${path}.subtree_inference_calls`);\n const subtreeRatedCalls = requireNumber(\n usage.subtree_rated_inference_calls,\n `${path}.subtree_rated_inference_calls`,\n );\n requireNumberRecord(usage.subtree_nb_tokens_by_category, `${path}.subtree_nb_tokens_by_category`);\n requireNumber(usage.subtree_total_tokens, `${path}.subtree_total_tokens`);\n usage.subtree_cost = normalizeCost(usage.subtree_cost, subtreeRatedCalls, `${path}.subtree_cost`);\n usage.subtree_cost_input = normalizeNullableNumber(\n usage.subtree_cost_input,\n `${path}.subtree_cost_input`,\n );\n usage.subtree_cost_output = normalizeNullableNumber(\n usage.subtree_cost_output,\n `${path}.subtree_cost_output`,\n );\n // Absent is tolerated and normalized to `[]` (mirroring the `io` treatment\n // below): a spec produced before per-model attribution existed is legitimately\n // missing it, and an absent list is unambiguous — no per-model data — unlike a\n // malformed one, which is still rejected.\n usage.by_model = normalizeModelUsageList(usage.by_model, `${path}.by_model`);\n usage.subtree_by_model = normalizeModelUsageList(\n usage.subtree_by_model,\n `${path}.subtree_by_model`,\n );\n}\n\n/**\n * Validate a per-model usage list, normalizing an absent one to `[]`.\n *\n * The model names here are displayed as fact (\"this is what ran\"), so a malformed\n * entry must fail loudly rather than render as an empty or partial attribution.\n * Absence is not malformation, though: specs generated before per-model\n * attribution existed simply have no such key.\n */\nfunction normalizeModelUsageList(value: unknown, path: string): unknown[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) {\n fail(path, `expected an array when present, got ${describe(value)}`);\n }\n value.forEach((entry, index) => {\n const entryPath = `${path}[${index}]`;\n if (!isPlainObject(entry)) {\n fail(entryPath, `expected an object, got ${describe(entry)}`);\n }\n requireNonEmptyString(entry.inference_model_name, `${entryPath}.inference_model_name`);\n requireNonEmptyString(entry.inference_model_id, `${entryPath}.inference_model_id`);\n // Absent on specs predating the field. Normalized to a value that is not \"llm\",\n // so token counts stay hidden rather than being shown on unverified grounds.\n if (entry.model_type === undefined) {\n entry.model_type = \"unknown\";\n } else {\n requireNonEmptyString(entry.model_type, `${entryPath}.model_type`);\n }\n requireNumber(entry.inference_calls, `${entryPath}.inference_calls`);\n const ratedCalls = requireNumber(\n entry.rated_inference_calls,\n `${entryPath}.rated_inference_calls`,\n );\n entry.cost = normalizeCost(entry.cost, ratedCalls, `${entryPath}.cost`);\n });\n return value;\n}\n\nfunction validateNode(node: unknown, path: string): void {\n if (!isPlainObject(node)) {\n fail(path, `expected an object, got ${describe(node)}`);\n }\n\n requireNonEmptyString(node.id, `${path}.id`);\n\n // A real pipelex run serializes two families of node: pipe-call nodes\n // (`kind` \"controller\" or \"operator\") and the binding nodes of a sequence's\n // binding steps (`kind` \"binding\"). The other `NodeKind` values exist in the\n // enum only for the mermaid renderer and never reach a graphspec.json — a\n // spec carrying one is malformed or wrong-version, so reject it.\n if (!DRAWN_NODE_KINDS.has(node.kind as string)) {\n fail(\n `${path}.kind`,\n `expected \"controller\", \"operator\" or \"binding\" — a real pipelex run emits only ` +\n `pipe-call and binding nodes, got ${JSON.stringify(node.kind)}`,\n );\n }\n\n if (typeof node.status !== \"string\" || !PIPE_STATUSES.has(node.status)) {\n fail(\n `${path}.status`,\n `expected one of ${[...PIPE_STATUSES].join(\", \")}, got ${JSON.stringify(node.status)}`,\n );\n }\n\n // A binding node's `pipe_code` is its step's `from` path, never empty.\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n const pipeType = requireNonEmptyString(node.pipe_type, `${path}.pipe_type`);\n if (node.kind === \"binding\") {\n // A binding step runs no pipe, so its class is the step's own and nothing else.\n if (pipeType !== BINDING_STEP_TYPE) {\n fail(\n `${path}.pipe_type`,\n `expected \"${BINDING_STEP_TYPE}\" on a binding node, got ${JSON.stringify(pipeType)}`,\n );\n }\n } else if (!KNOWN_PIPE_TYPES.has(pipeType)) {\n fail(\n `${path}.pipe_type`,\n `unrecognized pipe class \"${pipeType}\" — add it to PipeOperatorType ` +\n `or PipeControllerType in types.ts`,\n );\n }\n requireNonEmptyString(node.description, `${path}.description`);\n requireNonEmptyString(node.domain_code, `${path}.domain_code`);\n\n // `io` is always emitted by pipelex; tolerate an absent key (the pipelex\n // model defaults it) by normalizing to an empty shape.\n let io = node.io;\n if (io === undefined) {\n io = { inputs: [], outputs: [] };\n node.io = io;\n }\n if (!isPlainObject(io)) {\n fail(`${path}.io`, `expected an object, got ${describe(io)}`);\n }\n if (io.inputs === undefined) {\n io.inputs = [];\n } else if (!Array.isArray(io.inputs)) {\n fail(`${path}.io.inputs`, `expected an array, got ${describe(io.inputs)}`);\n }\n if (io.outputs === undefined) {\n io.outputs = [];\n } else if (!Array.isArray(io.outputs)) {\n fail(`${path}.io.outputs`, `expected an array, got ${describe(io.outputs)}`);\n }\n (io.inputs as unknown[]).forEach((item, j) => validateIoItem(item, `${path}.io.inputs[${j}]`));\n (io.outputs as unknown[]).forEach((item, j) => validateIoItem(item, `${path}.io.outputs[${j}]`));\n\n // `usage` is legitimately ABSENT OR NULL: pydantic serializes `usage=None` as an\n // explicit `\"usage\": null` rather than omitting the key, and that is the normal\n // shape for a run that collected no usage. Both mean the same thing, so null is\n // normalized away here and downstream code only ever sees an object or undefined.\n if (node.usage === null) {\n delete node.usage;\n } else if (node.usage !== undefined) {\n validateUsage(node.usage, `${path}.usage`);\n }\n}\n\nfunction validateEdge(edge: unknown, path: string): void {\n if (!isPlainObject(edge)) {\n fail(path, `expected an object, got ${describe(edge)}`);\n }\n requireNonEmptyString(edge.id, `${path}.id`);\n requireNonEmptyString(edge.source, `${path}.source`);\n requireNonEmptyString(edge.target, `${path}.target`);\n if (typeof edge.kind !== \"string\" || !EDGE_KINDS.has(edge.kind)) {\n fail(\n `${path}.kind`,\n `expected one of ${[...EDGE_KINDS].join(\", \")}, got ${JSON.stringify(edge.kind)}`,\n );\n }\n}\n\n/**\n * Validate a raw, untyped value as a `GraphSpec`. Throws\n * `GraphSpecValidationError` on the first violation.\n *\n * **Mutates the input in place:** on success it returns the *same* object\n * (typed as `GraphSpec`), with its normalizations written directly onto the\n * passed-in object: where a node has no `io` key it writes\n * `io = { inputs: [], outputs: [] }` (and fills absent `io.inputs` /\n * `io.outputs` arrays), and an absent unrated cost is written as `null`, so\n * code downstream never meets `undefined` where the types promise `null`. Pass a fresh\n * parse result (e.g. straight from `JSON.parse`) — do not pass a frozen or\n * externally shared object.\n */\nexport function validateGraphSpec(raw: unknown): GraphSpec {\n if (!isPlainObject(raw)) {\n fail(\"\", `expected a GraphSpec object, got ${describe(raw)}`);\n }\n\n if (!Array.isArray(raw.nodes)) {\n fail(\"nodes\", `expected an array, got ${describe(raw.nodes)}`);\n }\n if (!Array.isArray(raw.edges)) {\n fail(\"edges\", `expected an array, got ${describe(raw.edges)}`);\n }\n\n if (!isPlainObject(raw.meta)) {\n fail(\"meta\", `expected an object, got ${describe(raw.meta)}`);\n }\n if (raw.meta.format !== \"mthds\") {\n fail(\n \"meta.format\",\n `expected \"mthds\" (this does not look like pipelex GraphSpec JSON), ` +\n `got ${JSON.stringify(raw.meta.format)}`,\n );\n }\n if (\n raw.meta.mode !== undefined &&\n (typeof raw.meta.mode !== \"string\" || !GRAPH_SPEC_MODES.has(raw.meta.mode))\n ) {\n fail(\n \"meta.mode\",\n `expected one of ${[...GRAPH_SPEC_MODES].join(\", \")} when present, ` +\n `got ${JSON.stringify(raw.meta.mode)}`,\n );\n }\n\n // `pipe_registry` / `concept_registry` are gated by the pipelex\n // `data_inclusion` flag — legitimately absent, and minimally `{}`.\n if (raw.pipe_registry !== undefined && !isPlainObject(raw.pipe_registry)) {\n fail(\"pipe_registry\", `expected an object when present, got ${describe(raw.pipe_registry)}`);\n }\n if (raw.concept_registry !== undefined && !isPlainObject(raw.concept_registry)) {\n fail(\n \"concept_registry\",\n `expected an object when present, got ${describe(raw.concept_registry)}`,\n );\n }\n\n // Same all-or-nothing rule as the node field, and the same null-vs-absent point:\n // pydantic emits `\"usage\": null` for a run that collected none.\n if (raw.usage === null) {\n delete raw.usage;\n } else if (raw.usage !== undefined) {\n if (!isPlainObject(raw.usage)) {\n fail(\"usage\", `expected an object when present, got ${describe(raw.usage)}`);\n }\n validateUsage(raw.usage.total, \"usage.total\");\n validateUsage(raw.usage.unattributed, \"usage.unattributed\");\n }\n\n raw.nodes.forEach((node, i) => validateNode(node, `nodes[${i}]`));\n raw.edges.forEach((edge, i) => validateEdge(edge, `edges[${i}]`));\n\n return raw as unknown as GraphSpec;\n}\n\n/**\n * Narrow a `GraphSpecNode` to a `PipeCallNode` at an internal trust boundary,\n * throwing `GraphSpecValidationError` when the node is not a well-formed\n * pipe-call node.\n *\n * Graph-topology analysis (`buildGraph` and friends) identifies a node as a\n * pipe by its edges, not by re-checking `kind`/`pipe_code`. When the spec has\n * been through `validateGraphSpec` this always holds — but library consumers\n * may call `buildGraph` directly. This guard turns that gap into a loud,\n * greppable failure instead of a bare `TypeError` on a missing `pipe_code`.\n */\nexport function asPipeCallNode(node: GraphSpecNode, path = \"node\"): PipeCallNode {\n if (node.kind !== \"controller\" && node.kind !== \"operator\") {\n fail(\n `${path}.kind`,\n `expected a pipe-call node (\"controller\" or \"operator\"), ` +\n `got ${JSON.stringify(node.kind)}`,\n );\n }\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n return node as PipeCallNode;\n}\n\n/**\n * Narrow a `GraphSpecNode` to a `CardNode` — a pipe-call node or a binding\n * node, the two families the graph draws as a card — throwing\n * `GraphSpecValidationError` when the node is neither.\n *\n * The dataflow graph draws every node that produces or consumes a stuff, which\n * a binding node does as much as an operator: it reads its root and produces\n * what it binds. Same boundary contract as `asPipeCallNode`.\n */\nexport function asCardNode(node: GraphSpecNode, path = \"node\"): CardNode {\n if (node.kind === \"binding\") {\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n if (node.pipe_type !== BINDING_STEP_TYPE) {\n fail(\n `${path}.pipe_type`,\n `expected \"${BINDING_STEP_TYPE}\" on a binding node, got ${JSON.stringify(node.pipe_type)}`,\n );\n }\n return node as CardNode;\n }\n return asPipeCallNode(node, path);\n}\n","import type {\n GraphSpec,\n DataflowAnalysis,\n PipeBlueprintUnion,\n ConceptInfo,\n StuffRegistryEntry,\n} from \"./types\";\n\nexport function buildDataflowAnalysis(graphspec: GraphSpec | null): DataflowAnalysis | null {\n if (!graphspec) return null;\n\n const stuffRegistry: Record<string, StuffRegistryEntry> = {};\n const stuffProducers: Record<string, string[]> = {};\n const stuffConsumers: Record<string, string[]> = {};\n const containmentTree: Record<string, string[]> = {};\n const childNodeIds = new Set<string>();\n\n // Build containment tree from edges\n for (const edge of graphspec.edges) {\n if (edge.kind === \"contains\") {\n if (!containmentTree[edge.source]) containmentTree[edge.source] = [];\n containmentTree[edge.source].push(edge.target);\n childNodeIds.add(edge.target);\n }\n }\n\n // Controller IDs are nodes that have children\n const controllerNodeIds = new Set<string>(Object.keys(containmentTree));\n\n // Register stuffs from all nodes; track producers/consumers from operators only\n for (const node of graphspec.nodes) {\n const nodeIo = node.io;\n const isController = controllerNodeIds.has(node.id);\n\n // Register outputs\n for (const output of nodeIo.outputs) {\n if (output.digest && !stuffRegistry[output.digest]) {\n stuffRegistry[output.digest] = {\n name: output.name,\n concept: output.concept,\n multiplicity: output.multiplicity,\n contentType: output.content_type,\n };\n }\n if (output.digest && !isController) {\n if (!stuffProducers[output.digest]) stuffProducers[output.digest] = [];\n stuffProducers[output.digest].push(node.id);\n }\n }\n\n // Register inputs\n for (const input of nodeIo.inputs) {\n if (input.digest && !stuffRegistry[input.digest]) {\n stuffRegistry[input.digest] = {\n name: input.name,\n concept: input.concept,\n multiplicity: input.multiplicity,\n contentType: input.content_type,\n };\n }\n if (input.digest && !isController) {\n if (!stuffConsumers[input.digest]) stuffConsumers[input.digest] = [];\n stuffConsumers[input.digest].push(node.id);\n }\n }\n }\n\n return {\n stuffRegistry,\n stuffProducers,\n stuffConsumers,\n controllerNodeIds,\n childNodeIds,\n containmentTree,\n };\n}\n\n/**\n * Build a map from node id -> controller id for all nodes that belong to a controller.\n * Includes both direct children (operators) and stuff nodes assigned to controllers.\n *\n * Stuff nodes are placed at the lowest controller level where they connect producers\n * to consumers. A stuff node produced inside controller C is promoted to C's parent\n * if none of its consumers are inside C (output flows outward).\n */\nexport function buildChildToControllerMap(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n): Record<string, string> {\n const childToController: Record<string, string> = {};\n\n // Direct children from containment tree\n for (const [ctrlId, children] of Object.entries(analysis.containmentTree)) {\n for (const childId of children) {\n childToController[childId] = ctrlId;\n }\n }\n\n // Stuff nodes produced by operators inside controllers. A stuff with several\n // producers (a condition's output, one per outcome) starts in the deepest\n // controller holding all of them, so no outcome's edge has to leave its group\n // to reach it.\n for (const [digest, producerIds] of Object.entries(analysis.stuffProducers)) {\n const stuffId = \"stuff_\" + digest;\n const ctrlId = commonController(producerIds, childToController);\n if (ctrlId) {\n childToController[stuffId] = ctrlId;\n }\n }\n\n // Stuff produced by controllers themselves -> assign to parent controller\n for (const node of graphspec.nodes) {\n if (!analysis.controllerNodeIds.has(node.id)) continue;\n const parentCtrlId = childToController[node.id];\n if (!parentCtrlId) continue;\n for (const output of node.io.outputs) {\n if (!output.digest) continue;\n const stuffId = \"stuff_\" + output.digest;\n if (!childToController[stuffId]) {\n childToController[stuffId] = parentCtrlId;\n }\n }\n }\n\n // Batch item stuff (fan-out) -> assign to the PipeBatch controller\n for (const edge of graphspec.edges) {\n if (edge.kind === \"batch_item\" && edge.target_stuff_digest) {\n const stuffId = \"stuff_\" + edge.target_stuff_digest;\n // edge.source is the PipeBatch controller node\n if (analysis.controllerNodeIds.has(edge.source)) {\n childToController[stuffId] = edge.source;\n }\n }\n }\n\n // ─── Promote stuff nodes whose consumers are all outside their controller ──\n // Stuff involved in stuff-to-stuff edges (batch/parallel) should not be promoted\n // when they have no operator consumers — they're intermediate batch/parallel data.\n const stuffInStuffEdges = new Set<string>();\n for (const edge of graphspec.edges) {\n if (\n edge.kind === \"batch_item\" ||\n edge.kind === \"batch_aggregate\" ||\n edge.kind === \"parallel_combine\"\n ) {\n if (edge.source_stuff_digest) stuffInStuffEdges.add(edge.source_stuff_digest);\n if (edge.target_stuff_digest) stuffInStuffEdges.add(edge.target_stuff_digest);\n }\n }\n\n const stuffPrefix = \"stuff_\";\n const stuffEntries = Object.keys(childToController).filter((id) => id.startsWith(stuffPrefix));\n\n for (const stuffId of stuffEntries) {\n const digest = stuffId.slice(stuffPrefix.length);\n let assignedCtrl: string | undefined = childToController[stuffId];\n if (!assignedCtrl) continue;\n\n const consumers = analysis.stuffConsumers[digest] || [];\n\n if (consumers.length === 0) {\n // No operator consumers — promote to root only if this is a pure final output\n // (not involved in batch/parallel stuff-to-stuff edges)\n if (!stuffInStuffEdges.has(digest)) {\n delete childToController[stuffId];\n }\n continue;\n }\n\n // Has consumers — promote until we find a level where at least one consumer is inside\n while (assignedCtrl) {\n const ctrl = assignedCtrl;\n const hasConsumerInside = consumers.some((consumerId) =>\n isDescendantOf(consumerId, ctrl, childToController),\n );\n if (hasConsumerInside) break;\n\n const parentCtrl: string | undefined = childToController[assignedCtrl];\n if (parentCtrl) {\n childToController[stuffId] = parentCtrl;\n assignedCtrl = parentCtrl;\n } else {\n delete childToController[stuffId];\n assignedCtrl = undefined;\n }\n }\n }\n\n return childToController;\n}\n\n/** The deepest controller every one of these nodes sits inside, or undefined at the root. */\nfunction commonController(\n nodeIds: readonly string[],\n childToController: Record<string, string>,\n): string | undefined {\n if (nodeIds.length === 0) return undefined;\n let candidate: string | undefined = childToController[nodeIds[0]];\n while (candidate) {\n const ctrl: string = candidate;\n if (nodeIds.every((id) => isDescendantOf(id, ctrl, childToController))) return ctrl;\n candidate = childToController[ctrl];\n }\n return undefined;\n}\n\n/** Check if nodeId is a descendant of ancestorCtrlId in the containment hierarchy. */\nfunction isDescendantOf(\n nodeId: string,\n ancestorCtrlId: string,\n childToController: Record<string, string>,\n): boolean {\n let current = childToController[nodeId];\n while (current) {\n if (current === ancestorCtrlId) return true;\n current = childToController[current];\n }\n return false;\n}\n\n// ─── Registry lookup helpers ───────────────────────────────────────────────\n\nexport function getPipeBlueprint(spec: GraphSpec, pipeRef: string): PipeBlueprintUnion | undefined {\n return spec.pipe_registry?.[pipeRef];\n}\n\nexport function getConceptInfo(spec: GraphSpec, conceptRef: string): ConceptInfo | undefined {\n return spec.concept_registry?.[conceptRef];\n}\n\nexport function resolveConceptRef(spec: GraphSpec, codeOrRef: string): ConceptInfo | undefined {\n if (!spec.concept_registry) return undefined;\n // Direct lookup first (e.g., \"test_domain.Summary\")\n const direct = spec.concept_registry[codeOrRef];\n if (direct) return direct;\n // Search by code (e.g., \"Summary\" matches \"test_domain.Summary\")\n for (const info of Object.values(spec.concept_registry)) {\n if (info.code === codeOrRef) return info;\n }\n return undefined;\n}\n","import type { CardNode, GraphSpecMode, GraphSpecNodeIoItem, PipeCardPayload } from \"./types\";\nimport { multiplicitySuffix } from \"./types\";\n\n/** An io item as a card pill: its name, and its concept with the multiplicity marker (`Document[]`). */\nfunction ioPill(item: GraphSpecNodeIoItem): { name: string; concept: string } {\n return {\n name: item.name,\n concept: item.concept ? item.concept + multiplicitySuffix(item.multiplicity) : \"\",\n };\n}\n\n/**\n * Build a PipeCardPayload from a card node: a pipe-call node, or a binding\n * node, whose card names its `from` path where a pipe card names its pipe code.\n *\n * `validateGraphSpec` guarantees the node's `pipe_code`, `pipe_type`,\n * `description`, `status`, and `io` are present and well-formed, so this\n * function reads them directly with no fallback synthesis.\n */\nexport function buildPipeCardPayload(node: CardNode, graphMode?: GraphSpecMode): PipeCardPayload {\n const payload: PipeCardPayload = {\n pipeCode: node.pipe_code,\n pipeType: node.pipe_type,\n description: node.description,\n status: node.status,\n inputs: node.io.inputs.map(ioPill),\n outputs: node.io.outputs.map(ioPill),\n };\n if (graphMode !== undefined) payload.graphMode = graphMode;\n if (node.tags !== undefined) payload.tags = node.tags;\n return payload;\n}\n","import type { GraphSpec, DataflowAnalysis, GraphNode, GraphEdge, GraphData } from \"./types\";\nimport {\n ARROW_CLOSED_MARKER,\n NODE_TYPE_PIPE_CARD,\n NODE_TYPE_STUFF,\n graphSpecMode,\n isBindingNode,\n multiplicitySuffix,\n stuffNodeId,\n} from \"./types\";\nimport { buildDataflowAnalysis, buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asCardNode } from \"./validateGraphSpec\";\nimport { buildPipeCardPayload } from \"./pipeCardPayload\";\n\nconst STUFF_CHAR_WIDTH_PX = 7;\nconst STUFF_LABEL_PADDING = 48;\nconst MIN_STUFF_WIDTH = 140;\n\n/**\n * Build dataflow graph from GraphSpec. Creates pipe nodes + stuff (data) nodes +\n * producer/consumer edges. Returns label descriptors (not React elements).\n */\nexport function buildDataflowGraph(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n edgeType: string,\n): GraphData {\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n const graphMode = graphSpecMode(graphspec);\n const combinedStuffDigests = new Set<string>();\n for (const edge of graphspec.edges) {\n if (edge.kind === \"parallel_combine\" && edge.target_stuff_digest) {\n combinedStuffDigests.add(edge.target_stuff_digest);\n }\n }\n\n // Find participating pipes (those that produce or consume data)\n const participatingPipes = new Set<string>();\n for (const producers of Object.values(analysis.stuffProducers)) {\n for (const producer of producers) {\n participatingPipes.add(producer);\n }\n }\n for (const consumers of Object.values(analysis.stuffConsumers)) {\n for (const consumer of consumers) {\n participatingPipes.add(consumer);\n }\n }\n\n // Create pipe nodes (only those that participate in data flow). A binding\n // node participates like an operator — it reads its root and produces what it\n // binds — so it is drawn as a card too, which says it binds rather than runs.\n // A binding is drawn even with no data flow: one that failed, or was skipped\n // because its root or its single result was absent, closes with empty IO, and\n // it is still a step of its sequence whose status and error the viewer shows.\n for (const node of graphspec.nodes) {\n if (!participatingPipes.has(node.id) && !isBindingNode(node)) continue;\n // A drawn node is always a pipe-call or binding node; this guard turns a\n // malformed spec into a loud, greppable error rather than a bare TypeError.\n const pipeNode = asCardNode(node, `nodes[${node.id}]`);\n\n const isFailed = pipeNode.status === \"failed\";\n const label = pipeNode.pipe_code;\n const pipeCardData = buildPipeCardPayload(pipeNode, graphMode);\n\n nodes.push({\n id: pipeNode.id,\n type: NODE_TYPE_PIPE_CARD,\n data: {\n labelDescriptor: { kind: \"pipe\", label, isFailed },\n nodeData: pipeNode,\n isPipe: true,\n isStuff: false,\n labelText: label,\n pipeCode: pipeCardData.pipeCode,\n pipeType: pipeNode.pipe_type,\n graphMode,\n pipeCardData,\n },\n position: { x: 0, y: 0 },\n });\n }\n\n // Create stuff (data) nodes\n for (const [digest, stuffInfo] of Object.entries(analysis.stuffRegistry)) {\n const stuffId = stuffNodeId(digest);\n const label = stuffInfo.name;\n // The marked ref (`Document[]`), so a list reads as a list on the canvas.\n const concept = stuffInfo.concept\n ? stuffInfo.concept + multiplicitySuffix(stuffInfo.multiplicity)\n : \"\";\n const textWidth =\n Math.max(label.length, concept.length) * STUFF_CHAR_WIDTH_PX + STUFF_LABEL_PADDING;\n const stuffWidth = Math.max(MIN_STUFF_WIDTH, textWidth);\n\n // Classify: input (no producer), output (no consumer), or intermediate\n const isCombined = combinedStuffDigests.has(digest);\n const isInput = !analysis.stuffProducers[digest]?.length && !isCombined;\n const isOutput = !isInput && !isCombined && !analysis.stuffConsumers[digest]?.length;\n const stuffRole = isCombined\n ? (\"combined\" as const)\n : isInput\n ? (\"input\" as const)\n : isOutput\n ? (\"output\" as const)\n : undefined;\n\n const borderColor = isInput\n ? \"var(--color-stuff-input-border, #50FA7B)\"\n : isOutput\n ? \"var(--color-stuff-output-border, #a78bfa)\"\n : isCombined\n ? \"var(--color-parallel-combine)\"\n : \"var(--color-stuff-border)\";\n\n nodes.push({\n id: stuffId,\n type: NODE_TYPE_STUFF,\n data: {\n labelDescriptor: { kind: \"stuff\", label, concept },\n isStuff: true,\n isPipe: false,\n labelText: label,\n stuffRole,\n stuffDigest: digest,\n },\n position: { x: 0, y: 0 },\n style: {\n background: \"var(--color-stuff-bg)\",\n border: `2px solid ${borderColor}`,\n borderRadius: \"999px\",\n padding: \"0\",\n width: stuffWidth + \"px\",\n boxShadow: \"var(--shadow-md)\",\n },\n });\n }\n\n // Create edges: producer -> stuff\n let edgeId = 0;\n for (const [digest, producers] of Object.entries(analysis.stuffProducers)) {\n const stuffId = stuffNodeId(digest);\n for (const producerNodeId of producers) {\n edges.push({\n id: \"edge_\" + edgeId++,\n source: producerNodeId,\n target: stuffId,\n type: edgeType,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 2 },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-edge)\",\n },\n });\n }\n }\n\n // Create edges: stuff -> consumer\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n const stuffId = stuffNodeId(digest);\n for (const consumerNodeId of consumers) {\n edges.push({\n id: \"edge_\" + edgeId++,\n source: stuffId,\n target: consumerNodeId,\n type: edgeType,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 2 },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-edge)\",\n },\n });\n }\n }\n\n // Create PARALLEL_COMBINE edges from GraphSpec\n for (const edge of graphspec.edges) {\n if (edge.kind !== \"parallel_combine\") continue;\n if (!edge.source_stuff_digest || !edge.target_stuff_digest) continue;\n if (\n !analysis.stuffRegistry[edge.source_stuff_digest] ||\n !analysis.stuffRegistry[edge.target_stuff_digest]\n )\n continue;\n const sourceId = stuffNodeId(edge.source_stuff_digest);\n const targetId = stuffNodeId(edge.target_stuff_digest);\n\n edges.push({\n id: edge.id,\n source: sourceId,\n target: targetId,\n type: \"smoothstep\",\n animated: false,\n style: {\n stroke: \"var(--color-parallel-combine)\",\n strokeWidth: 2,\n strokeDasharray: \"5,5\",\n },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-parallel-combine)\",\n },\n });\n }\n\n // Create BATCH_ITEM and BATCH_AGGREGATE edges (data-centric mode: stuff -> stuff)\n for (const edge of graphspec.edges) {\n if (edge.kind !== \"batch_item\" && edge.kind !== \"batch_aggregate\") continue;\n\n if (!edge.source_stuff_digest || !edge.target_stuff_digest) continue;\n if (\n !analysis.stuffRegistry[edge.source_stuff_digest] ||\n !analysis.stuffRegistry[edge.target_stuff_digest]\n )\n continue;\n const sourceId = stuffNodeId(edge.source_stuff_digest);\n const targetId = stuffNodeId(edge.target_stuff_digest);\n const isBatchItem = edge.kind === \"batch_item\";\n\n edges.push({\n id: edge.id,\n source: sourceId,\n target: targetId,\n type: edgeType,\n animated: false,\n _batchEdge: true,\n label: edge.label || \"\",\n labelStyle: {\n fontSize: \"10px\",\n fontFamily: \"var(--font-mono)\",\n fill: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n },\n labelBgStyle: { fill: \"var(--color-bg)\", fillOpacity: 0.9 },\n style: {\n stroke: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n strokeWidth: 2,\n strokeDasharray: \"5,5\",\n },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n },\n });\n }\n\n // Mark edges that cross between different sibling controller groups\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n // and assign per-class edge types for better routing\n for (const edge of edges) {\n const srcCtrl = childToCtrl[edge.source] || null;\n const tgtCtrl = childToCtrl[edge.target] || null;\n if (srcCtrl && tgtCtrl && srcCtrl !== tgtCtrl) {\n edge._crossGroup = true;\n // Keep bezier for long-distance cross-group edges (natural curves look better)\n // but visually de-emphasize to reduce spaghetti effect\n edge.style = {\n ...edge.style,\n strokeWidth: 1.5,\n opacity: 0.65,\n };\n }\n }\n\n // Batch edges: keep bezier but visually differentiate\n for (const edge of edges) {\n if (edge._batchEdge) {\n edge.style = {\n ...edge.style,\n opacity: 0.7,\n };\n }\n }\n\n return { nodes, edges };\n}\n\n/**\n * Build graph from GraphSpec using dataflow mode.\n * Returns the built graph data and analysis.\n */\nexport function buildGraph(\n graphspec: GraphSpec | null,\n edgeType: string,\n): { graphData: GraphData; analysis: DataflowAnalysis | null } {\n if (graphspec) {\n const analysis = buildDataflowAnalysis(graphspec);\n // A graph with no data flow at all is still drawn when it holds a binding\n // node, which `buildDataflowGraph` draws whatever its IO.\n if (\n analysis &&\n (Object.keys(analysis.stuffProducers).length > 0 ||\n Object.keys(analysis.stuffConsumers).length > 0 ||\n graphspec.nodes.some(isBindingNode))\n ) {\n return { graphData: buildDataflowGraph(graphspec, analysis, edgeType), analysis };\n }\n }\n return { graphData: { nodes: [], edges: [] }, analysis: null };\n}\n","import type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphData,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphSpecNode,\n} from \"./types\";\nimport {\n ARROW_CLOSED_MARKER,\n NODE_TYPE_PIPE_CARD,\n graphSpecMode,\n isStuffNodeId,\n stuffDigestFromId,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asPipeCallNode } from \"./validateGraphSpec\";\nimport { buildPipeCardPayload } from \"./pipeCardPayload\";\n\n/**\n * Find every controller that shares the same `pipe_code` as `controllerId` —\n * the \"cousins\" of the clicked controller (other instances of the same pipe,\n * possibly living in different branches of the graph).\n *\n * The result always includes `controllerId` itself. If the controller has no\n * `pipe_code` or no cousins exist, returns a singleton set.\n *\n * Used by GraphViewer to mirror fold/expand actions across all instances of a\n * pipe — the default behavior — while alt/option-click bypasses cousin lookup\n * and toggles only the clicked controller.\n */\nexport function findCousinControllers(\n controllerId: string,\n graphspec: GraphSpec,\n controllerNodeIds: ReadonlySet<string>,\n): Set<string> {\n const result = new Set<string>([controllerId]);\n const clicked = graphspec.nodes.find((n) => n.id === controllerId);\n const pipeCode = clicked?.pipe_code;\n if (!pipeCode) return result;\n for (const node of graphspec.nodes) {\n if (node.pipe_code !== pipeCode) continue;\n if (!controllerNodeIds.has(node.id)) continue;\n result.add(node.id);\n }\n return result;\n}\n\n/**\n * Walk the containment chain from `nodeId` upward, returning the ordered list of\n * ancestor controller IDs from immediate parent → root.\n */\nexport function buildContainmentChain(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n): string[] {\n const chain: string[] = [];\n let current: string | undefined = childToCtrl[nodeId];\n const seen = new Set<string>();\n while (current && !seen.has(current)) {\n chain.push(current);\n seen.add(current);\n current = childToCtrl[current];\n }\n return chain;\n}\n\n/**\n * Find the **outermost** (closest to root) ancestor of `nodeId` whose ID is in\n * `foldedSet`. Returns `null` if no ancestor is folded.\n */\nexport function outermostFoldedAncestor(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n): string | null {\n const chain = buildContainmentChain(nodeId, childToCtrl);\n let outermost: string | null = null;\n for (const ancestorId of chain) {\n if (foldedSet.has(ancestorId)) outermost = ancestorId;\n }\n return outermost;\n}\n\n/**\n * Effective ID after folding: returns the outermost folded ancestor if one\n * exists; otherwise the node itself.\n */\nfunction effectiveId(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n): string {\n return outermostFoldedAncestor(nodeId, childToCtrl, foldedSet) ?? nodeId;\n}\n\nfunction findSpecNode(graphspec: GraphSpec, id: string): GraphSpecNode | undefined {\n return graphspec.nodes.find((n) => n.id === id);\n}\n\n/**\n * Apply the fold transformation to a dataflow graph.\n *\n * For each controller in `foldedSet` that is not itself inside another folded\n * controller (outermost-wins): emit a single `pipe-card` node carrying the\n * controller's payload, and rewrite/dedup edges so the controller card replaces\n * its hidden descendants.\n *\n * The function is pure — inputs are not mutated; a fresh `{ nodes, edges,\n * analysis }` is returned.\n */\nexport function applyFolds(\n graphData: GraphData,\n analysis: DataflowAnalysis,\n graphspec: GraphSpec,\n foldedSet: ReadonlySet<string>,\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void,\n): { nodes: GraphNode[]; edges: GraphEdge[]; analysis: DataflowAnalysis } {\n if (foldedSet.size === 0) {\n return { nodes: graphData.nodes, edges: graphData.edges, analysis };\n }\n\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n const graphMode = graphSpecMode(graphspec);\n\n // ─── Promote declared-output stuffs out of folded controllers ───────────\n // A folded controller's pipe-card represents the controller as a whole, and\n // its declared outputs are external data flowing out of it. If a stuff node\n // currently lives inside a folded controller that declares it as an output,\n // moving the stuff outside lets the folded card connect to it just like\n // before the fold. Without this, the stuff would be hidden alongside the\n // controller's internals, and the output edge would collapse to a self-loop.\n const outputDeclarers: Map<string, Set<string>> = new Map();\n for (const controllerId of foldedSet) {\n const ctrlNode = findSpecNode(graphspec, controllerId);\n const outputs = ctrlNode?.io?.outputs;\n if (!outputs) continue;\n for (const output of outputs) {\n if (!output.digest) continue;\n let set = outputDeclarers.get(output.digest);\n if (!set) {\n set = new Set();\n outputDeclarers.set(output.digest, set);\n }\n set.add(controllerId);\n }\n }\n\n if (outputDeclarers.size > 0) {\n for (const node of graphData.nodes) {\n if (!isStuffNodeId(node.id)) continue;\n const declarers = outputDeclarers.get(stuffDigestFromId(node.id));\n if (!declarers) continue;\n\n // Walk the chain to find the outermost folded ancestor that declares this\n // stuff as one of its outputs. Reparenting to that ancestor's parent\n // (which may itself be folded or root) ensures the stuff escapes the fold.\n let current: string | undefined = childToCtrl[node.id];\n let outermostDeclarer: string | null = null;\n const seen = new Set<string>();\n while (current && !seen.has(current)) {\n seen.add(current);\n if (declarers.has(current)) outermostDeclarer = current;\n current = childToCtrl[current];\n }\n if (!outermostDeclarer) continue;\n\n const newParent = childToCtrl[outermostDeclarer];\n if (newParent) {\n childToCtrl[node.id] = newParent;\n } else {\n delete childToCtrl[node.id];\n }\n }\n }\n\n // ─── Filter visible nodes ───────────────────────────────────────────────\n // Drop any node whose outermost folded ancestor is non-null (it's hidden\n // inside a folded controller).\n const visibleNodes: GraphNode[] = [];\n for (const node of graphData.nodes) {\n if (outermostFoldedAncestor(node.id, childToCtrl, foldedSet)) continue;\n visibleNodes.push(node);\n }\n\n // ─── Emit pipe-card nodes for outermost-folded controllers ──────────────\n for (const folded of foldedSet) {\n // Skip if this folded controller is itself inside another folded ancestor.\n if (outermostFoldedAncestor(folded, childToCtrl, foldedSet)) continue;\n const specNode = findSpecNode(graphspec, folded);\n if (!specNode) continue; // unknown ID — silently ignore\n\n // A folded controller is a pipe-call node; guard the narrowing so a\n // malformed spec fails loudly instead of producing an undefined pipeCode.\n const payload = buildPipeCardPayload(asPipeCallNode(specNode, folded), graphMode);\n if (onToggleFold) {\n payload.onExpand = (options?: FoldToggleOptions) => onToggleFold(folded, options);\n }\n\n const cardNode: GraphNode = {\n id: folded,\n type: NODE_TYPE_PIPE_CARD,\n data: {\n labelDescriptor: {\n kind: \"pipe\",\n label: payload.pipeCode,\n isFailed: payload.status === \"failed\",\n },\n nodeData: specNode,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: payload.pipeCode,\n pipeCode: payload.pipeCode,\n pipeType: specNode.pipe_type,\n graphMode,\n pipeCardData: payload,\n },\n position: { x: 0, y: 0 },\n };\n visibleNodes.push(cardNode);\n }\n\n // ─── Build updated analysis ─────────────────────────────────────────────\n // Drop folded controllers and any controller that lives inside a folded\n // outermost ancestor.\n const updatedControllerIds = new Set<string>();\n for (const ctrlId of analysis.controllerNodeIds) {\n if (foldedSet.has(ctrlId)) continue;\n if (outermostFoldedAncestor(ctrlId, childToCtrl, foldedSet)) continue;\n updatedControllerIds.add(ctrlId);\n }\n\n // containmentTree: only keep entries for surviving controllers; filter their\n // children to drop hidden ones (children whose outermost folded ancestor is\n // a different controller). Children that are the folded controllers themselves\n // should remain as children (they now render as pipe-card leaves).\n const updatedContainmentTree: Record<string, string[]> = {};\n for (const ctrlId of updatedControllerIds) {\n const originalChildren = analysis.containmentTree[ctrlId] ?? [];\n const survivors: string[] = [];\n for (const childId of originalChildren) {\n const outer = outermostFoldedAncestor(childId, childToCtrl, foldedSet);\n // If the child has a folded outermost ancestor that is NOT itself, drop.\n // If the child IS the folded outermost (childId is in foldedSet), keep —\n // it will appear as a pipe-card child of this controller.\n if (outer && outer !== childId) continue;\n survivors.push(childId);\n }\n updatedContainmentTree[ctrlId] = survivors;\n }\n\n // childNodeIds: rebuild from the updated containment tree.\n const updatedChildNodeIds = new Set<string>();\n for (const children of Object.values(updatedContainmentTree)) {\n for (const child of children) updatedChildNodeIds.add(child);\n }\n\n // Rewrite stuffProducers/stuffConsumers so any reference to an operator hidden\n // by a fold is replaced with its outermost folded ancestor — mirroring the\n // edge-endpoint rewrite below. Without this, buildChildToControllerMap's\n // promotion loop loses the consumer/producer trail and stuff nodes get\n // ejected to the root level when their parent controller's sibling is folded.\n // Deduplicated like the consumers: the outcomes of a folded condition all\n // produce its one output, and the folded card is that output's one producer.\n const updatedStuffProducers: Record<string, string[]> = {};\n for (const [digest, producers] of Object.entries(analysis.stuffProducers)) {\n const seen = new Set<string>();\n for (const producerId of producers) {\n seen.add(effectiveId(producerId, childToCtrl, foldedSet));\n }\n updatedStuffProducers[digest] = [...seen];\n }\n\n const updatedStuffConsumers: Record<string, string[]> = {};\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n const seen = new Set<string>();\n for (const consumerId of consumers) {\n seen.add(effectiveId(consumerId, childToCtrl, foldedSet));\n }\n updatedStuffConsumers[digest] = [...seen];\n }\n\n const updatedAnalysis: DataflowAnalysis = {\n stuffRegistry: analysis.stuffRegistry,\n stuffProducers: updatedStuffProducers,\n stuffConsumers: updatedStuffConsumers,\n controllerNodeIds: updatedControllerIds,\n childNodeIds: updatedChildNodeIds,\n containmentTree: updatedContainmentTree,\n };\n\n // ─── Rewrite edges ──────────────────────────────────────────────────────\n // Each surviving endpoint is replaced by its effective (outermost-folded)\n // ancestor or itself. Drop self-loops. Dedup by (newSrc, newDst, bucket).\n const dedupMap = new Map<string, GraphEdge>();\n for (const edge of graphData.edges) {\n const newSrc = effectiveId(edge.source, childToCtrl, foldedSet);\n const newDst = effectiveId(edge.target, childToCtrl, foldedSet);\n if (newSrc === newDst) continue;\n const bucket = edge._batchEdge ? \"batch\" : \"data\";\n const key = `${newSrc}->${newDst}|${bucket}`;\n if (dedupMap.has(key)) continue;\n // Clone the edge with rewritten endpoints; drop the stale _crossGroup flag\n // (recomputed below against folded containment).\n const cloned: GraphEdge = {\n ...edge,\n source: newSrc,\n target: newDst,\n id: edge.id,\n };\n // Batch edges carry per-item indices like \"[0]\", \"[1]\" in their labels.\n // Once a fold collapses many of these into a single edge (or hides the\n // per-item target), the surviving index is misleading — generalize to \"[N]\".\n if (edge._batchEdge && (newSrc !== edge.source || newDst !== edge.target)) {\n cloned.label = \"[N]\";\n }\n delete cloned._crossGroup;\n dedupMap.set(key, cloned);\n }\n\n // Recompute _crossGroup against the folded containment. Build a fresh\n // childToCtrl from the updated analysis so the classification reflects the\n // post-fold node graph (folded controllers no longer have children).\n const foldedChildToCtrl = buildChildToControllerMap(\n {\n ...graphspec,\n // Mask out the contains edges for folded outermost controllers — they no\n // longer logically contain anything after folding.\n edges: graphspec.edges.filter((e) => {\n if (e.kind !== \"contains\") return true;\n return updatedControllerIds.has(e.source);\n }),\n },\n updatedAnalysis,\n );\n\n const rewrittenEdges: GraphEdge[] = [];\n for (const edge of dedupMap.values()) {\n const srcCtrl = foldedChildToCtrl[edge.source] || null;\n const tgtCtrl = foldedChildToCtrl[edge.target] || null;\n if (srcCtrl && tgtCtrl && srcCtrl !== tgtCtrl) {\n edge._crossGroup = true;\n edge.style = {\n ...edge.style,\n strokeWidth: 1.5,\n opacity: 0.65,\n };\n } else if (edge.style && (edge.style.opacity === 0.65 || edge.style.strokeWidth === 1.5)) {\n // Edge no longer crosses sibling groups — reset the de-emphasized style.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { opacity: _opacity, strokeWidth: _strokeWidth, ...rest } = edge.style;\n edge.style = { ...rest, strokeWidth: 2 };\n if (!edge.markerEnd) {\n edge.markerEnd = { type: ARROW_CLOSED_MARKER, color: \"var(--color-edge)\" };\n }\n }\n rewrittenEdges.push(edge);\n }\n\n return { nodes: visibleNodes, edges: rewrittenEdges, analysis: updatedAnalysis };\n}\n","// Validation decorations — project targeted ValidationIssues onto rendered nodes.\n//\n// Pure module (no React): GraphViewer derives a per-node decoration map from\n// the same `validationIssues` prop that feeds the validation panel, then stamps\n// it onto node data. Node components render the ring + badge from that stamp.\n\nimport type { GraphNodeData, GraphSpec, NodeValidationSummary, ValidationIssue } from \"./types\";\nimport { outermostFoldedAncestor } from \"./graphFolds\";\nimport { isBindingNode } from \"./types\";\nimport { makePipeRef } from \"./pipeRefs\";\n\n/**\n * Resolve which GraphSpec node ids an issue targets:\n * - `nodeId` → that precise invocation (wins over `pipeRef` when both are set);\n * - `pipeRef` → every spec node invoking that exact `domain_code.pipe_code`\n * pipe (a pipe can appear in several places in the graph). The match is on\n * the qualified ref — never on the bare code — so two domains declaring the\n * same pipe code can never cross-decorate. A node missing either identity\n * field cannot be qualified and is never matched, and neither is a binding\n * node, whose `pipe_code` is the path it binds and names no pipe;\n * - neither → no targets (the issue stays panel-only).\n */\nfunction issueTargetIds(issue: ValidationIssue, graphspec: GraphSpec): string[] {\n if (issue.nodeId) return [issue.nodeId];\n if (issue.pipeRef) {\n return graphspec.nodes\n .filter(\n (n) =>\n !isBindingNode(n) &&\n n.domain_code !== undefined &&\n n.pipe_code !== undefined &&\n makePipeRef(n.domain_code, n.pipe_code) === issue.pipeRef,\n )\n .map((n) => n.id);\n }\n return [];\n}\n\n/**\n * Build the per-node decoration map for the current fold state.\n *\n * Each issue's target ids are remapped through the fold containment\n * (`outermostFoldedAncestor`): an issue on a node hidden inside a folded\n * controller decorates the folded controller's card instead, so folding never\n * hides an error. They then go through the style's `standIns`: an issue on a\n * node the style does not draw decorates the drawn node standing for it, such\n * as a batch item's run in the simple style, drawn once for every item. Targets\n * that don't resolve to a rendered node (e.g. a\n * diagnostic about a pipe that was skipped during the static walk) simply\n * produce no decoration — those issues stay panel-only.\n *\n * Counts are per issue × target invocation: a folded controller containing two\n * invocations of a broken pipe shows 2, matching the sum of the badges that\n * become visible when it is expanded.\n */\nexport function buildValidationDecorations(\n issues: readonly ValidationIssue[] | undefined,\n graphspec: GraphSpec | null,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n standIns?: ReadonlyMap<string, string>,\n): Map<string, NodeValidationSummary> {\n const decorations = new Map<string, NodeValidationSummary>();\n if (!issues || issues.length === 0 || !graphspec) return decorations;\n\n for (const issue of issues) {\n for (const targetId of issueTargetIds(issue, graphspec)) {\n const visibleId = visibleNodeId(targetId, childToCtrl, foldedSet, standIns);\n const existing = decorations.get(visibleId);\n const lines = [issue.message, ...(issue.suggestedFix ? [`Fix: ${issue.suggestedFix}`] : [])];\n if (existing) {\n if (issue.severity === \"error\") existing.severity = \"error\";\n existing.count += 1;\n existing.lines.push(...lines);\n } else {\n decorations.set(visibleId, { severity: issue.severity, count: 1, lines });\n }\n }\n }\n return decorations;\n}\n\n/**\n * The node drawn for a target: its outermost folded ancestor when it is folded\n * away, then whatever the style draws in its place (`StyleProjection.standIns`).\n */\nfunction visibleNodeId(\n targetId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n standIns: ReadonlyMap<string, string> | undefined,\n): string {\n const unfolded = outermostFoldedAncestor(targetId, childToCtrl, foldedSet) ?? targetId;\n return standIns?.get(unfolded) ?? unfolded;\n}\n\n/**\n * Resolve the rendered node a panel row should navigate to: the issue's first\n * target, remapped through the fold containment and the style's stand-ins, and\n * only if that id is actually rendered. `null` for panel-only issues (no\n * target, or the target never became a node).\n */\nexport function resolveIssueTargetNodeId(\n issue: ValidationIssue,\n graphspec: GraphSpec | null,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n renderedNodeIds: ReadonlySet<string>,\n standIns?: ReadonlyMap<string, string>,\n): string | null {\n if (!graphspec) return null;\n for (const targetId of issueTargetIds(issue, graphspec)) {\n const visibleId = visibleNodeId(targetId, childToCtrl, foldedSet, standIns);\n if (renderedNodeIds.has(visibleId)) return visibleId;\n }\n return null;\n}\n\n/**\n * Stamp the decoration map onto rendered nodes (and their pipe-card payloads).\n * Nodes whose decoration is unchanged are returned as-is; a node whose issues\n * disappeared gets its stamp cleared, so verdict flips are fully reactive.\n * `onBadgeClick` (when provided) rides along on decorated nodes only — the\n * badge renders as a button that opens the validation panel.\n */\nexport function applyValidationDecorations<T extends { id: string; data: GraphNodeData }>(\n nodes: T[],\n decorations: ReadonlyMap<string, NodeValidationSummary>,\n onBadgeClick?: () => void,\n): T[] {\n return nodes.map((node) => {\n const decoration = decorations.get(node.id);\n if (!decoration && !node.data.validation && !node.data.pipeCardData?.validation) return node;\n const badgeClick = decoration ? onBadgeClick : undefined;\n return {\n ...node,\n data: {\n ...node.data,\n validation: decoration,\n onValidationBadgeClick: badgeClick,\n pipeCardData: node.data.pipeCardData\n ? {\n ...node.data.pipeCardData,\n validation: decoration,\n onValidationBadgeClick: badgeClick,\n }\n : node.data.pipeCardData,\n },\n };\n });\n}\n","import { makePipeRef } from \"./pipeRefs\";\nimport type { GraphSpec, GraphSpecNode, GraphSpecNodeIoItem } from \"./types\";\nimport { isBindingNode } from \"./types\";\n\n/**\n * Where a data item lives in a `GraphSpec`, and which pipe put it there.\n *\n * The producing pipe is the half that is not obvious and the half that matters:\n * a stuff carries a concept, but the artifacts that describe how to RENDER it —\n * `output_form` and the output half of `pipe_io_contracts` — are keyed by\n * `pipe_ref`, not by concept. So a panel that wants to show a result as the\n * standard describes it has to walk from the data back to the pipe that\n * resolved to it, and this is that walk, done once at the boundary rather than\n * re-derived by every consumer.\n */\nexport interface StuffLocation {\n /** The item as the spec holds it, richest copy first (see below). */\n item: GraphSpecNodeIoItem;\n /**\n * `domain.code` of the pipe whose OUTPUT this is, when the spec names one.\n *\n * Absent for a pipeline-level input, which no pipe produced, for a value a\n * binding step bound, which no pipe produced either, and for a node missing\n * either half of the ref. A consumer must treat it as optional: a\n * method's own inputs are stuff too, and they are the common case at the top\n * of every graph.\n */\n producerPipeRef?: string;\n /**\n * The first pipe that CONSUMES it, and the slot it arrives in.\n *\n * The fallback identity for a method's own inputs, which no pipe produced and\n * which therefore no `output_form` describes. What does describe them is the\n * consuming pipe's `input_form` entry for that slot — the same field, seen\n * from the other side — so a renderer that holds the input artifacts can show\n * a method input as something better than a JSON dump.\n *\n * First rather than all, deliberately: several pipes may read the same stuff,\n * and every one of them describes it identically, because they are describing\n * the concept it carries. A list here would be a list of the same answer.\n */\n consumer?: { pipeRef: string; slotName: string };\n}\n\n/**\n * `domain.code`, or nothing when the node does not carry both halves, or is a\n * binding node: its `pipe_code` is the path it binds, so `domain.<path>` names\n * no pipe, and a path spelled like a pipe's code would pick up that pipe's\n * descriptors.\n *\n * Assembled with `makePipeRef` rather than a template literal so every consumer\n * agrees with the runtime on what a ref is — the domain path may be\n * multi-segment, and this repo has exactly one place that spelling lives.\n */\nexport function pipeRefOf(node: GraphSpecNode): string | undefined {\n if (isBindingNode(node) || !node.pipe_code || !node.domain_code) return undefined;\n return makePipeRef(node.domain_code, node.pipe_code);\n}\n\n/**\n * Find a data item by digest, preferring the pipe that PRODUCED it.\n *\n * Two passes, and the order is the point rather than an optimization. The same\n * digest appears on the producer's `outputs` and again on every consumer's\n * `inputs`, and only the producer's copy is guaranteed to carry the payload —\n * a consumer's may be a reference with `data` elided. Taking the first match in\n * spec order would therefore hand back an emptier copy of the same stuff\n * depending on how the nodes happened to be sorted.\n *\n * The second pass exists for the items no pipe produced: a method's declared\n * inputs appear only as some pipe's `inputs`, so a digest-first walk that\n * searched outputs alone would report the top of every graph as missing.\n */\nexport function findStuffByDigest(spec: GraphSpec, digest: string): StuffLocation | null {\n const consumer = findConsumer(spec, digest);\n for (const node of spec.nodes) {\n for (const output of node.io?.outputs ?? []) {\n if (output.digest === digest) {\n const producerPipeRef = pipeRefOf(node);\n return {\n item: output,\n ...(producerPipeRef ? { producerPipeRef } : {}),\n ...(consumer ? { consumer } : {}),\n };\n }\n }\n }\n for (const node of spec.nodes) {\n for (const input of node.io?.inputs ?? []) {\n if (input.digest === digest) return { item: input, ...(consumer ? { consumer } : {}) };\n }\n }\n return null;\n}\n\n/** The first pipe reading this digest, and the slot name it reads it as. */\nfunction findConsumer(\n spec: GraphSpec,\n digest: string,\n): { pipeRef: string; slotName: string } | undefined {\n for (const node of spec.nodes) {\n const pipeRef = pipeRefOf(node);\n if (!pipeRef) continue;\n for (const input of node.io?.inputs ?? []) {\n if (input.digest === digest) return { pipeRef, slotName: input.name };\n }\n }\n return undefined;\n}\n","import type { ElkNode, ElkPort, ElkExtendedEdge, LayoutOptions } from \"elkjs/lib/elk-api\";\nimport type {\n GraphNode,\n GraphEdge,\n GraphSpec,\n DataflowAnalysis,\n EdgeRoute,\n GraphPoint,\n LayoutConfig,\n GraphDirection,\n} from \"./types\";\nimport {\n NODE_TYPE_PIPE_CARD,\n CONTROLLER_PADDING_X,\n CONTROLLER_PADDING_TOP,\n CONTROLLER_PADDING_BOTTOM,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\n\n// ─── Direction mapping ──────────────────────────────────────────────────────\n\nfunction elkDirection(direction: GraphDirection): string {\n switch (direction) {\n case \"LR\":\n return \"RIGHT\";\n case \"RL\":\n return \"LEFT\";\n case \"BT\":\n return \"UP\";\n default:\n return \"DOWN\"; // TB\n }\n}\n\n// ─── Port helpers ───────────────────────────────────────────────────────────\n\nconst INPUT_PORT_SUFFIX = \"_in\";\nconst OUTPUT_PORT_SUFFIX = \"_out\";\n\nexport function inputPortId(nodeId: string): string {\n return nodeId + INPUT_PORT_SUFFIX;\n}\n\nexport function outputPortId(nodeId: string): string {\n return nodeId + OUTPUT_PORT_SUFFIX;\n}\n\nfunction makePorts(nodeId: string, dims: NodeDimensions, direction: GraphDirection): ElkPort[] {\n // Port sides must match the flow direction so ELK computes edge attachment\n // on the same side that ReactFlow renders handles.\n // LR: input=WEST, output=EAST | RL: input=EAST, output=WEST\n // TB: input=NORTH, output=SOUTH | BT: input=SOUTH, output=NORTH\n const portSides: Record<GraphDirection, { inSide: string; outSide: string }> = {\n LR: { inSide: \"WEST\", outSide: \"EAST\" },\n RL: { inSide: \"EAST\", outSide: \"WEST\" },\n TB: { inSide: \"NORTH\", outSide: \"SOUTH\" },\n BT: { inSide: \"SOUTH\", outSide: \"NORTH\" },\n };\n const { inSide, outSide } = portSides[direction];\n\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n // Pin ports at the exact center of each side so ELK computes layout\n // with the same edge attachment point that ReactFlow will render.\n const inX = isHorizontal ? (direction === \"LR\" ? 0 : dims.width) : dims.width / 2;\n const inY = isHorizontal ? dims.height / 2 : direction === \"TB\" ? 0 : dims.height;\n const outX = isHorizontal ? (direction === \"LR\" ? dims.width : 0) : dims.width / 2;\n const outY = isHorizontal ? dims.height / 2 : direction === \"TB\" ? dims.height : 0;\n\n return [\n {\n id: inputPortId(nodeId),\n x: inX,\n y: inY,\n width: 1,\n height: 1,\n layoutOptions: { \"elk.port.side\": inSide },\n },\n {\n id: outputPortId(nodeId),\n x: outX,\n y: outY,\n width: 1,\n height: 1,\n layoutOptions: { \"elk.port.side\": outSide },\n },\n ];\n}\n\n// ─── Node dimension estimation ──────────────────────────────────────────────\n// ELK needs dimensions upfront to compute layout.\n\ninterface NodeDimensions {\n width: number;\n height: number;\n}\n\n// ─── Pipe card layout constants (keep in sync with graph-core.css) ──────────\n// If you change these, update the matching rules in graph-core.css.\nconst PIPE_CARD_HEIGHT_CAP = 320;\n\n// Horizontal padding (12 + 12 = 24) + vertical padding (12 + 12 = 24)\nconst PIPE_CARD_PADDING_X = 28; // padding-left + padding-right (14 + 14)\nconst PIPE_CARD_PADDING_Y = 24; // padding-top + padding-bottom (12 + 12)\nconst PIPE_CARD_GAP = 8; // gap between flex children (header / description / io sections)\n\n// Header:\n// - TB: single row with badge + code + status (~22px)\n// - LR: badge/status row + pipe code on its own line below + flex gap (~50px)\nconst PIPE_CARD_HEADER_HEIGHT_TB = 22;\nconst PIPE_CARD_HEADER_HEIGHT_LR = 50;\n\n// Description: 11.5px font × 1.4 line-height ≈ 16.1px per line\nconst PIPE_CARD_DESC_LINE_HEIGHT = 16;\nconst PIPE_CARD_DESC_MAX_LINES_LR = 3;\n\n// I/O section heights (label + first row of pills)\nconst PIPE_CARD_IO_SECTION_HEIGHT_LR = 38; // stacked: label on top, pills below\nconst PIPE_CARD_IO_SECTION_HEIGHT_TB = 30; // inline: label on left, pills on right\nconst PIPE_CARD_IO_EXTRA_ROW_HEIGHT = 22; // each additional wrapping row of pills\n\n// Pill dimension caps (keep in sync with .pipe-card--tb .pipe-card-io-pill-name/concept max-width)\nconst PIPE_CARD_PILL_NAME_MAX_WIDTH = 140;\nconst PIPE_CARD_PILL_CONCEPT_MAX_WIDTH = 100;\nconst PIPE_CARD_PILL_CHROME_WIDTH = 17; // pill padding (10) + name/concept gap (3) + inter-pill gap (4)\nconst PIPE_CARD_IO_LABEL_WIDTH_TB = 58; // min-width 52 + gap 6\n\n// Character width estimates for Inter font\nconst CHAR_WIDTH_DESC = 5.5; // 11.5px font\nconst CHAR_WIDTH_PILL_NAME = 5.0; // 10px font (pill-name)\nconst CHAR_WIDTH_PILL_CONCEPT = 4.5; // 9px font (pill-concept)\n\nconst MAX_VISIBLE_INPUTS = 4;\n\n/** Estimate how many lines the description will wrap to, given direction + text length. */\nfunction estimateDescriptionLines(\n description: string,\n isHorizontal: boolean,\n cardWidth: number,\n): number {\n if (!description) return 0;\n if (!isHorizontal) return 1; // TB always clamps to 1 line (CSS handles ellipsis)\n const textWidth = cardWidth - PIPE_CARD_PADDING_X;\n const charsPerLine = Math.max(1, Math.floor(textWidth / CHAR_WIDTH_DESC));\n const neededLines = Math.ceil(description.length / charsPerLine);\n return Math.min(PIPE_CARD_DESC_MAX_LINES_LR, Math.max(1, neededLines));\n}\n\n/** Estimate the rendered width of a single pill in TB mode. */\nfunction estimateTbPillWidth(name: string, concept: string): number {\n const nameWidth = Math.min(\n PIPE_CARD_PILL_NAME_MAX_WIDTH,\n Math.ceil(name.length * CHAR_WIDTH_PILL_NAME),\n );\n const conceptWidth = Math.min(\n PIPE_CARD_PILL_CONCEPT_MAX_WIDTH,\n Math.ceil(concept.length * CHAR_WIDTH_PILL_CONCEPT),\n );\n return nameWidth + conceptWidth + PIPE_CARD_PILL_CHROME_WIDTH;\n}\n\n/** Count how many wrapping rows a set of pills will occupy in TB mode.\n * Uses a simple first-fit bin-packing against the available pill area width.\n */\nfunction countTbPillRows(\n pills: readonly { name: string; concept: string }[],\n cardWidth: number,\n): number {\n if (pills.length === 0) return 0;\n const availableWidth = cardWidth - PIPE_CARD_PADDING_X - PIPE_CARD_IO_LABEL_WIDTH_TB;\n let rows = 1;\n let currentRowWidth = 0;\n for (const pill of pills) {\n const pillWidth = estimateTbPillWidth(pill.name, pill.concept);\n if (currentRowWidth === 0) {\n currentRowWidth = pillWidth;\n continue;\n }\n if (currentRowWidth + pillWidth <= availableWidth) {\n currentRowWidth += pillWidth;\n } else {\n rows += 1;\n currentRowWidth = pillWidth;\n }\n }\n return rows;\n}\n\nexport function estimateNodeDimensions(node: GraphNode, isHorizontal: boolean): NodeDimensions {\n const nodeData = node.data || {};\n // A producer that knows its node's text metrics fixes the size itself.\n if (nodeData.layoutSize) return { ...nodeData.layoutSize };\n const isStuff = nodeData.isStuff;\n const labelText = nodeData.labelText || \"\";\n const isPipeCard = node.type === NODE_TYPE_PIPE_CARD;\n\n const pipeCardMinWidth = isHorizontal ? 180 : 280;\n const pipeCardMaxWidth = isHorizontal ? 240 : 400;\n // Stuff nodes are visually aligned with pipe cards — they must never be wider\n // than the pipe card max for the current direction, otherwise the graph looks\n // lopsided (a 400px stuff node next to a 240px pipe card in LR mode).\n const estimatedWidth = Math.max(180, Math.min(pipeCardMaxWidth, labelText.length * 8 + 60));\n\n let width: number;\n if (isStuff) {\n width = Math.max(180, estimatedWidth);\n } else if (isPipeCard && nodeData.pipeCardData) {\n width = pipeCardMaxWidth;\n } else {\n width = Math.max(isPipeCard ? pipeCardMinWidth : 200, estimatedWidth);\n }\n\n let height: number;\n if (isStuff) {\n height = 60;\n } else if (isPipeCard && nodeData.pipeCardData) {\n const pcd = nodeData.pipeCardData;\n const inputs = pcd.inputs ?? [];\n const outputs = pcd.outputs ?? [];\n const description = pcd.description || nodeData.nodeData?.description || \"\";\n\n // Header\n let total =\n PIPE_CARD_PADDING_Y +\n (isHorizontal ? PIPE_CARD_HEADER_HEIGHT_LR : PIPE_CARD_HEADER_HEIGHT_TB);\n\n // Description: actual lines needed\n const descLines = estimateDescriptionLines(description, isHorizontal, width);\n if (descLines > 0) {\n total += PIPE_CARD_GAP + descLines * PIPE_CARD_DESC_LINE_HEIGHT;\n }\n\n // Inputs — cap visible to MAX_VISIBLE_INPUTS (rest collapses behind \"+N more\")\n const visibleInputs = inputs.slice(0, MAX_VISIBLE_INPUTS);\n if (visibleInputs.length > 0) {\n total += PIPE_CARD_GAP;\n if (isHorizontal) {\n // LR: one pill per row, always stacked\n total +=\n PIPE_CARD_IO_SECTION_HEIGHT_LR +\n (visibleInputs.length - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n } else {\n // TB: bin-pack pills horizontally, each extra row adds height\n const rows = countTbPillRows(visibleInputs, width);\n total += PIPE_CARD_IO_SECTION_HEIGHT_TB + (rows - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n }\n }\n\n // Outputs — same logic as inputs\n if (outputs.length > 0) {\n total += PIPE_CARD_GAP;\n if (isHorizontal) {\n total +=\n PIPE_CARD_IO_SECTION_HEIGHT_LR + (outputs.length - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n } else {\n const rows = countTbPillRows(outputs, width);\n total += PIPE_CARD_IO_SECTION_HEIGHT_TB + (rows - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n }\n }\n\n height = Math.min(PIPE_CARD_HEIGHT_CAP, total);\n } else {\n height = isPipeCard ? 120 : 70;\n }\n\n return { width, height };\n}\n\n// ─── Controller nesting depth ───────────────────────────────────────────────\n\nfunction computeDepths(\n controllerNodeIds: ReadonlySet<string>,\n containmentTree: Readonly<Record<string, readonly string[]>>,\n): Record<string, number> {\n const depthCache: Record<string, number> = {};\n const visiting = new Set<string>();\n\n function getDepth(ctrlId: string): number {\n if (depthCache[ctrlId] !== undefined) return depthCache[ctrlId];\n if (visiting.has(ctrlId)) return 0;\n visiting.add(ctrlId);\n const children = containmentTree[ctrlId] || [];\n let maxChildDepth = -1;\n for (const childId of children) {\n if (controllerNodeIds.has(childId)) {\n maxChildDepth = Math.max(maxChildDepth, getDepth(childId));\n }\n }\n visiting.delete(ctrlId);\n depthCache[ctrlId] = maxChildDepth + 1;\n return depthCache[ctrlId];\n }\n\n for (const id of controllerNodeIds) getDepth(id);\n return depthCache;\n}\n\n// ─── Leaf node builder ──────────────────────────────────────────────────────\n\nfunction makeLeafNode(nodeId: string, dims: NodeDimensions, direction: GraphDirection): ElkNode {\n return {\n id: nodeId,\n width: dims.width,\n height: dims.height,\n ports: makePorts(nodeId, dims, direction),\n layoutOptions: {\n \"elk.portConstraints\": \"FIXED_POS\",\n },\n };\n}\n\n// ─── Edge builder ───────────────────────────────────────────────────────────\n\n/**\n * An edge from its source's output port to its target's input port. When the\n * layout routes edges, a labelled edge with a known label size carries its\n * label, so the layout reserves room for it.\n */\nfunction makeElkEdge(edge: GraphEdge, routeEdges: boolean): ElkExtendedEdge {\n const elkEdge: ElkExtendedEdge = {\n id: edge.id,\n sources: [outputPortId(edge.source)],\n targets: [inputPortId(edge.target)],\n };\n if (routeEdges && edge.label && edge.labelSize) {\n elkEdge.labels = [\n {\n id: `${edge.id}_label`,\n text: edge.label,\n width: edge.labelSize.width,\n height: edge.labelSize.height,\n },\n ];\n }\n return elkEdge;\n}\n\n// ─── Build ELK graph ────────────────────────────────────────────────────────\n\nexport function buildElkGraph(\n nodes: GraphNode[],\n edges: GraphEdge[],\n graphspec: GraphSpec | null,\n analysis: DataflowAnalysis | null,\n direction: GraphDirection,\n layoutConfig?: LayoutConfig,\n): { elkGraph: ElkNode; dimensionMap: Record<string, NodeDimensions> } {\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n const nodesep = layoutConfig?.nodesep ?? 80;\n const ranksep = layoutConfig?.ranksep ?? 70;\n const routeEdges = layoutConfig?.routeEdges ?? false;\n const groupPadding = layoutConfig?.groupPadding ?? {\n x: CONTROLLER_PADDING_X,\n top: CONTROLLER_PADDING_TOP,\n bottom: CONTROLLER_PADDING_BOTTOM,\n };\n const elkDir = elkDirection(direction);\n\n const edgeNodeSpacing = \"30\";\n\n const rootLayoutOptions: LayoutOptions = {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": elkDir,\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.spacing.nodeNode\": String(nodesep),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(ranksep),\n \"elk.spacing.edgeNode\": edgeNodeSpacing,\n \"elk.spacing.edgeEdge\": \"20\",\n \"elk.layered.spacing.edgeNodeBetweenLayers\": edgeNodeSpacing,\n \"elk.layered.spacing.edgeEdgeBetweenLayers\": \"15\",\n \"elk.layered.nodePlacement.favorStraightEdges\": \"true\",\n };\n if (routeEdges) {\n // Routes and label boxes in root coordinates, whatever node an edge's\n // route is computed in, so a drawn route needs no offset.\n rootLayoutOptions[\"elk.json.edgeCoords\"] = \"ROOT\";\n }\n\n // Fast path: no hierarchy info → flat layout\n if (!graphspec || !analysis || analysis.controllerNodeIds.size === 0) {\n const dimensionMap: Record<string, NodeDimensions> = {};\n const elkChildren: ElkNode[] = nodes.map((node) => {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n return makeLeafNode(node.id, dims, direction);\n });\n\n const elkEdges: ElkExtendedEdge[] = edges.map((edge) => makeElkEdge(edge, routeEdges));\n\n return {\n elkGraph: {\n id: \"root\",\n layoutOptions: rootLayoutOptions,\n children: elkChildren,\n edges: elkEdges,\n },\n dimensionMap,\n };\n }\n\n // Hierarchical layout: build tree from containment analysis\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n const depths = computeDepths(analysis.controllerNodeIds, analysis.containmentTree);\n const dimensionMap: Record<string, NodeDimensions> = {};\n\n // Create a map from node ID to its ELK leaf node\n const nodeById = new Map<string, GraphNode>();\n for (const node of nodes) nodeById.set(node.id, node);\n\n // Build controller compound nodes (bottom-up: leaf controllers first)\n const controllerElkNodes: Record<string, ElkNode> = {};\n const controllerIds = Array.from(analysis.controllerNodeIds);\n controllerIds.sort((a, b) => (depths[a] ?? 0) - (depths[b] ?? 0));\n\n for (const ctrlId of controllerIds) {\n const depth = depths[ctrlId] ?? 0;\n const depthScale = 1 + depth * 0.15;\n const padX = Math.round(groupPadding.x * depthScale);\n const padTop = Math.round(groupPadding.top * depthScale);\n const padBottom = Math.round(groupPadding.bottom * depthScale);\n\n const ctrlLayoutOptions: LayoutOptions = {\n \"elk.padding\": `[top=${padTop},left=${padX},bottom=${padBottom},right=${padX}]`,\n \"elk.spacing.nodeNode\": String(nodesep),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(ranksep),\n \"elk.spacing.edgeNode\": edgeNodeSpacing,\n \"elk.layered.spacing.edgeNodeBetweenLayers\": edgeNodeSpacing,\n };\n\n const children: ElkNode[] = [];\n const directChildren = analysis.containmentTree[ctrlId] || [];\n\n for (const childId of directChildren) {\n if (analysis.controllerNodeIds.has(childId)) {\n // Child is a controller — use its already-built compound node\n const childElk = controllerElkNodes[childId];\n if (childElk) children.push(childElk);\n } else {\n // Child is an operator — create leaf node with ports\n const graphNode = nodeById.get(childId);\n if (graphNode) {\n const dims = estimateNodeDimensions(graphNode, isHorizontal);\n dimensionMap[childId] = dims;\n children.push(makeLeafNode(childId, dims, direction));\n }\n }\n }\n\n // Add stuff nodes that belong to this controller\n for (const node of nodes) {\n if (node.data.isStuff && childToCtrl[node.id] === ctrlId) {\n if (!children.some((c) => c.id === node.id)) {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n children.push(makeLeafNode(node.id, dims, direction));\n }\n }\n }\n\n controllerElkNodes[ctrlId] = {\n id: ctrlId,\n layoutOptions: ctrlLayoutOptions,\n children,\n };\n }\n\n // Build root children: top-level controllers + loose nodes\n const rootChildren: ElkNode[] = [];\n\n // Add top-level controllers (those not contained by another controller)\n for (const ctrlId of controllerIds) {\n if (!childToCtrl[ctrlId]) {\n const elkNode = controllerElkNodes[ctrlId];\n if (elkNode) rootChildren.push(elkNode);\n }\n }\n\n // Add loose nodes (not inside any controller and not controllers themselves)\n for (const node of nodes) {\n if (!childToCtrl[node.id] && !analysis.controllerNodeIds.has(node.id)) {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n rootChildren.push(makeLeafNode(node.id, dims, direction));\n }\n }\n\n // All edges at root level — INCLUDE_CHILDREN handles cross-hierarchy routing\n const nodeIdSet = new Set(nodes.map((n) => n.id));\n const elkEdges: ElkExtendedEdge[] = edges\n .filter((e) => nodeIdSet.has(e.source) && nodeIdSet.has(e.target))\n .map((edge) => makeElkEdge(edge, routeEdges));\n\n return {\n elkGraph: {\n id: \"root\",\n layoutOptions: rootLayoutOptions,\n children: rootChildren,\n edges: elkEdges,\n },\n dimensionMap,\n };\n}\n\n// ─── Extract absolute positions from ELK output ────────────────────────────\n\nexport interface ElkPositionResult {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport function extractAbsolutePositions(elkResult: ElkNode): Record<string, ElkPositionResult> {\n const positions: Record<string, ElkPositionResult> = {};\n\n function walk(node: ElkNode, parentX: number, parentY: number) {\n const absX = parentX + (node.x ?? 0);\n const absY = parentY + (node.y ?? 0);\n\n if (node.id !== \"root\") {\n positions[node.id] = {\n x: absX,\n y: absY,\n width: node.width ?? 0,\n height: node.height ?? 0,\n };\n }\n\n for (const child of node.children ?? []) {\n walk(child, absX, absY);\n }\n }\n\n walk(elkResult, 0, 0);\n return positions;\n}\n\n// ─── Extract edge routes from ELK output ────────────────────────────────────\n\n/**\n * Every edge's route, keyed by edge id, from a layout run with `routeEdges`\n * (its coordinates are then the root's). An edge ELK reports no section for is\n * left out, and is drawn as a free curve.\n */\nexport function extractEdgeRoutes(elkResult: ElkNode): Record<string, EdgeRoute> {\n const routes: Record<string, EdgeRoute> = {};\n\n function walk(node: ElkNode) {\n for (const edge of node.edges ?? []) {\n const points: GraphPoint[] = [];\n for (const section of edge.sections ?? []) {\n for (const point of [section.startPoint, ...(section.bendPoints ?? []), section.endPoint]) {\n const last = points[points.length - 1];\n if (!last || last.x !== point.x || last.y !== point.y) {\n points.push({ x: point.x, y: point.y });\n }\n }\n }\n if (points.length < 2) continue;\n const route: EdgeRoute = { points };\n const label = edge.labels?.[0];\n if (label && label.x !== undefined && label.y !== undefined) {\n route.label = {\n x: label.x,\n y: label.y,\n width: label.width ?? 0,\n height: label.height ?? 0,\n };\n }\n routes[edge.id] = route;\n }\n for (const child of node.children ?? []) walk(child);\n }\n\n walk(elkResult);\n return routes;\n}\n","import ELK from \"elkjs/lib/elk.bundled.js\";\nimport type {\n GraphNode,\n GraphEdge,\n GraphSpec,\n DataflowAnalysis,\n LayoutConfig,\n GraphDirection,\n} from \"./types\";\nimport {\n buildElkGraph,\n extractAbsolutePositions,\n extractEdgeRoutes,\n estimateNodeDimensions,\n} from \"./elkGraphBuilder\";\nimport type { ElkPositionResult } from \"./elkGraphBuilder\";\n\n// Cache ELK instance at module level to avoid repeated WASM initialization\nlet elkInstance: InstanceType<typeof ELK> | null = null;\nfunction getElk(): InstanceType<typeof ELK> {\n if (!elkInstance) elkInstance = new ELK();\n return elkInstance;\n}\n\nexport interface LayoutResult {\n nodes: GraphNode[];\n edges: GraphEdge[];\n /** ELK-computed positions/sizes for controller compound nodes. */\n controllerPositions: Record<string, ElkPositionResult>;\n}\n\nexport async function getLayoutedElements(\n nodes: GraphNode[],\n edges: GraphEdge[],\n direction: GraphDirection,\n layoutConfig?: LayoutConfig,\n graphspec?: GraphSpec | null,\n analysis?: DataflowAnalysis | null,\n): Promise<LayoutResult> {\n if (nodes.length === 0) return { nodes: [], edges, controllerPositions: {} };\n\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n\n const { elkGraph, dimensionMap } = buildElkGraph(\n nodes,\n edges,\n graphspec ?? null,\n analysis ?? null,\n direction,\n layoutConfig,\n );\n\n const layoutResult = await getElk().layout(elkGraph);\n const positions = extractAbsolutePositions(layoutResult);\n\n // Extract controller positions from ELK output\n const controllerPositions: Record<string, ElkPositionResult> = {};\n if (analysis) {\n for (const ctrlId of analysis.controllerNodeIds) {\n if (positions[ctrlId]) {\n controllerPositions[ctrlId] = positions[ctrlId];\n }\n }\n }\n\n const result = nodes.map((node) => {\n const pos = positions[node.id];\n const dims = dimensionMap[node.id] ?? estimateNodeDimensions(node, isHorizontal);\n\n const width = dims.width;\n const height = dims.height;\n\n // Inject direction into pipeCardData so the card component can adjust orientation\n const pipeCardData = node.data.pipeCardData;\n const cardDirection = isHorizontal ? (\"LR\" as const) : (\"TB\" as const);\n const updatedPipeCardData = pipeCardData\n ? { ...pipeCardData, direction: cardDirection }\n : undefined;\n\n return {\n ...node,\n data: {\n ...node.data,\n _estimatedWidth: width,\n _estimatedHeight: height,\n pipeCardData: updatedPipeCardData,\n },\n // Lock the ReactFlow node wrapper to the exact dimensions ELK used for layout.\n // This ensures ReactFlow's Handle (centered on the DOM element) matches\n // the port position ELK computed (centered on the estimated dimensions).\n style: {\n ...node.style,\n width: width + \"px\",\n height: height + \"px\",\n },\n position: {\n x: pos ? pos.x : 0,\n y: pos ? pos.y : 0,\n },\n sourcePosition: (isHorizontal\n ? direction === \"LR\"\n ? \"right\"\n : \"left\"\n : direction === \"TB\"\n ? \"bottom\"\n : \"top\") as GraphNode[\"sourcePosition\"],\n targetPosition: (isHorizontal\n ? direction === \"LR\"\n ? \"left\"\n : \"right\"\n : direction === \"TB\"\n ? \"top\"\n : \"bottom\") as GraphNode[\"targetPosition\"],\n };\n });\n\n if (!layoutConfig?.routeEdges) return { nodes: result, edges, controllerPositions };\n const routes = extractEdgeRoutes(layoutResult);\n const routedEdges = edges.map((edge) => {\n const route = routes[edge.id];\n return route ? { ...edge, route } : edge;\n });\n return { nodes: result, edges: routedEdges, controllerPositions };\n}\n","// Drawing an edge along the route the layout computed for it. Pure and\n// React-free: the routed edge component calls it with the handle positions\n// ReactFlow measured, which may sit a few pixels from the ports the layout\n// assumed, or anywhere at all once a node has been dragged.\n\nimport type { EdgeRoute, GraphPoint } from \"./types\";\n\n/**\n * How far a measured handle may sit from the port the layout routed from\n * before the route is stale: a node dragged away. Handles are drawn a few\n * pixels outside their node, which this absorbs.\n */\nexport const ROUTE_ENDPOINT_TOLERANCE_PX = 8;\n\n/** The radius each bend of a route is rounded with, shrunk on short segments. */\nexport const ROUTE_CORNER_RADIUS_PX = 8;\n\nconst SAME_AXIS_EPSILON = 0.5;\n\nfunction near(a: GraphPoint, b: GraphPoint, tolerance: number): boolean {\n return Math.abs(a.x - b.x) <= tolerance && Math.abs(a.y - b.y) <= tolerance;\n}\n\n/**\n * A route's points with its ends moved onto the measured handles, or null\n * when either handle is too far from the port the layout routed from. The\n * point next to a moved end moves with it along the segment's axis, so an\n * orthogonal route stays orthogonal.\n */\nexport function fitRoute(\n points: readonly GraphPoint[],\n source: GraphPoint,\n target: GraphPoint,\n tolerance: number = ROUTE_ENDPOINT_TOLERANCE_PX,\n): GraphPoint[] | null {\n if (points.length < 2) return null;\n const first = points[0];\n const last = points[points.length - 1];\n if (!near(first, source, tolerance) || !near(last, target, tolerance)) return null;\n\n const fitted = points.map((p) => ({ x: p.x, y: p.y }));\n const end = fitted.length - 1;\n if (fitted.length > 2) {\n alignNeighbour(fitted, 0, 1, source);\n alignNeighbour(fitted, end, end - 1, target);\n }\n fitted[0] = { x: source.x, y: source.y };\n fitted[end] = { x: target.x, y: target.y };\n return fitted;\n}\n\n/** Move the neighbour of an end along with it, on the axis their segment shares. */\nfunction alignNeighbour(\n points: GraphPoint[],\n endIndex: number,\n neighbourIndex: number,\n to: GraphPoint,\n) {\n const end = points[endIndex];\n const neighbour = points[neighbourIndex];\n if (Math.abs(end.y - neighbour.y) <= SAME_AXIS_EPSILON) neighbour.y = to.y;\n else if (Math.abs(end.x - neighbour.x) <= SAME_AXIS_EPSILON) neighbour.x = to.x;\n}\n\n/**\n * An SVG path through a polyline, each bend rounded by a quadratic curve. A\n * bend's radius shrinks to half its shorter segment, so close bends never\n * overshoot each other.\n */\nexport function roundedPolylinePath(\n points: readonly GraphPoint[],\n radius: number = ROUTE_CORNER_RADIUS_PX,\n): string {\n if (points.length === 0) return \"\";\n const parts = [`M ${points[0].x},${points[0].y}`];\n for (let i = 1; i < points.length - 1; i++) {\n const previous = points[i - 1];\n const bend = points[i];\n const next = points[i + 1];\n const inLength = Math.hypot(bend.x - previous.x, bend.y - previous.y);\n const outLength = Math.hypot(next.x - bend.x, next.y - bend.y);\n const r = Math.min(radius, inLength / 2, outLength / 2);\n if (r <= 0) {\n parts.push(`L ${bend.x},${bend.y}`);\n continue;\n }\n const before = {\n x: bend.x + ((previous.x - bend.x) / inLength) * r,\n y: bend.y + ((previous.y - bend.y) / inLength) * r,\n };\n const after = {\n x: bend.x + ((next.x - bend.x) / outLength) * r,\n y: bend.y + ((next.y - bend.y) / outLength) * r,\n };\n parts.push(`L ${before.x},${before.y}`, `Q ${bend.x},${bend.y} ${after.x},${after.y}`);\n }\n const last = points[points.length - 1];\n parts.push(`L ${last.x},${last.y}`);\n return parts.join(\" \");\n}\n\n/** The point halfway along a polyline, for a label the layout did not place. */\nexport function polylineMidpoint(points: readonly GraphPoint[]): GraphPoint {\n if (points.length === 0) return { x: 0, y: 0 };\n let total = 0;\n for (let i = 1; i < points.length; i++) {\n total += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);\n }\n let remaining = total / 2;\n for (let i = 1; i < points.length; i++) {\n const a = points[i - 1];\n const b = points[i];\n const length = Math.hypot(b.x - a.x, b.y - a.y);\n if (length >= remaining && length > 0) {\n const t = remaining / length;\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n }\n remaining -= length;\n }\n return points[points.length - 1];\n}\n\n/** Where a routed edge's label is centred: the box the layout placed, or the route's midpoint. */\nexport function routeLabelCenter(route: EdgeRoute, fitted: readonly GraphPoint[]): GraphPoint {\n if (route.label) {\n return {\n x: route.label.x + route.label.width / 2,\n y: route.label.y + route.label.height / 2,\n };\n }\n return polylineMidpoint(fitted);\n}\n","import type {\n GraphSpec,\n DataflowAnalysis,\n FoldToggleOptions,\n GraphNode,\n GraphEdge,\n PipeCallNode,\n PipeType,\n} from \"./types\";\nimport {\n CONTROLLER_PADDING_X,\n CONTROLLER_PADDING_TOP,\n CONTROLLER_PADDING_BOTTOM,\n NODE_TYPE_CONTROLLER,\n graphSpecMode,\n nodeWidth,\n nodeHeight,\n isStuffNodeId,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asPipeCallNode, GraphSpecValidationError } from \"./validateGraphSpec\";\n\n/** Max visible children before a parallel/batch controller auto-collapses. */\nexport const MAX_VISIBLE_CONTROLLER_CHILDREN = 5;\n\n/**\n * Collect all descendant node IDs of a controller (recursive).\n */\nfunction getDescendants(\n controllerId: string,\n containmentTree: Readonly<Record<string, readonly string[]>>,\n controllerNodeIds: ReadonlySet<string>,\n): Set<string> {\n const result = new Set<string>();\n const stack = [controllerId];\n while (stack.length > 0) {\n const id = stack.pop()!;\n for (const childId of containmentTree[id] || []) {\n result.add(childId);\n if (controllerNodeIds.has(childId)) stack.push(childId);\n }\n }\n return result;\n}\n\n/** Position and size for a controller, as computed by the layout engine. */\nexport interface ControllerRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Build controller group nodes that wrap child operators/stuff nodes.\n *\n * When `controllerPositions` is provided (from ELK layout), uses those exact\n * positions/sizes instead of recomputing from child bounding boxes. This ensures\n * controller containers match the layout engine's spacing and edge routing.\n *\n * **Side effect:** mutates `layoutedNodes` entries to set `parentId`, `extent`,\n * and convert positions to parent-relative coordinates.\n */\nexport function buildControllerNodes(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n layoutedNodes: GraphNode[],\n controllerPositions?: Record<string, ControllerRect>,\n): GraphNode[] {\n const nodeById: Record<string, GraphNode> = {};\n const graphMode = graphSpecMode(graphspec);\n for (const n of layoutedNodes) {\n nodeById[n.id] = n;\n }\n\n // A controller node is a pipe-call node; the guard makes a malformed spec\n // fail loudly here rather than surfacing later as an undefined pipe_code.\n const controllerInfo: Record<string, PipeCallNode> = {};\n for (const node of graphspec.nodes) {\n if (analysis.controllerNodeIds.has(node.id)) {\n controllerInfo[node.id] = asPipeCallNode(node, `nodes[${node.id}]`);\n }\n }\n\n const depthCache: Record<string, number> = {};\n const depthVisiting = new Set<string>();\n function getDepth(controllerId: string): number {\n if (depthCache[controllerId] !== undefined) return depthCache[controllerId];\n if (depthVisiting.has(controllerId)) {\n throw new Error(\n `Cycle detected in containment tree: controller \"${controllerId}\" is part of a containment cycle`,\n );\n }\n depthVisiting.add(controllerId);\n const children = analysis.containmentTree[controllerId] || [];\n let maxChildDepth = -1;\n for (const childId of children) {\n if (analysis.controllerNodeIds.has(childId)) {\n maxChildDepth = Math.max(maxChildDepth, getDepth(childId));\n }\n }\n depthVisiting.delete(controllerId);\n depthCache[controllerId] = maxChildDepth + 1;\n return depthCache[controllerId];\n }\n\n const childToController = buildChildToControllerMap(graphspec, analysis);\n const controllerStuffChildren: Record<string, string[]> = {};\n for (const [nodeId, ctrlId] of Object.entries(childToController)) {\n if (!isStuffNodeId(nodeId)) continue;\n if (!nodeById[nodeId]) continue;\n if (!controllerStuffChildren[ctrlId]) controllerStuffChildren[ctrlId] = [];\n controllerStuffChildren[ctrlId].push(nodeId);\n }\n\n const controllerIds = Array.from(analysis.controllerNodeIds);\n for (const id of controllerIds) getDepth(id);\n controllerIds.sort((a, b) => depthCache[a] - depthCache[b]);\n\n const controllerNodes: GraphNode[] = [];\n const childToParent: Record<string, string> = {};\n\n for (const controllerId of controllerIds) {\n const directChildren = analysis.containmentTree[controllerId] || [];\n const renderedChildren = directChildren.filter((cid) => nodeById[cid]);\n const stuffChildren = controllerStuffChildren[controllerId] || [];\n const allChildren = [...renderedChildren, ...stuffChildren];\n\n if (allChildren.length === 0) continue;\n\n let groupX: number, groupY: number, groupW: number, groupH: number;\n\n const elkPos = controllerPositions?.[controllerId];\n if (elkPos) {\n // Use ELK's computed position and size — accounts for edge routing and spacing\n groupX = elkPos.x;\n groupY = elkPos.y;\n groupW = elkPos.width;\n groupH = elkPos.height;\n } else {\n // Fallback: compute bounding box from child positions\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const childId of allChildren) {\n const child = nodeById[childId];\n const pos = child.position;\n const w = nodeWidth(child);\n const h = nodeHeight(child);\n minX = Math.min(minX, pos.x);\n minY = Math.min(minY, pos.y);\n maxX = Math.max(maxX, pos.x + w);\n maxY = Math.max(maxY, pos.y + h);\n }\n\n const depth = depthCache[controllerId] ?? 0;\n const depthScale = 1 + depth * 0.15;\n const padX = Math.round(CONTROLLER_PADDING_X * depthScale);\n const padTop = Math.round(CONTROLLER_PADDING_TOP * depthScale);\n const padBottom = Math.round(CONTROLLER_PADDING_BOTTOM * depthScale);\n\n groupX = minX - padX;\n groupY = minY - padTop;\n groupW = maxX - minX + 2 * padX;\n groupH = maxY - minY + padTop + padBottom;\n }\n\n const info = controllerInfo[controllerId];\n if (!info) {\n // controllerId comes from a `contains` edge source with no matching node.\n throw new GraphSpecValidationError(\n `nodes[${controllerId}]`,\n `controller \"${controllerId}\" is referenced by a \"contains\" edge but ` +\n `has no corresponding node in graphspec.nodes`,\n );\n }\n const pipeCode = info.pipe_code;\n const groupNode: GraphNode = {\n id: controllerId,\n type: NODE_TYPE_CONTROLLER,\n data: {\n label: pipeCode,\n pipeType: info.pipe_type,\n isController: true,\n isPipe: false,\n isStuff: false,\n pipeCode: pipeCode,\n labelText: pipeCode,\n graphMode,\n },\n position: { x: groupX, y: groupY },\n style: {\n width: groupW + \"px\",\n height: groupH + \"px\",\n padding: \"0\",\n },\n };\n\n controllerNodes.push(groupNode);\n nodeById[controllerId] = groupNode;\n\n for (const childId of allChildren) {\n childToParent[childId] = controllerId;\n }\n }\n\n // Convert child positions to parent-relative and set parentId\n for (const [childId, parentId] of Object.entries(childToParent)) {\n const child = nodeById[childId];\n const parent = nodeById[parentId];\n if (!child || !parent) continue;\n child.position = {\n x: child.position.x - parent.position.x,\n y: child.position.y - parent.position.y,\n };\n child.parentId = parentId;\n child.extent = \"parent\";\n }\n\n return controllerNodes;\n}\n\n/**\n * Apply controller containers to layouted nodes if showControllers is enabled.\n *\n * When `expandedControllers` is provided, PipeParallel/PipeBatch controllers with\n * more than MAX_VISIBLE_CONTROLLER_CHILDREN children are auto-collapsed unless the\n * controller ID is in the expanded set.\n */\nexport function applyControllers(\n layoutedNodes: GraphNode[],\n layoutedEdges: GraphEdge[],\n graphspec: GraphSpec | null,\n analysis: DataflowAnalysis | null,\n showControllers: boolean,\n expandedControllers?: ReadonlySet<string>,\n onToggleCollapse?: (controllerId: string) => void,\n controllerPositions?: Record<string, ControllerRect>,\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void,\n): { nodes: GraphNode[]; edges: GraphEdge[] } {\n if (!showControllers || !analysis || !graphspec) {\n return { nodes: layoutedNodes, edges: layoutedEdges };\n }\n\n // ─── Compute child counts and effective collapse state ─────────────────\n const childCounts: Record<string, number> = {};\n const collapsedSet = new Set<string>();\n\n // A controller is a pipe-call node; the guard narrows its type the way\n // `buildControllerNodes` does, and fails loudly on a malformed spec.\n const controllerTypeMap: Record<string, PipeType> = {};\n for (const node of graphspec.nodes) {\n if (analysis.controllerNodeIds.has(node.id)) {\n controllerTypeMap[node.id] = asPipeCallNode(node, `nodes[${node.id}]`).pipe_type;\n }\n }\n\n for (const ctrlId of analysis.controllerNodeIds) {\n const directChildren = analysis.containmentTree[ctrlId] || [];\n childCounts[ctrlId] = directChildren.length;\n const pipeType = controllerTypeMap[ctrlId];\n const isCollapsible = pipeType === \"PipeParallel\" || pipeType === \"PipeBatch\";\n if (\n isCollapsible &&\n directChildren.length > MAX_VISIBLE_CONTROLLER_CHILDREN &&\n !expandedControllers?.has(ctrlId)\n ) {\n collapsedSet.add(ctrlId);\n }\n }\n\n // ─── Filter hidden children for collapsed controllers ──────────────────\n let filteredNodes = layoutedNodes;\n let filteredEdges = layoutedEdges;\n\n if (collapsedSet.size > 0) {\n const hiddenNodes = new Set<string>();\n\n for (const ctrlId of collapsedSet) {\n const directChildren = analysis.containmentTree[ctrlId] || [];\n const toHide = directChildren.slice(MAX_VISIBLE_CONTROLLER_CHILDREN);\n for (const childId of toHide) {\n hiddenNodes.add(childId);\n if (analysis.controllerNodeIds.has(childId)) {\n for (const d of getDescendants(\n childId,\n analysis.containmentTree,\n analysis.controllerNodeIds,\n )) {\n hiddenNodes.add(d);\n }\n }\n }\n }\n\n // Also hide stuff nodes inside collapsed controllers that have no visible\n // pipe connection. We ignore stuff-to-stuff edges (batch_item/batch_aggregate)\n // because those would keep orphaned branch stuff nodes alive.\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n for (const node of layoutedNodes) {\n if (!isStuffNodeId(node.id) || hiddenNodes.has(node.id)) continue;\n const ctrlId = childToCtrl[node.id];\n if (!ctrlId || (!collapsedSet.has(ctrlId) && !hiddenNodes.has(ctrlId))) continue;\n const pipeEdges = layoutedEdges.filter((e) => {\n if (e.source !== node.id && e.target !== node.id) return false;\n const other = e.source === node.id ? e.target : e.source;\n return !isStuffNodeId(other); // only pipe connections\n });\n if (\n pipeEdges.length === 0 ||\n pipeEdges.every((e) => {\n const other = e.source === node.id ? e.target : e.source;\n return hiddenNodes.has(other);\n })\n ) {\n hiddenNodes.add(node.id);\n }\n }\n\n filteredNodes = layoutedNodes.filter((n) => !hiddenNodes.has(n.id));\n filteredEdges = layoutedEdges.filter(\n (e) => !hiddenNodes.has(e.source) && !hiddenNodes.has(e.target),\n );\n }\n\n // ─── Build controller group nodes from visible children ────────────────\n const controllerNodes = buildControllerNodes(\n graphspec,\n analysis,\n filteredNodes,\n controllerPositions,\n );\n if (controllerNodes.length === 0) {\n return { nodes: filteredNodes, edges: filteredEdges };\n }\n\n // Inject collapse + fold metadata into controller node data\n for (const cn of controllerNodes) {\n const count = childCounts[cn.id] ?? 0;\n const isCollapsed = collapsedSet.has(cn.id);\n cn.data.childCount = count;\n cn.data.isCollapsed = isCollapsed;\n if (onToggleCollapse) {\n const id = cn.id;\n cn.data.onToggleCollapse = () => onToggleCollapse(id);\n }\n if (onToggleFold) {\n const id = cn.id;\n cn.data.onToggleFold = (options?: FoldToggleOptions) => onToggleFold(id, options);\n }\n }\n\n return { nodes: sortParentsFirst([...controllerNodes, ...filteredNodes]), edges: filteredEdges };\n}\n\n/**\n * Order nodes so every group node comes before its children, which ReactFlow\n * requires. Stable within a nesting depth. Throws on a cycle in the parent\n * chain rather than looping.\n */\nexport function sortParentsFirst(nodes: GraphNode[]): GraphNode[] {\n const nodeMap: Record<string, GraphNode> = {};\n for (const n of nodes) nodeMap[n.id] = n;\n const depthOf: Record<string, number> = {};\n const depthVisiting = new Set<string>();\n function getContainmentDepth(id: string): number {\n if (depthOf[id] !== undefined) return depthOf[id];\n if (depthVisiting.has(id)) {\n throw new Error(\n `Cycle detected in node parent chain: node \"${id}\" references itself as an ancestor`,\n );\n }\n depthVisiting.add(id);\n const n = nodeMap[id];\n depthOf[id] = n && n.parentId ? 1 + getContainmentDepth(n.parentId) : 0;\n depthVisiting.delete(id);\n return depthOf[id];\n }\n for (const n of nodes) getContainmentDepth(n.id);\n return [...nodes].sort((a, b) => depthOf[a.id] - depthOf[b.id]);\n}\n","import type { GraphStyleId, LayoutConfig } from \"@graph/types\";\nimport { GRAPH_STYLE } from \"@graph/types\";\nimport { DEFAULT_GRAPH_CONFIG } from \"@graph/graphConfig\";\n\n// ─── The style registry ─────────────────────────────────────────────────────\n// A closed, typed table: adding a style is a new `GRAPH_STYLE` id, after which\n// the compiler names every table keyed by `GraphStyleId` that needs a row — the\n// descriptor below, the drawing pipeline (`stylePipelines.ts`), the node\n// components and the menu icon in the React layer.\n\n/** Which of the toolbar's controls make sense for a style. */\nexport interface GraphStyleCapabilities {\n /**\n * The toolbar's show/hide controllers toggle applies: the style draws\n * controller frames on demand. A style that decides its own frames hides it.\n */\n controllerFrameToggle: boolean;\n /**\n * Fold-all and expand-all apply. In a style that also has the frame toggle\n * they apply only while the frames are shown, since a fold is undone from a\n * frame.\n */\n foldAll: boolean;\n}\n\n/** What the registry says of one style: what it is called, what it is for, and what it supports. */\nexport interface GraphStyleDescriptor {\n id: GraphStyleId;\n /** The style's name in the toolbar menu. */\n name: string;\n /** One line saying what the style shows, under its name in the menu. */\n description: string;\n capabilities: GraphStyleCapabilities;\n /**\n * How the style lays out: its spacing, which replaces `config.nodesep` and\n * `config.ranksep` for it, the room its frames keep around their steps, and\n * whether its edges follow routes around the nodes in their way. A style\n * that leaves it undefined keeps the host's spacing and the controller\n * groups' padding, and draws free curves of the host's `edgeType`.\n */\n layout?: Required<LayoutConfig>;\n}\n\nexport const GRAPH_STYLES: Record<GraphStyleId, GraphStyleDescriptor> = {\n [GRAPH_STYLE.DETAILED]: {\n id: GRAPH_STYLE.DETAILED,\n name: \"Detailed\",\n description: \"Every pipe with its inputs, outputs and settings\",\n capabilities: { controllerFrameToggle: true, foldAll: true },\n },\n [GRAPH_STYLE.SIMPLE]: {\n id: GRAPH_STYLE.SIMPLE,\n name: \"Simple\",\n description: \"The method's steps in plain words\",\n capabilities: { controllerFrameToggle: false, foldAll: true },\n layout: {\n nodesep: 28,\n ranksep: 44,\n routeEdges: true,\n groupPadding: { x: 20, top: 40, bottom: 16 },\n },\n },\n};\n\n/** Every registered style, in the order the menu lists them. */\nexport const GRAPH_STYLE_IDS: readonly GraphStyleId[] = [GRAPH_STYLE.DETAILED, GRAPH_STYLE.SIMPLE];\n\n/**\n * Whether a value names a registered style. For values that cross an untyped\n * boundary — an editor setting, the standalone page's config embed, a host\n * written in plain JavaScript — before they reach the viewer.\n */\nexport function isGraphStyleId(value: unknown): value is GraphStyleId {\n return typeof value === \"string\" && Object.hasOwn(GRAPH_STYLES, value);\n}\n\n/**\n * Resolve the active style from `(graphStyle prop, config.graphStyle, default)`.\n * Pure + exported so the controlled/reactive precedence is unit-testable\n * without React, under the same contract `resolveExternalThemeMode` holds:\n * - the prop wins when it names a style,\n * - falls back to `config.graphStyle` when the prop is undefined (so a host can\n * hand control back to config after passing the prop),\n * - falls back to the library default (`detailed`) when neither is set.\n *\n * A value naming no registered style is treated as unset rather than thrown\n * on: a style is a preference, and a host relaying a setting written for a\n * newer version of this package should still get a graph.\n */\nexport function resolveGraphStyle(styleProp: unknown, configStyle: unknown): GraphStyleId {\n if (isGraphStyleId(styleProp)) return styleProp;\n if (isGraphStyleId(configStyle)) return configStyle;\n return DEFAULT_GRAPH_CONFIG.graphStyle;\n}\n\n/**\n * The styles the toolbar menu offers, from the viewer's `styleMenu` prop:\n * nothing for `false` or `undefined` (the menu is opt-in), every registered\n * style in registry order for `true`, and for a list the registered styles it\n * names, in its order and without repeats. A menu needs two styles to choose\n * between, so fewer than two offer none.\n */\nexport function resolveStyleMenu(\n styleMenu: boolean | readonly GraphStyleId[] | undefined,\n): GraphStyleId[] {\n if (styleMenu === undefined || styleMenu === false) return [];\n const requested: readonly unknown[] = styleMenu === true ? GRAPH_STYLE_IDS : styleMenu;\n const offered: GraphStyleId[] = [];\n for (const id of requested) {\n if (isGraphStyleId(id) && !offered.includes(id)) offered.push(id);\n }\n return offered.length >= 2 ? offered : [];\n}\n","// Plain words from identifiers: the simple style's fallback when a method\n// declares nothing better. Pure, deterministic and tested — never a stand-in\n// for an authored description, and never a guess at meaning: an identifier is\n// split, cased and spelled, nothing more.\n\n/**\n * Words spelled as acronyms although they hold a vowel. A word with no vowel at\n * all (`cv`, `pdf`, `rfp`, `html`) is spelled as an acronym without being\n * listed, unless it is one of the `VOWELLESS_WORDS`.\n */\nconst VOWELED_ACRONYMS: ReadonlySet<string> = new Set([\n \"ai\",\n \"api\",\n \"faq\",\n \"iban\",\n \"id\",\n \"io\",\n \"ip\",\n \"json\",\n \"kpi\",\n \"ocr\",\n \"roi\",\n \"seo\",\n \"ui\",\n \"uri\",\n \"url\",\n \"uuid\",\n \"ux\",\n]);\n\nconst VOWELS = /[aeiouy]/;\n\n/**\n * Words with no vowel that are written in lowercase rather than spelled out:\n * abbreviations, units and ordinal suffixes found in identifiers (`weight_kg`\n * is \"Weight kg\", `nth_item` is \"Nth item\"). A plural is covered by its\n * singular (`hrs` by `hr`).\n */\nconst VOWELLESS_WORDS: ReadonlySet<string> = new Set([\n \"cfg\",\n \"cm\",\n \"cnt\",\n \"ctx\",\n \"dr\",\n \"dst\",\n \"fn\",\n \"ft\",\n \"hmm\",\n \"hr\",\n \"kg\",\n \"km\",\n \"lb\",\n \"mg\",\n \"ml\",\n \"mm\",\n \"mr\",\n \"mrs\",\n \"ms\",\n \"msg\",\n \"nd\",\n \"nth\",\n \"pkg\",\n \"pt\",\n \"rd\",\n \"src\",\n \"st\",\n \"th\",\n \"tmp\",\n \"vs\",\n]);\n\n/** Vowelless acronyms that end in `s` without being plurals (`css` is not two `cs`). */\nconst SINGULAR_S_ACRONYMS: ReadonlySet<string> = new Set([\n \"cms\",\n \"css\",\n \"dns\",\n \"gps\",\n \"rss\",\n \"sms\",\n]);\n\n/**\n * The plain names of the natives whose code does not read as words once split\n * (`YesNo` is not \"Yes no\"). Every other native reads well humanized.\n */\nconst NATIVE_PLAIN_NAMES: Readonly<Record<string, string>> = {\n Html: \"HTML\",\n JSON: \"JSON\",\n TextAndImages: \"Text and images\",\n YesNo: \"Yes or no\",\n SearchResult: \"Search result\",\n Composite: \"Combined results\",\n Dynamic: \"Data\",\n};\n\n/**\n * Natives that say what kind of value something is without saying what it is:\n * a final output of one of these is named by its variable, not its concept.\n */\nconst GENERIC_NATIVES: ReadonlySet<string> = new Set([\"Anything\", \"Dynamic\", \"Composite\"]);\n\nfunction isAcronymWord(lower: string): boolean {\n if (lower.length < 2 || !/^[a-z]+$/.test(lower)) return false;\n if (VOWELLESS_WORDS.has(lower)) return false;\n if (lower.endsWith(\"s\") && VOWELLESS_WORDS.has(lower.slice(0, -1))) return false;\n return !VOWELS.test(lower) || VOWELED_ACRONYMS.has(lower);\n}\n\n/** Spell one lowercase word: an acronym in capitals (a plural keeps its `s`), anything else as is. */\nfunction spellWord(lower: string, wasAllCaps: boolean): string {\n if (/^\\d+$/.test(lower)) return lower;\n if (wasAllCaps && lower.length >= 2) return lower.toUpperCase();\n // A plural acronym keeps its lowercase `s`: `cvs` is \"CVs\", not \"CVS\".\n if (lower.endsWith(\"s\") && !SINGULAR_S_ACRONYMS.has(lower) && isAcronymWord(lower.slice(0, -1))) {\n return lower.slice(0, -1).toUpperCase() + \"s\";\n }\n if (isAcronymWord(lower)) return lower.toUpperCase();\n return lower;\n}\n\n/**\n * Split an identifier into its words: on `_`, `-`, `.` and spaces, and at the\n * case boundaries of camel and Pascal case, keeping a run of capitals together\n * (`PDFDocument` is `PDF` and `Document`).\n */\nfunction splitIdentifier(identifier: string): { word: string; wasAllCaps: boolean }[] {\n const parts = identifier\n .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .replace(/([A-Za-z])(\\d)/g, \"$1 $2\")\n .split(/[\\s_\\-.]+/)\n .filter((p) => p.length > 0);\n return parts.map((p) => ({\n word: p.toLowerCase(),\n wasAllCaps: p.length >= 2 && p === p.toUpperCase() && /[A-Z]/.test(p),\n }));\n}\n\n/** Capitalize the first letter, leaving the rest as written. */\nfunction capitalizeFirst(text: string): string {\n return text.length === 0 ? text : text.charAt(0).toUpperCase() + text.slice(1);\n}\n\n/** The last segment of a dotted ref: the code without its domain (`recruitment.CandidateProfile` → `CandidateProfile`). */\nexport function stripDomain(ref: string): string {\n const dot = ref.lastIndexOf(\".\");\n return dot === -1 ? ref : ref.slice(dot + 1);\n}\n\n/**\n * An identifier as words in sentence case: `job_offer` → \"Job offer\",\n * `CandidateProfile` → \"Candidate profile\", `pr_url` → \"PR URL\",\n * `cvs` → \"CVs\". A dotted ref is humanized by its last segment.\n */\nexport function humanizeIdentifier(identifier: string): string {\n const words = splitIdentifier(stripDomain(identifier.trim()));\n if (words.length === 0) return \"\";\n return capitalizeFirst(words.map((w) => spellWord(w.word, w.wasAllCaps)).join(\" \"));\n}\n\n/** A concept's plain name, from its code or ref: a native's own plain name, or the humanized code. */\nexport function conceptPlainName(conceptRef: string): string {\n const code = stripDomain(conceptRef.trim());\n return NATIVE_PLAIN_NAMES[code] ?? humanizeIdentifier(code);\n}\n\n/** Whether a concept is a native that names no particular kind of content (`Anything`, `Dynamic`, `Composite`). */\nexport function isGenericConcept(conceptRef: string): boolean {\n return GENERIC_NATIVES.has(stripDomain(conceptRef.trim()));\n}\n\n/**\n * Authored text as a title: whitespace collapsed (descriptions are often\n * written across lines), the first letter capitalized, and a single closing\n * period dropped, since a label in a box reads as a heading. Nothing else is\n * touched: the words are the author's.\n */\nexport function sentenceCase(text: string): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n const trimmed =\n collapsed.endsWith(\".\") && !collapsed.endsWith(\"..\") ? collapsed.slice(0, -1) : collapsed;\n return capitalizeFirst(trimmed);\n}\n\n/** The outcome names that read better as other words: a yes-or-no test's, and the default. */\nconst OUTCOME_WORDS: Readonly<Record<string, string>> = {\n true: \"Yes\",\n false: \"No\",\n default: \"Otherwise\",\n};\n\n/**\n * A condition's outcome as an arrow label: `english` → \"English\",\n * `high_priority` → \"High priority\", a yes-or-no test's `true` and `false` →\n * \"Yes\" and \"No\", and the `default` outcome → \"Otherwise\". A branch taken on\n * several outcomes, written `no_fit | default` as the static builder labels it,\n * reads \"No fit or otherwise\".\n */\nexport function outcomeLabel(outcome: string): string {\n const outcomes = outcome\n .split(\"|\")\n .map((o) => o.trim())\n .filter((o) => o.length > 0);\n const labels = outcomes.map((o, index) => {\n const label = OUTCOME_WORDS[o.toLowerCase()] ?? humanizeIdentifier(o);\n return index === 0 ? label : label.charAt(0).toLowerCase() + label.slice(1);\n });\n return labels.join(\" or \");\n}\n","// How wide text renders before the DOM exists: what the layout sizes the\n// simple style's boxes and arrow labels from. Pure and deterministic.\n//\n// The widths are per glyph, as a fraction of the font size, measured in\n// Chromium for the stack `--font-sans` names (Inter, then the system sans of\n// each platform, then the generic one) and kept at the widest of them, at the\n// heaviest weight the style draws text in. An estimate that errs wide costs a\n// box a little spare room; one that errs narrow cuts a title's last line.\n\nconst GLYPH_EM_GROUPS: readonly (readonly [number, string])[] = [\n [0.28, \"il .,'\"],\n [0.33, \"fjt:;!()\"],\n [0.35, \"/\"],\n [0.39, \"I\"],\n [0.44, \"r-\"],\n [0.51, \"1z\"],\n [0.56, '7aceksvxyJ\"'],\n [0.59, \"25\"],\n [0.6, \"8\"],\n [0.61, \"369bdghnopquF?\"],\n [0.64, \"04\"],\n [0.67, \"ELPSTZ\"],\n [0.72, \"ABCDNRUVXY\"],\n [0.78, \"GHKOQ\"],\n [0.8, \"w\"],\n [0.83, \"&\"],\n [0.89, \"m\"],\n [0.94, \"M\"],\n [1, \"W%\"],\n];\n\nconst GLYPH_EM: ReadonlyMap<string, number> = new Map(\n GLYPH_EM_GROUPS.flatMap(([em, glyphs]) => [...glyphs].map((glyph) => [glyph, em] as const)),\n);\n\n/** A glyph outside the table (an accented letter, a symbol): as wide as the wide end of its case. */\nconst UNLISTED_LOWER_EM = 0.62;\nconst UNLISTED_UPPER_EM = 0.78;\n\nfunction glyphEm(glyph: string): number {\n const listed = GLYPH_EM.get(glyph);\n if (listed !== undefined) return listed;\n return glyph !== glyph.toLowerCase() ? UNLISTED_UPPER_EM : UNLISTED_LOWER_EM;\n}\n\n/** A text's rendered width at a font size, in pixels. */\nexport function textWidthPx(text: string, fontPx: number): number {\n let em = 0;\n for (const glyph of text) em += glyphEm(glyph);\n return em * fontPx;\n}\n\n/** How many lines a text wraps to at a width, and whether it fits in `maxLines`. */\nexport interface WrapEstimate {\n /** Lines the text needs, unclamped. */\n lines: number;\n /** Lines it is drawn on: `lines` clamped to `maxLines`. */\n drawnLines: number;\n /** Whether the text is cut, so its box must carry the full text in a tooltip. */\n clamped: boolean;\n}\n\n/**\n * Estimate a text's wrapped line count by greedy word wrap at a line width,\n * the way the browser wraps it. A word wider than a line is broken across as\n * many lines as it needs (`overflow-wrap: anywhere`), starting on a line of\n * its own.\n */\nexport function estimateWrap(\n text: string,\n lineWidthPx: number,\n fontPx: number,\n maxLines: number,\n): WrapEstimate {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const spacePx = textWidthPx(\" \", fontPx);\n let lines = words.length === 0 ? 0 : 1;\n let used = 0;\n for (const word of words) {\n const wordPx = textWidthPx(word, fontPx);\n if (wordPx > lineWidthPx) {\n if (used > 0) lines += 1;\n used = 0;\n for (const glyph of word) {\n const glyphPx = textWidthPx(glyph, fontPx);\n if (used > 0 && used + glyphPx > lineWidthPx) {\n lines += 1;\n used = 0;\n }\n used += glyphPx;\n }\n continue;\n }\n const needed = used === 0 ? wordPx : used + spacePx + wordPx;\n if (needed <= lineWidthPx) {\n used = needed;\n } else {\n lines += 1;\n used = wordPx;\n }\n }\n const drawnLines = Math.min(lines, maxLines);\n return { lines, drawnLines, clamped: lines > maxLines };\n}\n","// The simple style: a flowchart of the method's steps in the words its author\n// declared. A projection of the same GraphSpec the detailed style draws — no\n// new facts, and nothing guessed:\n//\n// - every operator is a step, titled by its pipe's description;\n// - the method's inputs and its final outputs are the only data drawn as nodes;\n// every other value becomes the arrows from the step that produced it to each\n// step that reads it;\n// - a condition is a decision whose arrows carry its outcomes;\n// - a batch runs \"for each\" item: its branch is drawn once, in a frame when it\n// has several steps, as one marked step when it has one;\n// - sequences and parallels have no frame: the arrows are the order, and\n// parallel branches are side by side because nothing joins them until a step\n// reads both;\n// - a binding step is plumbing: the arrow passes through it, unless it failed,\n// when it is a step, so a run that stopped there shows where;\n// - a folded controller is one step, saying how many it holds.\n//\n// Pure and React-free. It runs on the dataflow graph after the folds, so the\n// fold machinery stays the detailed style's, and its output feeds the same\n// ELK layout through each node's fixed `layoutSize`.\n\nimport type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphSpecNode,\n GraphSpecNodeIoItem,\n NodePipeType,\n PipeStatus,\n SimpleNodePayload,\n StepCategory,\n} from \"@graph/types\";\nimport {\n ARROW_CLOSED_MARKER,\n BINDING_STEP_TYPE,\n EDGE_TYPE_ROUTED,\n NODE_TYPE_PIPE_CARD,\n NODE_TYPE_SIMPLE_DECISION,\n NODE_TYPE_SIMPLE_FRAME,\n NODE_TYPE_SIMPLE_STEP,\n NODE_TYPE_SIMPLE_TERMINAL,\n STEP_CATEGORY,\n graphSpecMode,\n isPluralMultiplicity,\n stuffNodeId,\n} from \"@graph/types\";\nimport { getPipeBlueprint, resolveConceptRef } from \"@graph/graphAnalysis\";\nimport { makePipeRef, parsePipeRef } from \"@graph/pipeRefs\";\nimport { buildControllerNodes, sortParentsFirst } from \"@graph/graphControllers\";\nimport type { ControllerRect } from \"@graph/graphControllers\";\nimport {\n conceptPlainName,\n humanizeIdentifier,\n isGenericConcept,\n outcomeLabel,\n sentenceCase,\n stripDomain,\n} from \"./humanize\";\nimport { estimateWrap, textWidthPx } from \"./textMetrics\";\n\n/** The domain of the concepts the standard defines, as a spec's concept registry records them. */\nconst NATIVE_DOMAIN = \"native\";\n\n// ─── Metrics (keep in sync with SimpleStyle.css) ────────────────────────────\n// The layout sizes every node before the DOM exists, so these mirror the\n// stylesheet: box widths, paddings, font sizes and line heights.\n\nexport const SIMPLE_STEP_WIDTH = 160;\n/**\n * The wider box a step takes when its title would need more lines than\n * `SIMPLE_TITLE_MAX_LINES` at `SIMPLE_STEP_WIDTH` and fits them at this one:\n * one step of width, not a box per title, so a column of steps keeps its rhythm.\n */\nexport const SIMPLE_STEP_WIDE_WIDTH = 200;\nconst STEP_PADDING_X = 12;\nconst STEP_PADDING_Y = 10;\nconst STEP_BORDER = 1.5;\nconst STEP_HEADER_HEIGHT = 16;\nconst STEP_GAP = 6;\nexport const SIMPLE_STEP_TITLE_FONT_PX = 15;\nconst STEP_TITLE_LINE_HEIGHT = 20;\nexport const SIMPLE_TITLE_MAX_LINES = 3;\nconst STEP_MARKER_HEIGHT = 18;\nconst STEP_INNER_HEIGHT = 22;\n\n// An arrow's label: an outcome of a decision.\nconst EDGE_LABEL_FONT_PX = 11;\nconst EDGE_LABEL_LINE_HEIGHT = 13;\nconst EDGE_LABEL_PADDING: readonly [number, number] = [5, 3];\n\nexport const SIMPLE_TERMINAL_WIDTH = 124;\nconst TERMINAL_PADDING_X = 12;\nconst TERMINAL_PADDING_TOP = 10;\n/** The wave at the foot of the document shape. */\nconst TERMINAL_PADDING_BOTTOM = 16;\nconst TERMINAL_TITLE_FONT_PX = 13;\nconst TERMINAL_TITLE_LINE_HEIGHT = 17;\nconst TERMINAL_TITLE_MAX_LINES = 2;\nconst TERMINAL_SUBTITLE_HEIGHT = 17;\nconst TERMINAL_MIN_HEIGHT = 52;\n\n/**\n * The widths a decision diamond may take, narrowest first: it takes the first\n * at which its text fits in `SIMPLE_TITLE_MAX_LINES`, so a long question\n * widens the diamond rather than losing its end.\n */\nexport const SIMPLE_DECISION_WIDTHS: readonly number[] = [188, 220, 252, 284];\nconst DECISION_TITLE_FONT_PX = 13;\nconst DECISION_TITLE_LINE_HEIGHT = 16;\n/**\n * The share of a diamond's width its text may use. A centered rectangle fits\n * in a rhombus when its width and height shares sum to at most one, so the\n * text block's height share is what remains, minus a margin.\n */\nconst DECISION_TEXT_WIDTH_SHARE = 0.56;\nconst DECISION_TEXT_HEIGHT_SHARE = 0.4;\nconst DECISION_MIN_HEIGHT = 84;\n\n/**\n * Room kept free at the end of every line the layout wraps text for: the\n * browser breaks a line the moment it overflows, and a fraction of a pixel of\n * rounding must not cost a title its last line.\n */\nconst WRAP_SLACK_PX = 2;\n\n/** The width a step's title wraps at in a box of a width: inside its border and padding. */\nfunction stepTitleWidth(boxWidth: number): number {\n return boxWidth - 2 * (STEP_PADDING_X + STEP_BORDER) - WRAP_SLACK_PX;\n}\nconst TERMINAL_TITLE_WIDTH = SIMPLE_TERMINAL_WIDTH - 2 * TERMINAL_PADDING_X - WRAP_SLACK_PX;\n\nfunction decisionTextWidth(diamondWidth: number): number {\n return Math.floor(diamondWidth * DECISION_TEXT_WIDTH_SHARE) - WRAP_SLACK_PX;\n}\n\n/**\n * The controller depth from which the simple style folds a sub-method by\n * default (the method's own controller is depth 0). Deep nesting is where a\n * flowchart stops reading as one; a folded sub-method is one step a reader\n * can open. Only sequences fold this way: a decision, a loop or a parallel is\n * structure the reader came to see, however deep it sits.\n */\nexport const SIMPLE_FOLD_DEPTH = 3;\n\n// ─── Step categories ────────────────────────────────────────────────────────\n\n/**\n * The category each card class reads as. Exhaustive over `NodePipeType`, so a\n * new pipe class must say what it is. A binding step is drawn only when it\n * failed, as the field it was picking.\n */\nexport const STEP_CATEGORY_BY_PIPE_TYPE: Record<NodePipeType, StepCategory> = {\n PipeLLM: STEP_CATEGORY.AI,\n PipeStructure: STEP_CATEGORY.AI,\n PipeJudge: STEP_CATEGORY.JUDGE,\n PipeExtract: STEP_CATEGORY.EXTRACT,\n PipeImgGen: STEP_CATEGORY.IMAGE,\n PipeSearch: STEP_CATEGORY.SEARCH,\n PipeFunc: STEP_CATEGORY.CODE,\n PipeCompose: STEP_CATEGORY.TEMPLATE,\n PipeDocGen: STEP_CATEGORY.DOCUMENT,\n PipeSignature: STEP_CATEGORY.PLANNED,\n PipeSequence: STEP_CATEGORY.STEPS,\n PipeParallel: STEP_CATEGORY.PARALLEL,\n PipeCondition: STEP_CATEGORY.DECISION,\n PipeBatch: STEP_CATEGORY.REPEAT,\n BindingStep: STEP_CATEGORY.PICK,\n};\n\n/** The plain word each category is shown with. */\nexport const STEP_CATEGORY_WORDS: Record<StepCategory, string> = {\n ai: \"AI\",\n extract: \"Extract\",\n image: \"Image\",\n search: \"Search\",\n code: \"Code\",\n template: \"Template\",\n document: \"Document\",\n judge: \"Judge\",\n planned: \"To build\",\n steps: \"Steps\",\n parallel: \"Side by side\",\n decision: \"Decision\",\n repeat: \"For each\",\n pick: \"Pick\",\n};\n\n// ─── Sizes ──────────────────────────────────────────────────────────────────\n\n/** The size of a step box for its title and markers. */\nexport function simpleStepSize(\n title: string,\n options: { forEach?: boolean; innerSteps?: boolean } = {},\n): { width: number; height: number } {\n let width = SIMPLE_STEP_WIDTH;\n let wrap = simpleTitleWrap(title);\n if (wrap.clamped) {\n const wide = simpleTitleWrap(title, SIMPLE_STEP_WIDE_WIDTH);\n // Only a title the wider box saves takes it: one cut either way is cut\n // at the narrow width, which keeps the drawing compact.\n if (!wide.clamped) {\n width = SIMPLE_STEP_WIDE_WIDTH;\n wrap = wide;\n }\n }\n let height =\n 2 * STEP_PADDING_Y +\n STEP_HEADER_HEIGHT +\n STEP_GAP +\n Math.max(1, wrap.drawnLines) * STEP_TITLE_LINE_HEIGHT;\n if (options.forEach) height += STEP_GAP + STEP_MARKER_HEIGHT;\n if (options.innerSteps) height += STEP_GAP + STEP_INNER_HEIGHT;\n return { width, height: Math.ceil(height + 2 * STEP_BORDER) };\n}\n\n/** The size of an input or output's document shape. */\nexport function simpleTerminalSize(\n title: string,\n hasSubtitle: boolean,\n): { width: number; height: number } {\n const wrap = estimateWrap(\n title,\n TERMINAL_TITLE_WIDTH,\n TERMINAL_TITLE_FONT_PX,\n TERMINAL_TITLE_MAX_LINES,\n );\n const height =\n TERMINAL_PADDING_TOP +\n Math.max(1, wrap.drawnLines) * TERMINAL_TITLE_LINE_HEIGHT +\n (hasSubtitle ? TERMINAL_SUBTITLE_HEIGHT : 0) +\n TERMINAL_PADDING_BOTTOM;\n return { width: SIMPLE_TERMINAL_WIDTH, height: Math.max(TERMINAL_MIN_HEIGHT, height) };\n}\n\n/**\n * The size of a decision diamond for its text: the narrowest of\n * `SIMPLE_DECISION_WIDTHS` its text fits in, and the height that keeps the\n * text block inside the rhombus.\n */\nexport function simpleDecisionSize(title: string): { width: number; height: number } {\n let width = SIMPLE_DECISION_WIDTHS[0];\n let wrap = estimateWrap(\n title,\n decisionTextWidth(width),\n DECISION_TITLE_FONT_PX,\n SIMPLE_TITLE_MAX_LINES,\n );\n for (const candidate of SIMPLE_DECISION_WIDTHS.slice(1)) {\n if (!wrap.clamped) break;\n width = candidate;\n wrap = estimateWrap(\n title,\n decisionTextWidth(width),\n DECISION_TITLE_FONT_PX,\n SIMPLE_TITLE_MAX_LINES,\n );\n }\n const textHeight = Math.max(1, wrap.drawnLines) * DECISION_TITLE_LINE_HEIGHT;\n const height = Math.max(DECISION_MIN_HEIGHT, Math.ceil(textHeight / DECISION_TEXT_HEIGHT_SHARE));\n return { width, height };\n}\n\n/**\n * How a step title wraps in a box of a width (the default box unless given):\n * what the readability rubric checks, at the width the step is drawn at.\n */\nexport function simpleTitleWrap(title: string, boxWidth: number = SIMPLE_STEP_WIDTH) {\n return estimateWrap(\n title,\n stepTitleWidth(boxWidth),\n SIMPLE_STEP_TITLE_FONT_PX,\n SIMPLE_TITLE_MAX_LINES,\n );\n}\n\n// ─── Status ─────────────────────────────────────────────────────────────────\n\n/** Worst first: the status a set of runs of one step shows. */\nconst STATUS_PRECEDENCE: readonly PipeStatus[] = [\n \"failed\",\n \"running\",\n \"scheduled\",\n \"canceled\",\n \"skipped\",\n \"succeeded\",\n];\n\n/** The status a step drawn once stands for when it ran several times (once per batch item). */\nexport function aggregateStatus(statuses: readonly PipeStatus[]): PipeStatus | undefined {\n for (const status of STATUS_PRECEDENCE) {\n if (statuses.includes(status)) return status;\n }\n return undefined;\n}\n\n// ─── The projection ─────────────────────────────────────────────────────────\n\nexport interface SimpleProjectionInput {\n graphspec: GraphSpec;\n /** The dataflow graph after the folds: what the detailed style would lay out. */\n nodes: GraphNode[];\n /** The analysis after the folds (a folded controller is one producer and consumer). */\n analysis: DataflowAnalysis;\n /** The analysis before the folds: the full containment, which a fold hides. */\n rawAnalysis: DataflowAnalysis;\n}\n\nexport interface SimpleProjection {\n nodes: GraphNode[];\n edges: GraphEdge[];\n /**\n * The analysis the layout and the frames read: the \"for each\" frames are its\n * only controllers, holding the nodes drawn inside them, and it names no\n * producer or consumer, since every value the style draws is a method input\n * or a final output and sits outside every frame.\n */\n analysis: DataflowAnalysis;\n /** Each spec node the style does not draw, mapped to the drawn node that stands for it. */\n standIns: ReadonlyMap<string, string>;\n}\n\nfunction parentMap(\n containmentTree: Readonly<Record<string, readonly string[]>>,\n): Record<string, string> {\n const parentOf: Record<string, string> = {};\n for (const [ctrlId, children] of Object.entries(containmentTree)) {\n for (const child of children) parentOf[child] = ctrlId;\n }\n return parentOf;\n}\n\nfunction unique<T>(items: readonly T[]): T[] {\n return [...new Set(items)];\n}\n\n/**\n * Which node of a run stands in for which: a batch runs its branch once per\n * item, and the flowchart draws each step of it once. Two runs are the same\n * step when the same pipes lead to them from the top of the method, a pipe\n * known by its full reference so that two domains' pipes of one code stay two\n * steps, siblings outside a batch told apart by their order among calls to the\n * same pipe and the items of a batch not told apart at all. The run that shows\n * the step best stands in for the others, the first in containment order among\n * equals, so a first item that skipped a step, leaving it nothing to show, or\n * whose loop ran over an empty list, does not hide the items that ran it. A\n * static spec draws each batch's branch once, so nothing in it stands in for\n * anything.\n *\n * `drawability` ranks a run: 0 when it cannot be drawn, higher the more of the\n * step it shows. `representativeOf` holds only the runs another stands in for;\n * `order` is every node in containment order, parents first.\n */\nfunction batchRepresentatives(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n drawability: (id: string) => number,\n): { representativeOf: Map<string, string>; order: string[] } {\n const specById = new Map(graphspec.nodes.map((n) => [n.id, n]));\n const runsWithPath = new Map<string, string[]>();\n const representativeOf = new Map<string, string>();\n const order: string[] = [];\n\n function visit(ids: readonly string[], parentPath: string, inBatch: boolean): void {\n const seen = new Map<string, number>();\n for (const id of ids) {\n const spec = specById.get(id);\n const pipe = spec ? stepIdentity(spec) : id;\n const occurrence = seen.get(pipe) ?? 0;\n seen.set(pipe, occurrence + 1);\n const path = `${parentPath}/${pipe}#${inBatch ? \"*\" : occurrence}`;\n const runs = runsWithPath.get(path);\n if (runs) runs.push(id);\n else runsWithPath.set(path, [id]);\n order.push(id);\n visit(analysis.containmentTree[id] ?? [], path, spec?.pipe_type === \"PipeBatch\");\n }\n }\n visit(\n graphspec.nodes.filter((n) => !analysis.childNodeIds.has(n.id)).map((n) => n.id),\n \"\",\n false,\n );\n for (const runs of runsWithPath.values()) {\n if (runs.length < 2) continue;\n const best = Math.max(...runs.map(drawability));\n const representative = runs.find((id) => drawability(id) === best) ?? runs[0];\n for (const id of runs) if (id !== representative) representativeOf.set(id, representative);\n }\n return { representativeOf, order };\n}\n\n/**\n * What makes two runs the same step: the node's kind and the pipe it calls, by\n * its full reference where the spec gives a domain, or a binding's path.\n */\nfunction stepIdentity(spec: GraphSpecNode): string {\n const ref =\n spec.domain_code && spec.pipe_code\n ? makePipeRef(spec.domain_code, spec.pipe_code)\n : (spec.pipe_code ?? spec.pipe_type);\n return `${spec.kind}:${ref}`;\n}\n\n/** \"For each page\": an item name inside a sentence, keeping an acronym's capitals. */\nfunction forEachLabel(itemName: string): string {\n const words = humanizeIdentifier(itemName);\n const first = words.split(\" \")[0] ?? \"\";\n const inSentence =\n first.length >= 2 && first === first.toUpperCase()\n ? words\n : words.charAt(0).toLowerCase() + words.slice(1);\n return `For each ${inSentence}`;\n}\n\n/**\n * Project the folded dataflow graph into the simple style's nodes and edges.\n * Node ids stay the GraphSpec's — a step keeps its pipe node's id and a method\n * input or final output keeps its stuff node's — so selection, the detail\n * panel, run status and validation decorations find them as in any style.\n */\nexport function projectSimpleGraph(input: SimpleProjectionInput): SimpleProjection {\n const { graphspec, nodes, analysis, rawAnalysis } = input;\n const graphMode = graphSpecMode(graphspec);\n const specById = new Map<string, GraphSpecNode>(graphspec.nodes.map((n) => [n.id, n]));\n\n const cards = new Map<string, GraphNode>();\n for (const node of nodes) {\n if (node.type === NODE_TYPE_PIPE_CARD) cards.set(node.id, node);\n }\n const liveControllers = analysis.controllerNodeIds;\n const pipeTypeOf = (id: string): string | undefined => specById.get(id)?.pipe_type;\n\n // ── A run draws a batch's branch once per item; the flowchart draws it once.\n // Every run of a step stands in for the run of the same step that shows it\n // best, and only that run is drawn. A step only a later item reached, such as\n // the branch of a decision the first item did not take, is drawn too, where\n // its first run sits. A run shows its step fully when it is drawn with what\n // it holds: an operator, a folded step, or a controller the folds left open.\n // A controller run that held nothing, such as a loop over an empty list, is\n // drawn empty, so it stands in only where no run held anything. A controller\n // hidden inside a fold cannot be drawn at all.\n const { representativeOf, order } = batchRepresentatives(graphspec, rawAnalysis, (id) => {\n if (liveControllers.has(id)) return 2;\n const card = cards.get(id);\n if (!card) return 0;\n return card.data.isController !== true && specById.get(id)?.kind === \"controller\" ? 1 : 2;\n });\n const repOf = (id: string): string => representativeOf.get(id) ?? id;\n // The containment the flowchart draws: a later run's own steps hang from the\n // run drawn for what holds them.\n const parentOf: Record<string, string> = {};\n for (const [id, parent] of Object.entries(parentMap(rawAnalysis.containmentTree))) {\n parentOf[id] = repOf(parent);\n }\n // A fold hides what it holds in every item. Where the run drawn for a step is\n // folded and a later item's run of it is not, as after a fold of that run\n // alone, the later run's steps hang from the folded step and are drawn as it.\n const foldedAbove = new Map<string, string>();\n for (const id of order) {\n for (let current = parentOf[id]; current; current = parentOf[current]) {\n if (cards.get(current)?.data.isController === true) {\n foldedAbove.set(id, current);\n break;\n }\n }\n }\n /** The drawn node a run of the spec is drawn as. */\n const drawnAs = (id: string): string => foldedAbove.get(id) ?? repOf(id);\n const hidden = new Set([...representativeOf.keys(), ...foldedAbove.keys()]);\n const childrenOf: Record<string, string[]> = {};\n for (const id of order) {\n const parent = parentOf[id];\n if (parent && !hidden.has(id)) (childrenOf[parent] ??= []).push(id);\n }\n\n function isInside(id: string, ancestorId: string): boolean {\n let current = parentOf[id];\n while (current) {\n if (current === ancestorId) return true;\n current = parentOf[current];\n }\n return false;\n }\n\n // A batch's branch is the run drawn for its first item's: a later item's\n // when the first one's has nothing to show.\n const batchBranch: Record<string, string> = {};\n for (const ctrlId of liveControllers) {\n if (hidden.has(ctrlId) || pipeTypeOf(ctrlId) !== \"PipeBatch\") continue;\n const first = analysis.containmentTree[ctrlId]?.[0];\n if (first) batchBranch[ctrlId] = repOf(first);\n }\n\n const decisions = [...liveControllers].filter(\n (id) => !hidden.has(id) && pipeTypeOf(id) === \"PipeCondition\",\n );\n const decisionSet = new Set(decisions);\n // A batch whose branch is a sub-method gets a frame; one whose branch is a\n // single step marks that step instead.\n const frameSet = new Set(\n Object.entries(batchBranch)\n .filter(([batchId, branchId]) => !hidden.has(batchId) && liveControllers.has(branchId))\n .map(([batchId]) => batchId),\n );\n const markedSteps: Record<string, string> = {};\n for (const [batchId, branchId] of Object.entries(batchBranch)) {\n if (!hidden.has(batchId) && cards.has(branchId)) markedSteps[branchId] = batchId;\n }\n\n const runStatuses = new Map<string, PipeStatus[]>();\n for (const spec of graphspec.nodes) {\n const rep = repOf(spec.id);\n const statuses = runStatuses.get(rep);\n if (statuses) statuses.push(spec.status);\n else runStatuses.set(rep, [spec.status]);\n }\n\n /** A node's status; one drawn once for every batch item stands for all of its runs. */\n function stepStatus(spec: GraphSpecNode): PipeStatus {\n return aggregateStatus(runStatuses.get(spec.id) ?? []) ?? spec.status;\n }\n\n // A binding step is plumbing, drawn only when it failed: the run stopped\n // there, and the step is where a reader finds the error.\n const drawnSteps = [...cards.values()].filter(\n (card) =>\n !hidden.has(card.id) &&\n (card.data.pipeType !== BINDING_STEP_TYPE ||\n aggregateStatus(runStatuses.get(card.id) ?? []) === \"failed\"),\n );\n const drawnStepIds = new Set(drawnSteps.map((card) => card.id));\n\n // ── The method's inputs and final outputs: the root pipes' io. Where a spec\n // has several roots, a value one root writes and another reads passes\n // between them, and is neither.\n const roots = graphspec.nodes.filter(\n (n) =>\n !rawAnalysis.childNodeIds.has(n.id) && (n.kind === \"controller\" || n.kind === \"operator\"),\n );\n const rootReads = new Set(roots.flatMap((r) => r.io.inputs.map((item) => item.digest)));\n const rootWrites = new Set(roots.flatMap((r) => r.io.outputs.map((item) => item.digest)));\n const inputItems = new Map<string, GraphSpecNodeIoItem>();\n const outputItems = new Map<string, GraphSpecNodeIoItem>();\n for (const root of roots) {\n for (const item of root.io.inputs) {\n if (!item.digest || inputItems.has(item.digest) || rootWrites.has(item.digest)) continue;\n inputItems.set(item.digest, item);\n }\n }\n for (const root of roots) {\n for (const item of root.io.outputs) {\n if (!item.digest || inputItems.has(item.digest) || outputItems.has(item.digest)) continue;\n if (rootReads.has(item.digest)) continue;\n outputItems.set(item.digest, item);\n }\n }\n\n // ── Where each value comes from, among the nodes drawn.\n // A value no step writes is combined from others: a parallel's combined\n // output, a batch's list of results, or the item a batch hands one branch.\n // Indexed once, since a batch run has one such value per item.\n const combinedFrom = new Map<string, string[]>();\n for (const edge of graphspec.edges) {\n const from = edge.source_stuff_digest;\n const into = edge.target_stuff_digest;\n if (!from || !into || from === into) continue;\n if (\n edge.kind !== \"parallel_combine\" &&\n edge.kind !== \"batch_aggregate\" &&\n edge.kind !== \"batch_item\"\n ) {\n continue;\n }\n const list = combinedFrom.get(into);\n if (list) list.push(from);\n else combinedFrom.set(into, [from]);\n }\n const sourceMemo = new Map<string, string[]>();\n function sourcesOf(digest: string, visiting: Set<string> = new Set()): string[] {\n const memo = sourceMemo.get(digest);\n if (memo) return memo;\n if (inputItems.has(digest)) return [stuffNodeId(digest)];\n if (visiting.has(digest)) return [];\n visiting.add(digest);\n const found: string[] = [];\n const producers = analysis.stuffProducers[digest] ?? [];\n for (const producerId of producers) {\n const card = cards.get(drawnAs(producerId));\n if (!card) continue;\n if (card.data.pipeType === BINDING_STEP_TYPE && !drawnStepIds.has(card.id)) {\n // A binding passes its root through under a new name.\n for (const item of specById.get(producerId)?.io.inputs ?? []) {\n if (item.digest) found.push(...sourcesOf(item.digest, visiting));\n }\n } else {\n found.push(card.id);\n }\n }\n if (producers.length === 0) {\n for (const from of combinedFrom.get(digest) ?? []) found.push(...sourcesOf(from, visiting));\n }\n visiting.delete(digest);\n const result = unique(found);\n sourceMemo.set(digest, result);\n return result;\n }\n\n // ── Decisions: the outcome each of their branches is taken on.\n function branchOf(decisionId: string, nodeId: string): string {\n let current = nodeId;\n while (parentOf[current] && parentOf[current] !== decisionId) current = parentOf[current];\n return current;\n }\n\n function outcomeFor(decisionId: string, branchId: string): string | undefined {\n const branch = specById.get(branchId);\n const tagged = branch?.tags?.outcome;\n if (tagged) return outcomeLabel(tagged);\n const contains = graphspec.edges.find(\n (e) =>\n e.kind === \"contains\" && repOf(e.source) === decisionId && e.target === branchId && e.label,\n );\n if (contains?.label) return outcomeLabel(contains.label);\n const decision = specById.get(decisionId);\n if (!decision?.domain_code || !decision.pipe_code || !branch?.pipe_code) return undefined;\n const blueprint = getPipeBlueprint(\n graphspec,\n makePipeRef(decision.domain_code, decision.pipe_code),\n );\n if (blueprint?.type !== \"PipeCondition\") return undefined;\n // An outcome is the branch's when it names the branch's pipe: its code, and\n // its domain, which a bare reference takes from the condition's own.\n const conditionDomain = decision.domain_code;\n const branchCode = branch.pipe_code;\n function namesBranch(pipeRef: string): boolean {\n const parsed = parsePipeRef(pipeRef);\n if (!parsed) return stripDomain(pipeRef) === branchCode;\n if (parsed.pipeCode !== branchCode) return false;\n const domain = parsed.domainPath ?? conditionDomain;\n return branch?.domain_code === undefined || branch.domain_code === domain;\n }\n const outcomes = Object.entries(blueprint.outcome_map)\n .filter(([, pipeRef]) => namesBranch(pipeRef))\n .map(([outcome]) => outcome);\n if (blueprint.default_outcome && namesBranch(blueprint.default_outcome)) {\n outcomes.push(\"default\");\n }\n return outcomes.length > 0 ? outcomeLabel(outcomes.join(\"|\")) : undefined;\n }\n\n function decisionsAbove(nodeId: string): string[] {\n const chain: string[] = [];\n let current = parentOf[nodeId];\n while (current) {\n if (decisionSet.has(current)) chain.unshift(current);\n current = parentOf[current];\n }\n return chain;\n }\n\n // ── Edges.\n const edges = new Map<string, GraphEdge>();\n let edgeCount = 0;\n function addEdge(source: string, target: string): void {\n if (source === target) return;\n const key = `${source}->${target}`;\n if (edges.has(key)) return;\n const label = decisionSet.has(source)\n ? outcomeFor(source, branchOf(source, target))\n : undefined;\n edges.set(key, simpleEdge(`simple_edge_${edgeCount++}`, source, target, label));\n }\n\n /**\n * Draw the arrows that bring a value from its sources to a node. Where the\n * node sits in a decision's branch and the value comes from outside that\n * decision, the value goes to the decision, and the decision's arrow, labelled\n * with the branch's outcome, goes on — decision by decision, outermost first.\n */\n function route(sources: readonly string[], target: string): void {\n let froms = [...sources];\n for (const decisionId of decisionsAbove(target)) {\n const outside = froms.filter((s) => !isInside(s, decisionId));\n if (outside.length === 0) continue;\n for (const source of outside) addEdge(source, decisionId);\n froms = [decisionId, ...froms.filter((s) => isInside(s, decisionId))];\n }\n for (const source of froms) addEdge(source, target);\n }\n\n const danglingInputs = new Map<string, GraphSpecNodeIoItem>();\n function sourcesOrDangling(digest: string): string[] {\n const sources = sourcesOf(digest);\n if (sources.length > 0) return sources;\n // A value read with no step drawn behind it is shown as an input of its\n // own rather than left hanging.\n const entry = analysis.stuffRegistry[digest];\n if (!entry) return [];\n danglingInputs.set(digest, {\n name: entry.name,\n digest,\n ...(entry.concept !== undefined ? { concept: entry.concept } : {}),\n ...(entry.multiplicity !== undefined ? { multiplicity: entry.multiplicity } : {}),\n });\n return [stuffNodeId(digest)];\n }\n\n // A later run reads its own item's values, which come from the same steps as\n // the first run's, so it only adds an arrow where its item went another way.\n // A value with nothing drawn behind it is shown once, for the first run.\n function routeRead(digest: string, readerId: string): void {\n const target = drawnAs(readerId);\n if (target === readerId) route(sourcesOrDangling(digest), target);\n else route(sourcesOf(digest), target);\n }\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n for (const consumerId of consumers) {\n if (drawnStepIds.has(drawnAs(consumerId))) routeRead(digest, consumerId);\n }\n }\n for (const conditionId of liveControllers) {\n if (!decisionSet.has(drawnAs(conditionId))) continue;\n for (const item of specById.get(conditionId)?.io.inputs ?? []) {\n if (item.digest) routeRead(item.digest, conditionId);\n }\n }\n for (const digest of outputItems.keys()) {\n for (const source of sourcesOf(digest)) addEdge(source, stuffNodeId(digest));\n }\n\n // A decision's arrow goes to the first steps of a branch only. A later step\n // of the branch that also reads a value from before the decision is reached\n // through the steps before it, so a second arrow with the same outcome would\n // say the branch is taken twice.\n for (const [key, edge] of [...edges]) {\n if (!decisionSet.has(edge.source)) continue;\n const branchId = branchOf(edge.source, edge.target);\n const followsInBranch = [...edges.values()].some(\n (other) =>\n other.target === edge.target &&\n other.source !== edge.source &&\n isInside(other.source, branchId),\n );\n if (followsInBranch) edges.delete(key);\n }\n\n // Every branch a decision can take gets its arrow, even one that reads\n // nothing from before the decision.\n function firstDrawnIn(id: string): string | undefined {\n if (drawnStepIds.has(id) || decisionSet.has(id)) return id;\n for (const child of childrenOf[id] ?? []) {\n const found = firstDrawnIn(child);\n if (found) return found;\n }\n return undefined;\n }\n for (const decisionId of decisions) {\n for (const branchId of childrenOf[decisionId] ?? []) {\n const reached = [...edges.values()].some(\n (e) => e.source === decisionId && (e.target === branchId || isInside(e.target, branchId)),\n );\n const entry = reached ? undefined : firstDrawnIn(branchId);\n if (entry) addEdge(decisionId, entry);\n }\n }\n\n // ── Nodes, in the spec's order: inputs, then steps and decisions, then outputs.\n const simpleNodes: GraphNode[] = [];\n\n /**\n * Whether a concept is one the standard defines (`Text`, `Image`, …), as the\n * spec's concept registry records it, which says what kind of value\n * something is without saying what it is.\n */\n function isBuiltInConcept(conceptRef: string): boolean {\n const info = resolveConceptRef(graphspec, conceptRef);\n if (info) return info.domain_code === NATIVE_DOMAIN;\n return conceptRef.startsWith(`${NATIVE_DOMAIN}.`) || isGenericConcept(conceptRef);\n }\n\n /**\n * A method input is named by its variable, with its concept's plain name\n * beneath when that says more. The final output is named by its concept when\n * the author defined it (\"Bid summary\"), and like an input when the concept\n * is built in, since a shape reading \"Text\" says nothing.\n */\n function terminalNode(item: GraphSpecNodeIoItem, kind: \"input\" | \"output\"): GraphNode {\n const digest = item.digest as string;\n const concept = item.concept;\n const isList = isPluralMultiplicity(item.multiplicity);\n let title: string;\n let subtitle: string | undefined;\n if (kind === \"output\" && concept && !isBuiltInConcept(concept)) {\n title = conceptPlainName(concept);\n } else {\n title = humanizeIdentifier(item.name);\n const plain = concept ? conceptPlainName(concept) : undefined;\n subtitle = plain && plain.toLowerCase() !== title.toLowerCase() ? plain : undefined;\n }\n const simple: SimpleNodePayload = { kind, title, isList, ...(subtitle ? { subtitle } : {}) };\n return {\n id: stuffNodeId(digest),\n type: NODE_TYPE_SIMPLE_TERMINAL,\n data: {\n isPipe: false,\n isStuff: true,\n labelText: title,\n stuffRole: kind,\n stuffDigest: digest,\n graphMode,\n simple,\n layoutSize: simpleTerminalSize(title, subtitle !== undefined),\n },\n position: { x: 0, y: 0 },\n };\n }\n\n for (const item of [...inputItems.values(), ...danglingInputs.values()]) {\n simpleNodes.push(terminalNode(item, \"input\"));\n }\n\n /**\n * The name of the item a batch hands its branch: the blueprint's, unless it\n * is unwritten yet, else the name the branch reads its item under, else the\n * item's own.\n */\n function itemName(batchId: string): string | undefined {\n const batch = specById.get(batchId);\n if (batch?.domain_code && batch.pipe_code) {\n const blueprint = getPipeBlueprint(\n graphspec,\n makePipeRef(batch.domain_code, batch.pipe_code),\n );\n if (blueprint?.type === \"PipeBatch\" && blueprint.batch_params.input_item_stuff_name) {\n return blueprint.batch_params.input_item_stuff_name;\n }\n }\n // The branch drawn may be a later item's run, so the items of every run of\n // the batch are looked for, not only the first run's.\n const runsOfBatch = new Set([batchId]);\n for (const [id, representative] of representativeOf) {\n if (representative === batchId) runsOfBatch.add(id);\n }\n const itemEdges = graphspec.edges.filter(\n (e) => e.kind === \"batch_item\" && runsOfBatch.has(e.source) && e.target_stuff_digest,\n );\n const itemDigests = new Set(itemEdges.map((e) => e.target_stuff_digest));\n const branchId = batchBranch[batchId] ?? rawAnalysis.containmentTree[batchId]?.[0];\n const branch = branchId ? specById.get(branchId) : undefined;\n const read = branch?.io.inputs.find((i) => itemDigests.has(i.digest));\n if (read?.name) return read.name;\n const firstItem = itemEdges.find((e) => e.source === batchId) ?? itemEdges[0];\n return firstItem?.target_stuff_digest\n ? analysis.stuffRegistry[firstItem.target_stuff_digest]?.name || undefined\n : undefined;\n }\n\n function stepsInside(id: string): number {\n const spec = specById.get(id);\n const children = rawAnalysis.containmentTree[id] ?? [];\n if (children.length === 0) return spec && spec.kind === \"operator\" ? 1 : 0;\n const counted = spec?.pipe_type === \"PipeBatch\" ? children.slice(0, 1) : children;\n return counted.reduce((sum, child) => sum + stepsInside(child), 0);\n }\n\n /**\n * What a step box says: its description, or for a folded batch, its\n * branch's. A binding step, drawn only when it failed, names the field it was\n * picking, the last segment of its path, since its description is the\n * runtime's own sentence about names.\n */\n function stepTitle(spec: GraphSpecNode, folded: boolean): string {\n if (spec.pipe_type === BINDING_STEP_TYPE) {\n return humanizeIdentifier(spec.pipe_code?.split(\".\").pop() ?? \"\");\n }\n let source = spec;\n if (folded && spec.pipe_type === \"PipeBatch\") {\n const branch = specById.get(rawAnalysis.containmentTree[spec.id]?.[0] ?? \"\");\n if (branch) source = branch;\n }\n const described = sentenceCase(source.description ?? \"\");\n return described || humanizeIdentifier(source.pipe_code ?? \"\");\n }\n\n const stepById = new Map(drawnSteps.map((card) => [card.id, card]));\n for (const spec of graphspec.nodes) {\n const card = stepById.get(spec.id);\n if (card) {\n const folded = card.data.isController === true;\n const pipeType: NodePipeType = spec.pipe_type;\n const title = stepTitle(spec, folded);\n const markerBatch =\n markedSteps[spec.id] ?? (folded && pipeType === \"PipeBatch\" ? spec.id : undefined);\n const markerItem = markerBatch ? itemName(markerBatch) : undefined;\n const forEach = markerBatch ? forEachLabel(markerItem || \"item\") : undefined;\n const innerStepCount = folded ? stepsInside(spec.id) : undefined;\n const onExpand = card.data.pipeCardData?.onExpand;\n const simple: SimpleNodePayload = {\n kind: \"step\",\n title,\n category: STEP_CATEGORY_BY_PIPE_TYPE[pipeType],\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n ...(forEach ? { forEach } : {}),\n ...(innerStepCount !== undefined ? { innerStepCount } : {}),\n ...(onExpand ? { onExpand } : {}),\n };\n simpleNodes.push({\n id: spec.id,\n type: NODE_TYPE_SIMPLE_STEP,\n data: {\n nodeData: spec,\n isPipe: !folded,\n isStuff: false,\n isController: folded,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple,\n layoutSize: simpleStepSize(title, {\n forEach: forEach !== undefined,\n innerSteps: innerStepCount !== undefined,\n }),\n },\n position: { x: 0, y: 0 },\n });\n continue;\n }\n if (decisionSet.has(spec.id)) {\n const title =\n sentenceCase(spec.description ?? \"\") || humanizeIdentifier(spec.pipe_code ?? \"\");\n simpleNodes.push({\n id: spec.id,\n type: NODE_TYPE_SIMPLE_DECISION,\n data: {\n nodeData: spec,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple: {\n kind: \"decision\",\n title,\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n },\n layoutSize: simpleDecisionSize(title),\n },\n position: { x: 0, y: 0 },\n });\n }\n }\n\n for (const item of outputItems.values()) {\n simpleNodes.push(terminalNode(item, \"output\"));\n }\n\n // ── The frames: each \"for each\" batch holds the nodes drawn in its branch.\n const nearestFrame = (id: string): string | undefined => {\n let current = parentOf[id];\n while (current) {\n if (frameSet.has(current)) return current;\n current = parentOf[current];\n }\n return undefined;\n };\n const containmentTree: Record<string, string[]> = {};\n for (const frameId of frameSet) containmentTree[frameId] = [];\n for (const node of simpleNodes) {\n const frameId = node.data.isStuff ? undefined : nearestFrame(node.id);\n if (frameId) containmentTree[frameId].push(node.id);\n }\n for (const frameId of frameSet) {\n const outer = nearestFrame(frameId);\n if (outer) containmentTree[outer].push(frameId);\n }\n const childNodeIds = new Set(Object.values(containmentTree).flat());\n\n // Each frame is carried as a placeholder holding what it says, which the\n // frame pass turns into the positioned group once the layout has sized it.\n for (const frameId of frameSet) {\n const spec = specById.get(frameId);\n if (!spec) continue;\n const title = forEachLabel(itemName(frameId) || \"item\");\n simpleNodes.push({\n id: frameId,\n type: NODE_TYPE_SIMPLE_FRAME,\n data: {\n nodeData: spec,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple: {\n kind: \"frame\",\n title,\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n },\n },\n position: { x: 0, y: 0 },\n });\n }\n\n // ── What stands for a node the flowchart does not draw, so that a validation\n // issue pinned to it still badges a drawn node: a later item's run stands for\n // the run drawn, or for the folded step it is drawn as, a batch of one step\n // for its marked step, a binding step for\n // the first drawn node that reads its result (or, when nothing does, for what\n // stands for the sub-method holding it), and a sub-method drawn as its steps\n // for the first of them.\n const drawnIds = new Set(simpleNodes.map((n) => n.id));\n const standIns = new Map<string, string>();\n const resolving = new Set<string>();\n function standIn(id: string): string | undefined {\n if (drawnIds.has(id)) return id;\n if (standIns.has(id)) return standIns.get(id);\n if (resolving.has(id)) return undefined;\n resolving.add(id);\n let found: string | undefined;\n const representative = foldedAbove.get(id) ?? representativeOf.get(id);\n const branch = batchBranch[id];\n if (representative) {\n found = standIn(representative);\n } else if (branch && markedSteps[branch] === id) {\n found = standIn(branch);\n } else if (pipeTypeOf(id) === BINDING_STEP_TYPE) {\n for (const item of specById.get(id)?.io.outputs ?? []) {\n if (!item.digest) continue;\n for (const reader of rawAnalysis.stuffConsumers[item.digest] ?? []) {\n found ??= standIn(reader);\n }\n if (!found && drawnIds.has(stuffNodeId(item.digest))) found = stuffNodeId(item.digest);\n if (found) break;\n }\n // A result nothing reads: what stands for the sub-method holding it.\n const parent = parentOf[id];\n if (!found && parent) found = standIn(parent);\n } else {\n for (const child of childrenOf[id] ?? []) {\n found = standIn(child);\n if (found) break;\n }\n }\n resolving.delete(id);\n if (found) standIns.set(id, found);\n return found;\n }\n for (const spec of graphspec.nodes) standIn(spec.id);\n\n return {\n nodes: simpleNodes,\n edges: [...edges.values()],\n standIns,\n analysis: {\n stuffRegistry: analysis.stuffRegistry,\n stuffProducers: {},\n stuffConsumers: {},\n controllerNodeIds: frameSet,\n childNodeIds,\n containmentTree,\n },\n };\n}\n\n/**\n * The size an arrow label is drawn at, background included, so the layout\n * reserves room for it: the text at `EDGE_LABEL_FONT_PX`, padded by\n * `EDGE_LABEL_PADDING`.\n */\nexport function simpleEdgeLabelSize(label: string): { width: number; height: number } {\n const [padX, padY] = EDGE_LABEL_PADDING;\n return {\n width: Math.ceil(textWidthPx(label, EDGE_LABEL_FONT_PX)) + 2 * padX,\n height: EDGE_LABEL_LINE_HEIGHT + 2 * padY,\n };\n}\n\n// The simple style draws every arrow along the layout's route (`EDGE_TYPE_ROUTED`),\n// whatever curve the host picked for the detailed style: a flowchart's arrows\n// go around the steps between their ends, never through them.\nfunction simpleEdge(\n id: string,\n source: string,\n target: string,\n label: string | undefined,\n): GraphEdge {\n const edge: GraphEdge = {\n id,\n source,\n target,\n type: EDGE_TYPE_ROUTED,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 1.5 },\n markerEnd: { type: ARROW_CLOSED_MARKER, color: \"var(--color-edge)\" },\n };\n if (label) {\n edge.label = label;\n edge.labelStyle = {\n fontSize: `${EDGE_LABEL_FONT_PX}px`,\n fontFamily: \"var(--font-sans)\",\n fontWeight: 600,\n fill: \"var(--ctrl-condition-text)\",\n };\n edge.labelBgStyle = { fill: \"var(--color-bg)\", fillOpacity: 0.92 };\n edge.labelBgPadding = [...EDGE_LABEL_PADDING];\n edge.labelBgBorderRadius = 4;\n edge.labelSize = simpleEdgeLabelSize(label);\n }\n return edge;\n}\n\n/**\n * The controllers the simple style folds when a graph is first drawn: every\n * sub-method (a sequence) from `SIMPLE_FOLD_DEPTH` levels down. The host's own fold mode\n * still applies on top.\n */\nexport function simpleDefaultFolds(\n graphspec: GraphSpec,\n rawAnalysis: DataflowAnalysis,\n): Set<string> {\n const parentOf = parentMap(rawAnalysis.containmentTree);\n const sequenceIds = new Set(\n graphspec.nodes.filter((n) => n.pipe_type === \"PipeSequence\").map((n) => n.id),\n );\n const folded = new Set<string>();\n for (const ctrlId of rawAnalysis.controllerNodeIds) {\n let depth = 0;\n let current = parentOf[ctrlId];\n while (current) {\n depth += 1;\n current = parentOf[current];\n }\n if (depth >= SIMPLE_FOLD_DEPTH && sequenceIds.has(ctrlId)) folded.add(ctrlId);\n }\n return folded;\n}\n\n/**\n * Wrap the laid-out nodes in the simple style's \"for each\" frames: the group\n * nodes are built by the same pass that builds the detailed style's controller\n * groups, at the positions and sizes the layout gave them, and take what they\n * say from the projection's placeholders. Mutates the content nodes' `parentId`\n * and positions as that pass does, so callers pass a copy of the layout.\n */\nexport function applySimpleFrames(args: {\n nodes: GraphNode[];\n edges: GraphEdge[];\n graphspec: GraphSpec;\n analysis: DataflowAnalysis;\n controllerPositions?: Record<string, ControllerRect>;\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void;\n}): { nodes: GraphNode[]; edges: GraphEdge[] } {\n const { nodes, edges, graphspec, analysis, controllerPositions, onToggleFold } = args;\n const placeholders = new Map<string, GraphNode>();\n const content: GraphNode[] = [];\n for (const node of nodes) {\n if (node.type === NODE_TYPE_SIMPLE_FRAME) placeholders.set(node.id, node);\n else content.push(node);\n }\n if (analysis.controllerNodeIds.size === 0) return { nodes: content, edges };\n const groups = buildControllerNodes(graphspec, analysis, content, controllerPositions);\n const frames = groups.map((group): GraphNode => {\n const placeholder = placeholders.get(group.id);\n const id = group.id;\n const simple = placeholder?.data.simple;\n const withFold =\n simple?.kind === \"frame\" && onToggleFold\n ? { ...simple, onFold: (options?: FoldToggleOptions) => onToggleFold(id, options) }\n : simple;\n return {\n ...group,\n type: NODE_TYPE_SIMPLE_FRAME,\n data: { ...(placeholder?.data ?? group.data), simple: withFold },\n };\n });\n return { nodes: sortParentsFirst([...frames, ...content]), edges };\n}\n","import type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphStyleId,\n} from \"@graph/types\";\nimport { GRAPH_STYLE } from \"@graph/types\";\nimport { applyControllers } from \"@graph/graphControllers\";\nimport type { ControllerRect } from \"@graph/graphControllers\";\nimport { applySimpleFrames, projectSimpleGraph, simpleDefaultFolds } from \"./simpleStyle\";\n\n/** What a style's projection receives: the dataflow graph after the folds, for one spec. */\nexport interface StyleProjectionArgs {\n graphspec: GraphSpec;\n nodes: GraphNode[];\n edges: GraphEdge[];\n /** The analysis after the folds; null for a spec with no data flow. */\n analysis: DataflowAnalysis | null;\n /** The analysis before the folds; null for a spec with no data flow. */\n rawAnalysis: DataflowAnalysis | null;\n}\n\n/** What the layout lays out for a style, and the analysis its frames are built from. */\nexport interface StyleProjection {\n nodes: GraphNode[];\n edges: GraphEdge[];\n analysis: DataflowAnalysis | null;\n /**\n * For a style that draws some of the spec's nodes through others: each node\n * it does not draw, mapped to the drawn node that stands for it, so that a\n * validation issue pinned to the first still badges the second.\n */\n standIns?: ReadonlyMap<string, string>;\n}\n\n/** What a style's frame pass receives: the laid-out nodes, and the viewer's frame and fold state. */\nexport interface StyleFrameArgs {\n nodes: GraphNode[];\n edges: GraphEdge[];\n graphspec: GraphSpec | null;\n analysis: DataflowAnalysis | null;\n controllerPositions?: Record<string, ControllerRect>;\n showControllers: boolean;\n expandedControllers?: ReadonlySet<string>;\n onToggleCollapse?: (controllerId: string) => void;\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void;\n}\n\n/**\n * The drawing pipeline a style contributes, in the order the viewer runs it:\n * the controllers it folds on a fresh graph, the projection of the folded\n * dataflow graph into the nodes and edges it lays out, and the pass that wraps\n * the laid-out nodes in its frames. Pure: the node components that draw the\n * result are the React layer's.\n */\nexport interface GraphStylePipeline {\n defaultFolds(graphspec: GraphSpec, rawAnalysis: DataflowAnalysis): Set<string>;\n project(args: StyleProjectionArgs): StyleProjection;\n frame(args: StyleFrameArgs): { nodes: GraphNode[]; edges: GraphEdge[] };\n}\n\nexport const GRAPH_STYLE_PIPELINES: Record<GraphStyleId, GraphStylePipeline> = {\n // Today's drawing: the dataflow graph as built, framed by controller groups\n // when the viewer shows them.\n [GRAPH_STYLE.DETAILED]: {\n defaultFolds: () => new Set(),\n project: ({ nodes, edges, analysis }) => ({ nodes, edges, analysis }),\n frame: (args) =>\n applyControllers(\n args.nodes,\n args.edges,\n args.graphspec,\n args.analysis,\n args.showControllers,\n args.expandedControllers,\n args.onToggleCollapse,\n args.controllerPositions,\n args.onToggleFold,\n ),\n },\n [GRAPH_STYLE.SIMPLE]: {\n defaultFolds: simpleDefaultFolds,\n project: ({ graphspec, nodes, analysis, rawAnalysis }) =>\n analysis && rawAnalysis\n ? projectSimpleGraph({ graphspec, nodes, analysis, rawAnalysis })\n : { nodes: [], edges: [], analysis: null },\n // Its frames are its own decision, whatever the controllers toggle says.\n frame: (args) =>\n args.graphspec && args.analysis\n ? applySimpleFrames({\n nodes: args.nodes,\n edges: args.edges,\n graphspec: args.graphspec,\n analysis: args.analysis,\n controllerPositions: args.controllerPositions,\n onToggleFold: args.onToggleFold,\n })\n : { nodes: args.nodes, edges: args.edges },\n },\n};\n"],"mappings":"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","names":["dimensionMap","elkEdges","_a","_b","_a","_b","_c","_d","_e","_f"]}
1
+ {"version":3,"sources":["../src/graph/validateGraphSpec.ts","../src/graph/graphAnalysis.ts","../src/graph/pipeCardPayload.ts","../src/graph/graphBuilders.ts","../src/graph/graphFolds.ts","../src/graph/graphValidation.ts","../src/graph/stuffLookup.ts","../src/graph/elkGraphBuilder.ts","../src/graph/graphLayout.ts","../src/graph/edgeRoutes.ts","../src/graph/graphControllers.ts","../src/graph/styles/graphStyles.ts","../src/graph/styles/humanize.ts","../src/graph/styles/textMetrics.ts","../src/graph/styles/simpleStyle.ts","../src/graph/styles/stylePipelines.ts"],"sourcesContent":["/**\n * Single boundary validator for GraphSpec JSON.\n *\n * `validateGraphSpec()` runs once when a spec is loaded from an untyped source\n * (embedded JSON, library consumer input). After it returns, internal code\n * trusts the data and uses the strict types in `types.ts` — no inline\n * defensive checks scattered across modules.\n *\n * The pipelex runtime guarantees specific fields are always present and\n * non-null. This validator enforces those guarantees and fails loudly when\n * they are violated, instead of hiding upstream bugs behind fabricated values.\n *\n * Note: `validateGraphSpec` normalizes the input **in place** — see its doc\n * comment.\n */\nimport type {\n CardNode,\n GraphSpec,\n GraphSpecEdgeKind,\n GraphSpecMode,\n GraphSpecNode,\n NodeKind,\n PipeCallNode,\n PipeStatus,\n} from \"./types\";\nimport { BINDING_STEP_TYPE, KNOWN_PIPE_TYPES } from \"./types\";\n\n/**\n * Thrown by `validateGraphSpec` when a spec violates a pipelex runtime\n * guarantee. `path` points at the offending location (e.g.\n * `nodes[3].io.inputs[0].name`) and `name` is the stable, greppable\n * `\"GraphSpecValidationError\"`.\n */\nexport class GraphSpecValidationError extends Error {\n readonly path: string;\n\n constructor(path: string, detail: string) {\n const location = path === \"\" ? \"<root>\" : path;\n super(`GraphSpec validation failed at ${location}: ${detail}`);\n this.name = \"GraphSpecValidationError\";\n this.path = path;\n }\n}\n\nconst PIPE_STATUSES: ReadonlySet<string> = new Set<PipeStatus>([\n \"succeeded\",\n \"failed\",\n \"running\",\n \"scheduled\",\n \"skipped\",\n \"canceled\",\n]);\n\nconst GRAPH_SPEC_MODES: ReadonlySet<string> = new Set<GraphSpecMode>([\"dry\", \"live\", \"static\"]);\n\n/** The node kinds a spec may carry: the pipe-call kinds, and the binding kind. */\nconst DRAWN_NODE_KINDS: ReadonlySet<string> = new Set<NodeKind>([\n \"controller\",\n \"operator\",\n \"binding\",\n]);\n\nconst EDGE_KINDS: ReadonlySet<string> = new Set<GraphSpecEdgeKind>([\n \"contains\",\n \"data\",\n \"control\",\n \"selected_outcome\",\n \"batch_item\",\n \"batch_aggregate\",\n \"parallel_combine\",\n]);\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\nfunction fail(path: string, detail: string): never {\n throw new GraphSpecValidationError(path, detail);\n}\n\nfunction requireNonEmptyString(value: unknown, path: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n fail(path, `expected a non-empty string, got ${describe(value)}`);\n }\n return value;\n}\n\nfunction validateIoItem(item: unknown, path: string): void {\n if (!isPlainObject(item)) {\n fail(path, `expected an object, got ${describe(item)}`);\n }\n // `name` is guaranteed by pipelex; `concept` and `digest` stay tolerant.\n requireNonEmptyString(item.name, `${path}.name`);\n // `multiplicity` may be absent (a producer predating it, or a single value),\n // but a present one must be the registry's encoding: a malformed marker would\n // otherwise read as single without a word, which is the drop it exists to end.\n const multiplicity = item.multiplicity;\n if (\n multiplicity !== undefined &&\n multiplicity !== null &&\n typeof multiplicity !== \"boolean\" &&\n !(Number.isInteger(multiplicity) && (multiplicity as number) >= 1)\n ) {\n const got = typeof multiplicity === \"number\" ? String(multiplicity) : describe(multiplicity);\n fail(`${path}.multiplicity`, `expected a boolean, a positive integer or null, got ${got}`);\n }\n}\n\nfunction requireNumber(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n fail(path, `expected a finite number, got ${describe(value)}`);\n }\n return value;\n}\n\n/**\n * A cost, which may arrive absent where its producer wrote `null`, normalized to `null`\n * when — and only when — the rated-call count says `null` is what it was.\n *\n * Some hosts drop `null` values from the JSON they relay (ChatGPT does, on the way to\n * an MCP App view), so every unrated cost reaches the renderer as a missing key. Absence\n * alone is ambiguous: a dropped `null`, or a cost the producer failed to write. Invariant\n * 2 on `GraphSpecNodeUsage` settles it: `cost` is `null` exactly when no call was rated.\n * With no rated call the value can only have been `null`. With one, a missing cost is a\n * real gap, and reading it as unrated would understate what the run spent, so it fails.\n */\nfunction normalizeCost(value: unknown, ratedCalls: number, path: string): number | null {\n if (value === null) return null;\n if (value === undefined) {\n if (ratedCalls === 0) return null;\n fail(path, `expected a finite number for ${ratedCalls} rated call(s), got undefined`);\n }\n return requireNumber(value, path);\n}\n\n/**\n * A nullable number that may also be absent, normalized to `null`.\n *\n * For fields added after specs were already being produced. The validator's job is\n * to reject malformed data, not to insist the producer is the latest version: an\n * absent key is unambiguous (the producer predates the field), while a wrong-typed\n * one is not and still fails.\n */\nfunction normalizeNullableNumber(value: unknown, path: string): number | null {\n if (value === undefined || value === null) return null;\n return requireNumber(value, path);\n}\n\nfunction requireNumberRecord(value: unknown, path: string): void {\n if (!isPlainObject(value)) {\n fail(path, `expected an object, got ${describe(value)}`);\n }\n for (const [key, entry] of Object.entries(value)) {\n requireNumber(entry, `${path}.${key}`);\n }\n}\n\n/**\n * Validate a usage object (node-level or graph-level).\n *\n * This is the whole point of the boundary: types alone are a compile-time\n * fiction, so a malformed `usage` from a stale or hostile spec would otherwise\n * flow through as trusted data and be rendered as a dollar figure. In\n * particular `cost` MUST come out as `number | null` and nothing else — a string\n * cost, or an absent one on calls that were rated, silently becoming \"unrated\"\n * would understate what a run spent. The counts stay required: an absent count\n * is not a relayed `null`, since the producer never writes one.\n */\nfunction validateUsage(usage: unknown, path: string): void {\n if (!isPlainObject(usage)) {\n fail(path, `expected an object, got ${describe(usage)}`);\n }\n requireNumber(usage.inference_calls, `${path}.inference_calls`);\n const ratedCalls = requireNumber(usage.rated_inference_calls, `${path}.rated_inference_calls`);\n requireNumberRecord(usage.nb_tokens_by_category, `${path}.nb_tokens_by_category`);\n requireNumber(usage.total_tokens, `${path}.total_tokens`);\n usage.cost = normalizeCost(usage.cost, ratedCalls, `${path}.cost`);\n usage.cost_input = normalizeNullableNumber(usage.cost_input, `${path}.cost_input`);\n usage.cost_output = normalizeNullableNumber(usage.cost_output, `${path}.cost_output`);\n requireNumber(usage.subtree_inference_calls, `${path}.subtree_inference_calls`);\n const subtreeRatedCalls = requireNumber(\n usage.subtree_rated_inference_calls,\n `${path}.subtree_rated_inference_calls`,\n );\n requireNumberRecord(usage.subtree_nb_tokens_by_category, `${path}.subtree_nb_tokens_by_category`);\n requireNumber(usage.subtree_total_tokens, `${path}.subtree_total_tokens`);\n usage.subtree_cost = normalizeCost(usage.subtree_cost, subtreeRatedCalls, `${path}.subtree_cost`);\n usage.subtree_cost_input = normalizeNullableNumber(\n usage.subtree_cost_input,\n `${path}.subtree_cost_input`,\n );\n usage.subtree_cost_output = normalizeNullableNumber(\n usage.subtree_cost_output,\n `${path}.subtree_cost_output`,\n );\n // Absent is tolerated and normalized to `[]` (mirroring the `io` treatment\n // below): a spec produced before per-model attribution existed is legitimately\n // missing it, and an absent list is unambiguous — no per-model data — unlike a\n // malformed one, which is still rejected.\n usage.by_model = normalizeModelUsageList(usage.by_model, `${path}.by_model`);\n usage.subtree_by_model = normalizeModelUsageList(\n usage.subtree_by_model,\n `${path}.subtree_by_model`,\n );\n}\n\n/**\n * Validate a per-model usage list, normalizing an absent one to `[]`.\n *\n * The model names here are displayed as fact (\"this is what ran\"), so a malformed\n * entry must fail loudly rather than render as an empty or partial attribution.\n * Absence is not malformation, though: specs generated before per-model\n * attribution existed simply have no such key.\n */\nfunction normalizeModelUsageList(value: unknown, path: string): unknown[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) {\n fail(path, `expected an array when present, got ${describe(value)}`);\n }\n value.forEach((entry, index) => {\n const entryPath = `${path}[${index}]`;\n if (!isPlainObject(entry)) {\n fail(entryPath, `expected an object, got ${describe(entry)}`);\n }\n requireNonEmptyString(entry.inference_model_name, `${entryPath}.inference_model_name`);\n requireNonEmptyString(entry.inference_model_id, `${entryPath}.inference_model_id`);\n // Absent on specs predating the field. Normalized to a value that is not \"llm\",\n // so token counts stay hidden rather than being shown on unverified grounds.\n if (entry.model_type === undefined) {\n entry.model_type = \"unknown\";\n } else {\n requireNonEmptyString(entry.model_type, `${entryPath}.model_type`);\n }\n requireNumber(entry.inference_calls, `${entryPath}.inference_calls`);\n const ratedCalls = requireNumber(\n entry.rated_inference_calls,\n `${entryPath}.rated_inference_calls`,\n );\n entry.cost = normalizeCost(entry.cost, ratedCalls, `${entryPath}.cost`);\n });\n return value;\n}\n\nfunction validateNode(node: unknown, path: string): void {\n if (!isPlainObject(node)) {\n fail(path, `expected an object, got ${describe(node)}`);\n }\n\n requireNonEmptyString(node.id, `${path}.id`);\n\n // A real pipelex run serializes two families of node: pipe-call nodes\n // (`kind` \"controller\" or \"operator\") and the binding nodes of a sequence's\n // binding steps (`kind` \"binding\"). The other `NodeKind` values exist in the\n // enum only for the mermaid renderer and never reach a graphspec.json — a\n // spec carrying one is malformed or wrong-version, so reject it.\n if (!DRAWN_NODE_KINDS.has(node.kind as string)) {\n fail(\n `${path}.kind`,\n `expected \"controller\", \"operator\" or \"binding\" — a real pipelex run emits only ` +\n `pipe-call and binding nodes, got ${JSON.stringify(node.kind)}`,\n );\n }\n\n if (typeof node.status !== \"string\" || !PIPE_STATUSES.has(node.status)) {\n fail(\n `${path}.status`,\n `expected one of ${[...PIPE_STATUSES].join(\", \")}, got ${JSON.stringify(node.status)}`,\n );\n }\n\n // A binding node's `pipe_code` is its step's `from` path, never empty.\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n const pipeType = requireNonEmptyString(node.pipe_type, `${path}.pipe_type`);\n if (node.kind === \"binding\") {\n // A binding step runs no pipe, so its class is the step's own and nothing else.\n if (pipeType !== BINDING_STEP_TYPE) {\n fail(\n `${path}.pipe_type`,\n `expected \"${BINDING_STEP_TYPE}\" on a binding node, got ${JSON.stringify(pipeType)}`,\n );\n }\n } else if (!KNOWN_PIPE_TYPES.has(pipeType)) {\n fail(\n `${path}.pipe_type`,\n `unrecognized pipe class \"${pipeType}\" — add it to PipeOperatorType ` +\n `or PipeControllerType in types.ts`,\n );\n }\n requireNonEmptyString(node.description, `${path}.description`);\n requireNonEmptyString(node.domain_code, `${path}.domain_code`);\n\n // `io` is always emitted by pipelex; tolerate an absent key (the pipelex\n // model defaults it) by normalizing to an empty shape.\n let io = node.io;\n if (io === undefined) {\n io = { inputs: [], outputs: [] };\n node.io = io;\n }\n if (!isPlainObject(io)) {\n fail(`${path}.io`, `expected an object, got ${describe(io)}`);\n }\n if (io.inputs === undefined) {\n io.inputs = [];\n } else if (!Array.isArray(io.inputs)) {\n fail(`${path}.io.inputs`, `expected an array, got ${describe(io.inputs)}`);\n }\n if (io.outputs === undefined) {\n io.outputs = [];\n } else if (!Array.isArray(io.outputs)) {\n fail(`${path}.io.outputs`, `expected an array, got ${describe(io.outputs)}`);\n }\n (io.inputs as unknown[]).forEach((item, j) => validateIoItem(item, `${path}.io.inputs[${j}]`));\n (io.outputs as unknown[]).forEach((item, j) => validateIoItem(item, `${path}.io.outputs[${j}]`));\n\n // `usage` is legitimately ABSENT OR NULL: pydantic serializes `usage=None` as an\n // explicit `\"usage\": null` rather than omitting the key, and that is the normal\n // shape for a run that collected no usage. Both mean the same thing, so null is\n // normalized away here and downstream code only ever sees an object or undefined.\n if (node.usage === null) {\n delete node.usage;\n } else if (node.usage !== undefined) {\n validateUsage(node.usage, `${path}.usage`);\n }\n}\n\nfunction validateEdge(edge: unknown, path: string): void {\n if (!isPlainObject(edge)) {\n fail(path, `expected an object, got ${describe(edge)}`);\n }\n requireNonEmptyString(edge.id, `${path}.id`);\n requireNonEmptyString(edge.source, `${path}.source`);\n requireNonEmptyString(edge.target, `${path}.target`);\n if (typeof edge.kind !== \"string\" || !EDGE_KINDS.has(edge.kind)) {\n fail(\n `${path}.kind`,\n `expected one of ${[...EDGE_KINDS].join(\", \")}, got ${JSON.stringify(edge.kind)}`,\n );\n }\n}\n\n/**\n * Validate a raw, untyped value as a `GraphSpec`. Throws\n * `GraphSpecValidationError` on the first violation.\n *\n * **Mutates the input in place:** on success it returns the *same* object\n * (typed as `GraphSpec`), with its normalizations written directly onto the\n * passed-in object: where a node has no `io` key it writes\n * `io = { inputs: [], outputs: [] }` (and fills absent `io.inputs` /\n * `io.outputs` arrays), and an absent unrated cost is written as `null`, so\n * code downstream never meets `undefined` where the types promise `null`. Pass a fresh\n * parse result (e.g. straight from `JSON.parse`) — do not pass a frozen or\n * externally shared object.\n */\nexport function validateGraphSpec(raw: unknown): GraphSpec {\n if (!isPlainObject(raw)) {\n fail(\"\", `expected a GraphSpec object, got ${describe(raw)}`);\n }\n\n if (!Array.isArray(raw.nodes)) {\n fail(\"nodes\", `expected an array, got ${describe(raw.nodes)}`);\n }\n if (!Array.isArray(raw.edges)) {\n fail(\"edges\", `expected an array, got ${describe(raw.edges)}`);\n }\n\n if (!isPlainObject(raw.meta)) {\n fail(\"meta\", `expected an object, got ${describe(raw.meta)}`);\n }\n if (raw.meta.format !== \"mthds\") {\n fail(\n \"meta.format\",\n `expected \"mthds\" (this does not look like pipelex GraphSpec JSON), ` +\n `got ${JSON.stringify(raw.meta.format)}`,\n );\n }\n if (\n raw.meta.mode !== undefined &&\n (typeof raw.meta.mode !== \"string\" || !GRAPH_SPEC_MODES.has(raw.meta.mode))\n ) {\n fail(\n \"meta.mode\",\n `expected one of ${[...GRAPH_SPEC_MODES].join(\", \")} when present, ` +\n `got ${JSON.stringify(raw.meta.mode)}`,\n );\n }\n\n // `pipe_registry` / `concept_registry` are gated by the pipelex\n // `data_inclusion` flag — legitimately absent, and minimally `{}`.\n if (raw.pipe_registry !== undefined && !isPlainObject(raw.pipe_registry)) {\n fail(\"pipe_registry\", `expected an object when present, got ${describe(raw.pipe_registry)}`);\n }\n if (raw.concept_registry !== undefined && !isPlainObject(raw.concept_registry)) {\n fail(\n \"concept_registry\",\n `expected an object when present, got ${describe(raw.concept_registry)}`,\n );\n }\n\n // Same all-or-nothing rule as the node field, and the same null-vs-absent point:\n // pydantic emits `\"usage\": null` for a run that collected none.\n if (raw.usage === null) {\n delete raw.usage;\n } else if (raw.usage !== undefined) {\n if (!isPlainObject(raw.usage)) {\n fail(\"usage\", `expected an object when present, got ${describe(raw.usage)}`);\n }\n validateUsage(raw.usage.total, \"usage.total\");\n validateUsage(raw.usage.unattributed, \"usage.unattributed\");\n }\n\n raw.nodes.forEach((node, i) => validateNode(node, `nodes[${i}]`));\n raw.edges.forEach((edge, i) => validateEdge(edge, `edges[${i}]`));\n\n return raw as unknown as GraphSpec;\n}\n\n/**\n * Narrow a `GraphSpecNode` to a `PipeCallNode` at an internal trust boundary,\n * throwing `GraphSpecValidationError` when the node is not a well-formed\n * pipe-call node.\n *\n * Graph-topology analysis (`buildGraph` and friends) identifies a node as a\n * pipe by its edges, not by re-checking `kind`/`pipe_code`. When the spec has\n * been through `validateGraphSpec` this always holds — but library consumers\n * may call `buildGraph` directly. This guard turns that gap into a loud,\n * greppable failure instead of a bare `TypeError` on a missing `pipe_code`.\n */\nexport function asPipeCallNode(node: GraphSpecNode, path = \"node\"): PipeCallNode {\n if (node.kind !== \"controller\" && node.kind !== \"operator\") {\n fail(\n `${path}.kind`,\n `expected a pipe-call node (\"controller\" or \"operator\"), ` +\n `got ${JSON.stringify(node.kind)}`,\n );\n }\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n return node as PipeCallNode;\n}\n\n/**\n * Narrow a `GraphSpecNode` to a `CardNode` — a pipe-call node or a binding\n * node, the two families the graph draws as a card — throwing\n * `GraphSpecValidationError` when the node is neither.\n *\n * The dataflow graph draws every node that produces or consumes a stuff, which\n * a binding node does as much as an operator: it reads its root and produces\n * what it binds. Same boundary contract as `asPipeCallNode`.\n */\nexport function asCardNode(node: GraphSpecNode, path = \"node\"): CardNode {\n if (node.kind === \"binding\") {\n requireNonEmptyString(node.pipe_code, `${path}.pipe_code`);\n if (node.pipe_type !== BINDING_STEP_TYPE) {\n fail(\n `${path}.pipe_type`,\n `expected \"${BINDING_STEP_TYPE}\" on a binding node, got ${JSON.stringify(node.pipe_type)}`,\n );\n }\n return node as CardNode;\n }\n return asPipeCallNode(node, path);\n}\n","import type {\n GraphSpec,\n DataflowAnalysis,\n PipeBlueprintUnion,\n ConceptInfo,\n StuffRegistryEntry,\n} from \"./types\";\n\nexport function buildDataflowAnalysis(graphspec: GraphSpec | null): DataflowAnalysis | null {\n if (!graphspec) return null;\n\n const stuffRegistry: Record<string, StuffRegistryEntry> = {};\n const stuffProducers: Record<string, string[]> = {};\n const stuffConsumers: Record<string, string[]> = {};\n const containmentTree: Record<string, string[]> = {};\n const childNodeIds = new Set<string>();\n\n // Build containment tree from edges\n for (const edge of graphspec.edges) {\n if (edge.kind === \"contains\") {\n if (!containmentTree[edge.source]) containmentTree[edge.source] = [];\n containmentTree[edge.source].push(edge.target);\n childNodeIds.add(edge.target);\n }\n }\n\n // Controller IDs are nodes that have children\n const controllerNodeIds = new Set<string>(Object.keys(containmentTree));\n\n // Register stuffs from all nodes; track producers/consumers from operators only\n for (const node of graphspec.nodes) {\n const nodeIo = node.io;\n const isController = controllerNodeIds.has(node.id);\n\n // Register outputs\n for (const output of nodeIo.outputs) {\n if (output.digest && !stuffRegistry[output.digest]) {\n stuffRegistry[output.digest] = {\n name: output.name,\n concept: output.concept,\n multiplicity: output.multiplicity,\n contentType: output.content_type,\n };\n }\n if (output.digest && !isController) {\n if (!stuffProducers[output.digest]) stuffProducers[output.digest] = [];\n stuffProducers[output.digest].push(node.id);\n }\n }\n\n // Register inputs\n for (const input of nodeIo.inputs) {\n if (input.digest && !stuffRegistry[input.digest]) {\n stuffRegistry[input.digest] = {\n name: input.name,\n concept: input.concept,\n multiplicity: input.multiplicity,\n contentType: input.content_type,\n };\n }\n if (input.digest && !isController) {\n if (!stuffConsumers[input.digest]) stuffConsumers[input.digest] = [];\n stuffConsumers[input.digest].push(node.id);\n }\n }\n }\n\n return {\n stuffRegistry,\n stuffProducers,\n stuffConsumers,\n controllerNodeIds,\n childNodeIds,\n containmentTree,\n };\n}\n\n/**\n * Build a map from node id -> controller id for all nodes that belong to a controller.\n * Includes both direct children (operators) and stuff nodes assigned to controllers.\n *\n * Stuff nodes are placed at the lowest controller level where they connect producers\n * to consumers. A stuff node produced inside controller C is promoted to C's parent\n * if none of its consumers are inside C (output flows outward).\n */\nexport function buildChildToControllerMap(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n): Record<string, string> {\n const childToController: Record<string, string> = {};\n\n // Direct children from containment tree\n for (const [ctrlId, children] of Object.entries(analysis.containmentTree)) {\n for (const childId of children) {\n childToController[childId] = ctrlId;\n }\n }\n\n // Stuff nodes produced by operators inside controllers. A stuff with several\n // producers (a condition's output, one per outcome) starts in the deepest\n // controller holding all of them, so no outcome's edge has to leave its group\n // to reach it.\n for (const [digest, producerIds] of Object.entries(analysis.stuffProducers)) {\n const stuffId = \"stuff_\" + digest;\n const ctrlId = commonController(producerIds, childToController);\n if (ctrlId) {\n childToController[stuffId] = ctrlId;\n }\n }\n\n // Stuff produced by controllers themselves -> assign to parent controller\n for (const node of graphspec.nodes) {\n if (!analysis.controllerNodeIds.has(node.id)) continue;\n const parentCtrlId = childToController[node.id];\n if (!parentCtrlId) continue;\n for (const output of node.io.outputs) {\n if (!output.digest) continue;\n const stuffId = \"stuff_\" + output.digest;\n if (!childToController[stuffId]) {\n childToController[stuffId] = parentCtrlId;\n }\n }\n }\n\n // Batch item stuff (fan-out) -> assign to the PipeBatch controller\n for (const edge of graphspec.edges) {\n if (edge.kind === \"batch_item\" && edge.target_stuff_digest) {\n const stuffId = \"stuff_\" + edge.target_stuff_digest;\n // edge.source is the PipeBatch controller node\n if (analysis.controllerNodeIds.has(edge.source)) {\n childToController[stuffId] = edge.source;\n }\n }\n }\n\n // ─── Promote stuff nodes whose consumers are all outside their controller ──\n // Stuff involved in stuff-to-stuff edges (batch/parallel) should not be promoted\n // when they have no operator consumers — they're intermediate batch/parallel data.\n const stuffInStuffEdges = new Set<string>();\n for (const edge of graphspec.edges) {\n if (\n edge.kind === \"batch_item\" ||\n edge.kind === \"batch_aggregate\" ||\n edge.kind === \"parallel_combine\"\n ) {\n if (edge.source_stuff_digest) stuffInStuffEdges.add(edge.source_stuff_digest);\n if (edge.target_stuff_digest) stuffInStuffEdges.add(edge.target_stuff_digest);\n }\n }\n\n const stuffPrefix = \"stuff_\";\n const stuffEntries = Object.keys(childToController).filter((id) => id.startsWith(stuffPrefix));\n\n for (const stuffId of stuffEntries) {\n const digest = stuffId.slice(stuffPrefix.length);\n let assignedCtrl: string | undefined = childToController[stuffId];\n if (!assignedCtrl) continue;\n\n const consumers = analysis.stuffConsumers[digest] || [];\n\n if (consumers.length === 0) {\n // No operator consumers — promote to root only if this is a pure final output\n // (not involved in batch/parallel stuff-to-stuff edges)\n if (!stuffInStuffEdges.has(digest)) {\n delete childToController[stuffId];\n }\n continue;\n }\n\n // Has consumers — promote until we find a level where at least one consumer is inside\n while (assignedCtrl) {\n const ctrl = assignedCtrl;\n const hasConsumerInside = consumers.some((consumerId) =>\n isDescendantOf(consumerId, ctrl, childToController),\n );\n if (hasConsumerInside) break;\n\n const parentCtrl: string | undefined = childToController[assignedCtrl];\n if (parentCtrl) {\n childToController[stuffId] = parentCtrl;\n assignedCtrl = parentCtrl;\n } else {\n delete childToController[stuffId];\n assignedCtrl = undefined;\n }\n }\n }\n\n return childToController;\n}\n\n/** The deepest controller every one of these nodes sits inside, or undefined at the root. */\nfunction commonController(\n nodeIds: readonly string[],\n childToController: Record<string, string>,\n): string | undefined {\n if (nodeIds.length === 0) return undefined;\n let candidate: string | undefined = childToController[nodeIds[0]];\n while (candidate) {\n const ctrl: string = candidate;\n if (nodeIds.every((id) => isDescendantOf(id, ctrl, childToController))) return ctrl;\n candidate = childToController[ctrl];\n }\n return undefined;\n}\n\n/** Check if nodeId is a descendant of ancestorCtrlId in the containment hierarchy. */\nfunction isDescendantOf(\n nodeId: string,\n ancestorCtrlId: string,\n childToController: Record<string, string>,\n): boolean {\n let current = childToController[nodeId];\n while (current) {\n if (current === ancestorCtrlId) return true;\n current = childToController[current];\n }\n return false;\n}\n\n// ─── Registry lookup helpers ───────────────────────────────────────────────\n\nexport function getPipeBlueprint(spec: GraphSpec, pipeRef: string): PipeBlueprintUnion | undefined {\n return spec.pipe_registry?.[pipeRef];\n}\n\nexport function getConceptInfo(spec: GraphSpec, conceptRef: string): ConceptInfo | undefined {\n return spec.concept_registry?.[conceptRef];\n}\n\nexport function resolveConceptRef(spec: GraphSpec, codeOrRef: string): ConceptInfo | undefined {\n if (!spec.concept_registry) return undefined;\n // Direct lookup first (e.g., \"test_domain.Summary\")\n const direct = spec.concept_registry[codeOrRef];\n if (direct) return direct;\n // Search by code (e.g., \"Summary\" matches \"test_domain.Summary\")\n for (const info of Object.values(spec.concept_registry)) {\n if (info.code === codeOrRef) return info;\n }\n return undefined;\n}\n","import type { CardNode, GraphSpecMode, GraphSpecNodeIoItem, PipeCardPayload } from \"./types\";\nimport { multiplicitySuffix } from \"./types\";\n\n/** An io item as a card pill: its name, and its concept with the multiplicity marker (`Document[]`). */\nfunction ioPill(item: GraphSpecNodeIoItem): { name: string; concept: string } {\n return {\n name: item.name,\n concept: item.concept ? item.concept + multiplicitySuffix(item.multiplicity) : \"\",\n };\n}\n\n/**\n * Build a PipeCardPayload from a card node: a pipe-call node, or a binding\n * node, whose card names its `from` path where a pipe card names its pipe code.\n *\n * `validateGraphSpec` guarantees the node's `pipe_code`, `pipe_type`,\n * `description`, `status`, and `io` are present and well-formed, so this\n * function reads them directly with no fallback synthesis.\n */\nexport function buildPipeCardPayload(node: CardNode, graphMode?: GraphSpecMode): PipeCardPayload {\n const payload: PipeCardPayload = {\n pipeCode: node.pipe_code,\n pipeType: node.pipe_type,\n description: node.description,\n status: node.status,\n inputs: node.io.inputs.map(ioPill),\n outputs: node.io.outputs.map(ioPill),\n };\n if (graphMode !== undefined) payload.graphMode = graphMode;\n if (node.tags !== undefined) payload.tags = node.tags;\n return payload;\n}\n","import type { GraphSpec, DataflowAnalysis, GraphNode, GraphEdge, GraphData } from \"./types\";\nimport {\n ARROW_CLOSED_MARKER,\n NODE_TYPE_PIPE_CARD,\n NODE_TYPE_STUFF,\n graphSpecMode,\n isBindingNode,\n multiplicitySuffix,\n stuffNodeId,\n} from \"./types\";\nimport { buildDataflowAnalysis, buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asCardNode } from \"./validateGraphSpec\";\nimport { buildPipeCardPayload } from \"./pipeCardPayload\";\n\nconst STUFF_CHAR_WIDTH_PX = 7;\nconst STUFF_LABEL_PADDING = 48;\nconst MIN_STUFF_WIDTH = 140;\n\n/**\n * Build dataflow graph from GraphSpec. Creates pipe nodes + stuff (data) nodes +\n * producer/consumer edges. Returns label descriptors (not React elements).\n */\nexport function buildDataflowGraph(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n edgeType: string,\n): GraphData {\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n const graphMode = graphSpecMode(graphspec);\n const combinedStuffDigests = new Set<string>();\n for (const edge of graphspec.edges) {\n if (edge.kind === \"parallel_combine\" && edge.target_stuff_digest) {\n combinedStuffDigests.add(edge.target_stuff_digest);\n }\n }\n\n // Find participating pipes (those that produce or consume data)\n const participatingPipes = new Set<string>();\n for (const producers of Object.values(analysis.stuffProducers)) {\n for (const producer of producers) {\n participatingPipes.add(producer);\n }\n }\n for (const consumers of Object.values(analysis.stuffConsumers)) {\n for (const consumer of consumers) {\n participatingPipes.add(consumer);\n }\n }\n\n // Create pipe nodes (only those that participate in data flow). A binding\n // node participates like an operator — it reads its root and produces what it\n // binds — so it is drawn as a card too, which says it binds rather than runs.\n // A binding is drawn even with no data flow: one that failed, or was skipped\n // because its root or its single result was absent, closes with empty IO, and\n // it is still a step of its sequence whose status and error the viewer shows.\n for (const node of graphspec.nodes) {\n if (!participatingPipes.has(node.id) && !isBindingNode(node)) continue;\n // A drawn node is always a pipe-call or binding node; this guard turns a\n // malformed spec into a loud, greppable error rather than a bare TypeError.\n const pipeNode = asCardNode(node, `nodes[${node.id}]`);\n\n const isFailed = pipeNode.status === \"failed\";\n const label = pipeNode.pipe_code;\n const pipeCardData = buildPipeCardPayload(pipeNode, graphMode);\n\n nodes.push({\n id: pipeNode.id,\n type: NODE_TYPE_PIPE_CARD,\n data: {\n labelDescriptor: { kind: \"pipe\", label, isFailed },\n nodeData: pipeNode,\n isPipe: true,\n isStuff: false,\n labelText: label,\n pipeCode: pipeCardData.pipeCode,\n pipeType: pipeNode.pipe_type,\n graphMode,\n pipeCardData,\n },\n position: { x: 0, y: 0 },\n });\n }\n\n // Create stuff (data) nodes\n for (const [digest, stuffInfo] of Object.entries(analysis.stuffRegistry)) {\n const stuffId = stuffNodeId(digest);\n const label = stuffInfo.name;\n // The marked ref (`Document[]`), so a list reads as a list on the canvas.\n const concept = stuffInfo.concept\n ? stuffInfo.concept + multiplicitySuffix(stuffInfo.multiplicity)\n : \"\";\n const textWidth =\n Math.max(label.length, concept.length) * STUFF_CHAR_WIDTH_PX + STUFF_LABEL_PADDING;\n const stuffWidth = Math.max(MIN_STUFF_WIDTH, textWidth);\n\n // Classify: input (no producer), output (no consumer), or intermediate\n const isCombined = combinedStuffDigests.has(digest);\n const isInput = !analysis.stuffProducers[digest]?.length && !isCombined;\n const isOutput = !isInput && !isCombined && !analysis.stuffConsumers[digest]?.length;\n const stuffRole = isCombined\n ? (\"combined\" as const)\n : isInput\n ? (\"input\" as const)\n : isOutput\n ? (\"output\" as const)\n : undefined;\n\n const borderColor = isInput\n ? \"var(--color-stuff-input-border, #50FA7B)\"\n : isOutput\n ? \"var(--color-stuff-output-border, #a78bfa)\"\n : isCombined\n ? \"var(--color-parallel-combine)\"\n : \"var(--color-stuff-border)\";\n\n nodes.push({\n id: stuffId,\n type: NODE_TYPE_STUFF,\n data: {\n labelDescriptor: { kind: \"stuff\", label, concept },\n isStuff: true,\n isPipe: false,\n labelText: label,\n stuffRole,\n stuffDigest: digest,\n },\n position: { x: 0, y: 0 },\n style: {\n background: \"var(--color-stuff-bg)\",\n border: `2px solid ${borderColor}`,\n borderRadius: \"999px\",\n padding: \"0\",\n width: stuffWidth + \"px\",\n boxShadow: \"var(--shadow-md)\",\n },\n });\n }\n\n // Create edges: producer -> stuff\n let edgeId = 0;\n for (const [digest, producers] of Object.entries(analysis.stuffProducers)) {\n const stuffId = stuffNodeId(digest);\n for (const producerNodeId of producers) {\n edges.push({\n id: \"edge_\" + edgeId++,\n source: producerNodeId,\n target: stuffId,\n type: edgeType,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 2 },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-edge)\",\n },\n });\n }\n }\n\n // Create edges: stuff -> consumer\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n const stuffId = stuffNodeId(digest);\n for (const consumerNodeId of consumers) {\n edges.push({\n id: \"edge_\" + edgeId++,\n source: stuffId,\n target: consumerNodeId,\n type: edgeType,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 2 },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-edge)\",\n },\n });\n }\n }\n\n // Create PARALLEL_COMBINE edges from GraphSpec\n for (const edge of graphspec.edges) {\n if (edge.kind !== \"parallel_combine\") continue;\n if (!edge.source_stuff_digest || !edge.target_stuff_digest) continue;\n if (\n !analysis.stuffRegistry[edge.source_stuff_digest] ||\n !analysis.stuffRegistry[edge.target_stuff_digest]\n )\n continue;\n const sourceId = stuffNodeId(edge.source_stuff_digest);\n const targetId = stuffNodeId(edge.target_stuff_digest);\n\n edges.push({\n id: edge.id,\n source: sourceId,\n target: targetId,\n type: \"smoothstep\",\n animated: false,\n style: {\n stroke: \"var(--color-parallel-combine)\",\n strokeWidth: 2,\n strokeDasharray: \"5,5\",\n },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: \"var(--color-parallel-combine)\",\n },\n });\n }\n\n // Create BATCH_ITEM and BATCH_AGGREGATE edges (data-centric mode: stuff -> stuff)\n for (const edge of graphspec.edges) {\n if (edge.kind !== \"batch_item\" && edge.kind !== \"batch_aggregate\") continue;\n\n if (!edge.source_stuff_digest || !edge.target_stuff_digest) continue;\n if (\n !analysis.stuffRegistry[edge.source_stuff_digest] ||\n !analysis.stuffRegistry[edge.target_stuff_digest]\n )\n continue;\n const sourceId = stuffNodeId(edge.source_stuff_digest);\n const targetId = stuffNodeId(edge.target_stuff_digest);\n const isBatchItem = edge.kind === \"batch_item\";\n\n edges.push({\n id: edge.id,\n source: sourceId,\n target: targetId,\n type: edgeType,\n animated: false,\n _batchEdge: true,\n label: edge.label || \"\",\n labelStyle: {\n fontSize: \"10px\",\n fontFamily: \"var(--font-mono)\",\n fill: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n },\n labelBgStyle: { fill: \"var(--color-bg)\", fillOpacity: 0.9 },\n style: {\n stroke: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n strokeWidth: 2,\n strokeDasharray: \"5,5\",\n },\n markerEnd: {\n type: ARROW_CLOSED_MARKER,\n color: isBatchItem ? \"var(--color-batch-item)\" : \"var(--color-batch-aggregate)\",\n },\n });\n }\n\n // Mark edges that cross between different sibling controller groups\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n // and assign per-class edge types for better routing\n for (const edge of edges) {\n const srcCtrl = childToCtrl[edge.source] || null;\n const tgtCtrl = childToCtrl[edge.target] || null;\n if (srcCtrl && tgtCtrl && srcCtrl !== tgtCtrl) {\n edge._crossGroup = true;\n // Keep bezier for long-distance cross-group edges (natural curves look better)\n // but visually de-emphasize to reduce spaghetti effect\n edge.style = {\n ...edge.style,\n strokeWidth: 1.5,\n opacity: 0.65,\n };\n }\n }\n\n // Batch edges: keep bezier but visually differentiate\n for (const edge of edges) {\n if (edge._batchEdge) {\n edge.style = {\n ...edge.style,\n opacity: 0.7,\n };\n }\n }\n\n return { nodes, edges };\n}\n\n/**\n * Build graph from GraphSpec using dataflow mode.\n * Returns the built graph data and analysis.\n */\nexport function buildGraph(\n graphspec: GraphSpec | null,\n edgeType: string,\n): { graphData: GraphData; analysis: DataflowAnalysis | null } {\n if (graphspec) {\n const analysis = buildDataflowAnalysis(graphspec);\n // A graph with no data flow at all is still drawn when it holds a binding\n // node, which `buildDataflowGraph` draws whatever its IO.\n if (\n analysis &&\n (Object.keys(analysis.stuffProducers).length > 0 ||\n Object.keys(analysis.stuffConsumers).length > 0 ||\n graphspec.nodes.some(isBindingNode))\n ) {\n return { graphData: buildDataflowGraph(graphspec, analysis, edgeType), analysis };\n }\n }\n return { graphData: { nodes: [], edges: [] }, analysis: null };\n}\n","import type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphData,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphSpecNode,\n} from \"./types\";\nimport {\n ARROW_CLOSED_MARKER,\n NODE_TYPE_PIPE_CARD,\n graphSpecMode,\n isStuffNodeId,\n stuffDigestFromId,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asPipeCallNode } from \"./validateGraphSpec\";\nimport { buildPipeCardPayload } from \"./pipeCardPayload\";\n\n/**\n * Find every controller that shares the same `pipe_code` as `controllerId` —\n * the \"cousins\" of the clicked controller (other instances of the same pipe,\n * possibly living in different branches of the graph).\n *\n * The result always includes `controllerId` itself. If the controller has no\n * `pipe_code` or no cousins exist, returns a singleton set.\n *\n * Used by GraphViewer to mirror fold/expand actions across all instances of a\n * pipe — the default behavior — while alt/option-click bypasses cousin lookup\n * and toggles only the clicked controller.\n */\nexport function findCousinControllers(\n controllerId: string,\n graphspec: GraphSpec,\n controllerNodeIds: ReadonlySet<string>,\n): Set<string> {\n const result = new Set<string>([controllerId]);\n const clicked = graphspec.nodes.find((n) => n.id === controllerId);\n const pipeCode = clicked?.pipe_code;\n if (!pipeCode) return result;\n for (const node of graphspec.nodes) {\n if (node.pipe_code !== pipeCode) continue;\n if (!controllerNodeIds.has(node.id)) continue;\n result.add(node.id);\n }\n return result;\n}\n\n/**\n * Walk the containment chain from `nodeId` upward, returning the ordered list of\n * ancestor controller IDs from immediate parent → root.\n */\nexport function buildContainmentChain(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n): string[] {\n const chain: string[] = [];\n let current: string | undefined = childToCtrl[nodeId];\n const seen = new Set<string>();\n while (current && !seen.has(current)) {\n chain.push(current);\n seen.add(current);\n current = childToCtrl[current];\n }\n return chain;\n}\n\n/**\n * Find the **outermost** (closest to root) ancestor of `nodeId` whose ID is in\n * `foldedSet`. Returns `null` if no ancestor is folded.\n */\nexport function outermostFoldedAncestor(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n): string | null {\n const chain = buildContainmentChain(nodeId, childToCtrl);\n let outermost: string | null = null;\n for (const ancestorId of chain) {\n if (foldedSet.has(ancestorId)) outermost = ancestorId;\n }\n return outermost;\n}\n\n/**\n * Effective ID after folding: returns the outermost folded ancestor if one\n * exists; otherwise the node itself.\n */\nfunction effectiveId(\n nodeId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n): string {\n return outermostFoldedAncestor(nodeId, childToCtrl, foldedSet) ?? nodeId;\n}\n\nfunction findSpecNode(graphspec: GraphSpec, id: string): GraphSpecNode | undefined {\n return graphspec.nodes.find((n) => n.id === id);\n}\n\n/**\n * Apply the fold transformation to a dataflow graph.\n *\n * For each controller in `foldedSet` that is not itself inside another folded\n * controller (outermost-wins): emit a single `pipe-card` node carrying the\n * controller's payload, and rewrite/dedup edges so the controller card replaces\n * its hidden descendants.\n *\n * The function is pure — inputs are not mutated; a fresh `{ nodes, edges,\n * analysis }` is returned.\n */\nexport function applyFolds(\n graphData: GraphData,\n analysis: DataflowAnalysis,\n graphspec: GraphSpec,\n foldedSet: ReadonlySet<string>,\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void,\n): { nodes: GraphNode[]; edges: GraphEdge[]; analysis: DataflowAnalysis } {\n if (foldedSet.size === 0) {\n return { nodes: graphData.nodes, edges: graphData.edges, analysis };\n }\n\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n const graphMode = graphSpecMode(graphspec);\n\n // ─── Promote declared-output stuffs out of folded controllers ───────────\n // A folded controller's pipe-card represents the controller as a whole, and\n // its declared outputs are external data flowing out of it. If a stuff node\n // currently lives inside a folded controller that declares it as an output,\n // moving the stuff outside lets the folded card connect to it just like\n // before the fold. Without this, the stuff would be hidden alongside the\n // controller's internals, and the output edge would collapse to a self-loop.\n const outputDeclarers: Map<string, Set<string>> = new Map();\n for (const controllerId of foldedSet) {\n const ctrlNode = findSpecNode(graphspec, controllerId);\n const outputs = ctrlNode?.io?.outputs;\n if (!outputs) continue;\n for (const output of outputs) {\n if (!output.digest) continue;\n let set = outputDeclarers.get(output.digest);\n if (!set) {\n set = new Set();\n outputDeclarers.set(output.digest, set);\n }\n set.add(controllerId);\n }\n }\n\n if (outputDeclarers.size > 0) {\n for (const node of graphData.nodes) {\n if (!isStuffNodeId(node.id)) continue;\n const declarers = outputDeclarers.get(stuffDigestFromId(node.id));\n if (!declarers) continue;\n\n // Walk the chain to find the outermost folded ancestor that declares this\n // stuff as one of its outputs. Reparenting to that ancestor's parent\n // (which may itself be folded or root) ensures the stuff escapes the fold.\n let current: string | undefined = childToCtrl[node.id];\n let outermostDeclarer: string | null = null;\n const seen = new Set<string>();\n while (current && !seen.has(current)) {\n seen.add(current);\n if (declarers.has(current)) outermostDeclarer = current;\n current = childToCtrl[current];\n }\n if (!outermostDeclarer) continue;\n\n const newParent = childToCtrl[outermostDeclarer];\n if (newParent) {\n childToCtrl[node.id] = newParent;\n } else {\n delete childToCtrl[node.id];\n }\n }\n }\n\n // ─── Filter visible nodes ───────────────────────────────────────────────\n // Drop any node whose outermost folded ancestor is non-null (it's hidden\n // inside a folded controller).\n const visibleNodes: GraphNode[] = [];\n for (const node of graphData.nodes) {\n if (outermostFoldedAncestor(node.id, childToCtrl, foldedSet)) continue;\n visibleNodes.push(node);\n }\n\n // ─── Emit pipe-card nodes for outermost-folded controllers ──────────────\n for (const folded of foldedSet) {\n // Skip if this folded controller is itself inside another folded ancestor.\n if (outermostFoldedAncestor(folded, childToCtrl, foldedSet)) continue;\n const specNode = findSpecNode(graphspec, folded);\n if (!specNode) continue; // unknown ID — silently ignore\n\n // A folded controller is a pipe-call node; guard the narrowing so a\n // malformed spec fails loudly instead of producing an undefined pipeCode.\n const payload = buildPipeCardPayload(asPipeCallNode(specNode, folded), graphMode);\n if (onToggleFold) {\n payload.onExpand = (options?: FoldToggleOptions) => onToggleFold(folded, options);\n }\n\n const cardNode: GraphNode = {\n id: folded,\n type: NODE_TYPE_PIPE_CARD,\n data: {\n labelDescriptor: {\n kind: \"pipe\",\n label: payload.pipeCode,\n isFailed: payload.status === \"failed\",\n },\n nodeData: specNode,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: payload.pipeCode,\n pipeCode: payload.pipeCode,\n pipeType: specNode.pipe_type,\n graphMode,\n pipeCardData: payload,\n },\n position: { x: 0, y: 0 },\n };\n visibleNodes.push(cardNode);\n }\n\n // ─── Build updated analysis ─────────────────────────────────────────────\n // Drop folded controllers and any controller that lives inside a folded\n // outermost ancestor.\n const updatedControllerIds = new Set<string>();\n for (const ctrlId of analysis.controllerNodeIds) {\n if (foldedSet.has(ctrlId)) continue;\n if (outermostFoldedAncestor(ctrlId, childToCtrl, foldedSet)) continue;\n updatedControllerIds.add(ctrlId);\n }\n\n // containmentTree: only keep entries for surviving controllers; filter their\n // children to drop hidden ones (children whose outermost folded ancestor is\n // a different controller). Children that are the folded controllers themselves\n // should remain as children (they now render as pipe-card leaves).\n const updatedContainmentTree: Record<string, string[]> = {};\n for (const ctrlId of updatedControllerIds) {\n const originalChildren = analysis.containmentTree[ctrlId] ?? [];\n const survivors: string[] = [];\n for (const childId of originalChildren) {\n const outer = outermostFoldedAncestor(childId, childToCtrl, foldedSet);\n // If the child has a folded outermost ancestor that is NOT itself, drop.\n // If the child IS the folded outermost (childId is in foldedSet), keep —\n // it will appear as a pipe-card child of this controller.\n if (outer && outer !== childId) continue;\n survivors.push(childId);\n }\n updatedContainmentTree[ctrlId] = survivors;\n }\n\n // childNodeIds: rebuild from the updated containment tree.\n const updatedChildNodeIds = new Set<string>();\n for (const children of Object.values(updatedContainmentTree)) {\n for (const child of children) updatedChildNodeIds.add(child);\n }\n\n // Rewrite stuffProducers/stuffConsumers so any reference to an operator hidden\n // by a fold is replaced with its outermost folded ancestor — mirroring the\n // edge-endpoint rewrite below. Without this, buildChildToControllerMap's\n // promotion loop loses the consumer/producer trail and stuff nodes get\n // ejected to the root level when their parent controller's sibling is folded.\n // Deduplicated like the consumers: the outcomes of a folded condition all\n // produce its one output, and the folded card is that output's one producer.\n const updatedStuffProducers: Record<string, string[]> = {};\n for (const [digest, producers] of Object.entries(analysis.stuffProducers)) {\n const seen = new Set<string>();\n for (const producerId of producers) {\n seen.add(effectiveId(producerId, childToCtrl, foldedSet));\n }\n updatedStuffProducers[digest] = [...seen];\n }\n\n const updatedStuffConsumers: Record<string, string[]> = {};\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n const seen = new Set<string>();\n for (const consumerId of consumers) {\n seen.add(effectiveId(consumerId, childToCtrl, foldedSet));\n }\n updatedStuffConsumers[digest] = [...seen];\n }\n\n const updatedAnalysis: DataflowAnalysis = {\n stuffRegistry: analysis.stuffRegistry,\n stuffProducers: updatedStuffProducers,\n stuffConsumers: updatedStuffConsumers,\n controllerNodeIds: updatedControllerIds,\n childNodeIds: updatedChildNodeIds,\n containmentTree: updatedContainmentTree,\n };\n\n // ─── Rewrite edges ──────────────────────────────────────────────────────\n // Each surviving endpoint is replaced by its effective (outermost-folded)\n // ancestor or itself. Drop self-loops. Dedup by (newSrc, newDst, bucket).\n const dedupMap = new Map<string, GraphEdge>();\n for (const edge of graphData.edges) {\n const newSrc = effectiveId(edge.source, childToCtrl, foldedSet);\n const newDst = effectiveId(edge.target, childToCtrl, foldedSet);\n if (newSrc === newDst) continue;\n const bucket = edge._batchEdge ? \"batch\" : \"data\";\n const key = `${newSrc}->${newDst}|${bucket}`;\n if (dedupMap.has(key)) continue;\n // Clone the edge with rewritten endpoints; drop the stale _crossGroup flag\n // (recomputed below against folded containment).\n const cloned: GraphEdge = {\n ...edge,\n source: newSrc,\n target: newDst,\n id: edge.id,\n };\n // Batch edges carry per-item indices like \"[0]\", \"[1]\" in their labels.\n // Once a fold collapses many of these into a single edge (or hides the\n // per-item target), the surviving index is misleading — generalize to \"[N]\".\n if (edge._batchEdge && (newSrc !== edge.source || newDst !== edge.target)) {\n cloned.label = \"[N]\";\n }\n delete cloned._crossGroup;\n dedupMap.set(key, cloned);\n }\n\n // Recompute _crossGroup against the folded containment. Build a fresh\n // childToCtrl from the updated analysis so the classification reflects the\n // post-fold node graph (folded controllers no longer have children).\n const foldedChildToCtrl = buildChildToControllerMap(\n {\n ...graphspec,\n // Mask out the contains edges for folded outermost controllers — they no\n // longer logically contain anything after folding.\n edges: graphspec.edges.filter((e) => {\n if (e.kind !== \"contains\") return true;\n return updatedControllerIds.has(e.source);\n }),\n },\n updatedAnalysis,\n );\n\n const rewrittenEdges: GraphEdge[] = [];\n for (const edge of dedupMap.values()) {\n const srcCtrl = foldedChildToCtrl[edge.source] || null;\n const tgtCtrl = foldedChildToCtrl[edge.target] || null;\n if (srcCtrl && tgtCtrl && srcCtrl !== tgtCtrl) {\n edge._crossGroup = true;\n edge.style = {\n ...edge.style,\n strokeWidth: 1.5,\n opacity: 0.65,\n };\n } else if (edge.style && (edge.style.opacity === 0.65 || edge.style.strokeWidth === 1.5)) {\n // Edge no longer crosses sibling groups — reset the de-emphasized style.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { opacity: _opacity, strokeWidth: _strokeWidth, ...rest } = edge.style;\n edge.style = { ...rest, strokeWidth: 2 };\n if (!edge.markerEnd) {\n edge.markerEnd = { type: ARROW_CLOSED_MARKER, color: \"var(--color-edge)\" };\n }\n }\n rewrittenEdges.push(edge);\n }\n\n return { nodes: visibleNodes, edges: rewrittenEdges, analysis: updatedAnalysis };\n}\n","// Validation decorations — project targeted ValidationIssues onto rendered nodes.\n//\n// Pure module (no React): GraphViewer derives a per-node decoration map from\n// the same `validationIssues` prop that feeds the validation panel, then stamps\n// it onto node data. Node components render the ring + badge from that stamp.\n\nimport type { GraphNodeData, GraphSpec, NodeValidationSummary, ValidationIssue } from \"./types\";\nimport { outermostFoldedAncestor } from \"./graphFolds\";\nimport { isBindingNode } from \"./types\";\nimport { makePipeRef } from \"./pipeRefs\";\n\n/**\n * Resolve which GraphSpec node ids an issue targets:\n * - `nodeId` → that precise invocation (wins over `pipeRef` when both are set);\n * - `pipeRef` → every spec node invoking that exact `domain_code.pipe_code`\n * pipe (a pipe can appear in several places in the graph). The match is on\n * the qualified ref — never on the bare code — so two domains declaring the\n * same pipe code can never cross-decorate. A node missing either identity\n * field cannot be qualified and is never matched, and neither is a binding\n * node, whose `pipe_code` is the path it binds and names no pipe;\n * - neither → no targets (the issue stays panel-only).\n */\nfunction issueTargetIds(issue: ValidationIssue, graphspec: GraphSpec): string[] {\n if (issue.nodeId) return [issue.nodeId];\n if (issue.pipeRef) {\n return graphspec.nodes\n .filter(\n (n) =>\n !isBindingNode(n) &&\n n.domain_code !== undefined &&\n n.pipe_code !== undefined &&\n makePipeRef(n.domain_code, n.pipe_code) === issue.pipeRef,\n )\n .map((n) => n.id);\n }\n return [];\n}\n\n/**\n * Build the per-node decoration map for the current fold state.\n *\n * Each issue's target ids are remapped through the fold containment\n * (`outermostFoldedAncestor`): an issue on a node hidden inside a folded\n * controller decorates the folded controller's card instead, so folding never\n * hides an error. They then go through the style's `standIns`: an issue on a\n * node the style does not draw decorates the drawn node standing for it, such\n * as a batch item's run in the simple style, drawn once for every item. Targets\n * that don't resolve to a rendered node (e.g. a\n * diagnostic about a pipe that was skipped during the static walk) simply\n * produce no decoration — those issues stay panel-only.\n *\n * Counts are per issue × target invocation: a folded controller containing two\n * invocations of a broken pipe shows 2, matching the sum of the badges that\n * become visible when it is expanded.\n */\nexport function buildValidationDecorations(\n issues: readonly ValidationIssue[] | undefined,\n graphspec: GraphSpec | null,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n standIns?: ReadonlyMap<string, string>,\n): Map<string, NodeValidationSummary> {\n const decorations = new Map<string, NodeValidationSummary>();\n if (!issues || issues.length === 0 || !graphspec) return decorations;\n\n for (const issue of issues) {\n for (const targetId of issueTargetIds(issue, graphspec)) {\n const visibleId = visibleNodeId(targetId, childToCtrl, foldedSet, standIns);\n const existing = decorations.get(visibleId);\n const lines = [issue.message, ...(issue.suggestedFix ? [`Fix: ${issue.suggestedFix}`] : [])];\n if (existing) {\n if (issue.severity === \"error\") existing.severity = \"error\";\n existing.count += 1;\n existing.lines.push(...lines);\n } else {\n decorations.set(visibleId, { severity: issue.severity, count: 1, lines });\n }\n }\n }\n return decorations;\n}\n\n/**\n * The node drawn for a target: its outermost folded ancestor when it is folded\n * away, then whatever the style draws in its place (`StyleProjection.standIns`).\n */\nfunction visibleNodeId(\n targetId: string,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n standIns: ReadonlyMap<string, string> | undefined,\n): string {\n const unfolded = outermostFoldedAncestor(targetId, childToCtrl, foldedSet) ?? targetId;\n return standIns?.get(unfolded) ?? unfolded;\n}\n\n/**\n * Resolve the rendered node a panel row should navigate to: the issue's first\n * target, remapped through the fold containment and the style's stand-ins, and\n * only if that id is actually rendered. `null` for panel-only issues (no\n * target, or the target never became a node).\n */\nexport function resolveIssueTargetNodeId(\n issue: ValidationIssue,\n graphspec: GraphSpec | null,\n childToCtrl: Readonly<Record<string, string>>,\n foldedSet: ReadonlySet<string>,\n renderedNodeIds: ReadonlySet<string>,\n standIns?: ReadonlyMap<string, string>,\n): string | null {\n if (!graphspec) return null;\n for (const targetId of issueTargetIds(issue, graphspec)) {\n const visibleId = visibleNodeId(targetId, childToCtrl, foldedSet, standIns);\n if (renderedNodeIds.has(visibleId)) return visibleId;\n }\n return null;\n}\n\n/**\n * Stamp the decoration map onto rendered nodes (and their pipe-card payloads).\n * Nodes whose decoration is unchanged are returned as-is; a node whose issues\n * disappeared gets its stamp cleared, so verdict flips are fully reactive.\n * `onBadgeClick` (when provided) rides along on decorated nodes only — the\n * badge renders as a button that opens the validation panel.\n */\nexport function applyValidationDecorations<T extends { id: string; data: GraphNodeData }>(\n nodes: T[],\n decorations: ReadonlyMap<string, NodeValidationSummary>,\n onBadgeClick?: () => void,\n): T[] {\n return nodes.map((node) => {\n const decoration = decorations.get(node.id);\n if (!decoration && !node.data.validation && !node.data.pipeCardData?.validation) return node;\n const badgeClick = decoration ? onBadgeClick : undefined;\n return {\n ...node,\n data: {\n ...node.data,\n validation: decoration,\n onValidationBadgeClick: badgeClick,\n pipeCardData: node.data.pipeCardData\n ? {\n ...node.data.pipeCardData,\n validation: decoration,\n onValidationBadgeClick: badgeClick,\n }\n : node.data.pipeCardData,\n },\n };\n });\n}\n","import { makePipeRef } from \"./pipeRefs\";\nimport type { GraphSpec, GraphSpecNode, GraphSpecNodeIoItem } from \"./types\";\nimport { isBindingNode } from \"./types\";\n\n/**\n * Where a data item lives in a `GraphSpec`, and which pipe put it there.\n *\n * The producing pipe is the half that is not obvious and the half that matters:\n * a stuff carries a concept, but the artifacts that describe how to RENDER it —\n * `output_form` and the output half of `pipe_io_contracts` — are keyed by\n * `pipe_ref`, not by concept. So a panel that wants to show a result as the\n * standard describes it has to walk from the data back to the pipe that\n * resolved to it, and this is that walk, done once at the boundary rather than\n * re-derived by every consumer.\n */\nexport interface StuffLocation {\n /** The item as the spec holds it, richest copy first (see below). */\n item: GraphSpecNodeIoItem;\n /**\n * `domain.code` of the pipe whose OUTPUT this is, when the spec names one.\n *\n * Absent for a pipeline-level input, which no pipe produced, for a value a\n * binding step bound, which no pipe produced either, and for a node missing\n * either half of the ref. A consumer must treat it as optional: a\n * method's own inputs are stuff too, and they are the common case at the top\n * of every graph.\n */\n producerPipeRef?: string;\n /**\n * The first pipe that CONSUMES it, and the slot it arrives in.\n *\n * The fallback identity for a method's own inputs, which no pipe produced and\n * which therefore no `output_form` describes. What does describe them is the\n * consuming pipe's `input_form` entry for that slot — the same field, seen\n * from the other side — so a renderer that holds the input artifacts can show\n * a method input as something better than a JSON dump.\n *\n * First rather than all, deliberately: several pipes may read the same stuff,\n * and every one of them describes it identically, because they are describing\n * the concept it carries. A list here would be a list of the same answer.\n */\n consumer?: { pipeRef: string; slotName: string };\n}\n\n/**\n * `domain.code`, or nothing when the node does not carry both halves, or is a\n * binding node: its `pipe_code` is the path it binds, so `domain.<path>` names\n * no pipe, and a path spelled like a pipe's code would pick up that pipe's\n * descriptors.\n *\n * Assembled with `makePipeRef` rather than a template literal so every consumer\n * agrees with the runtime on what a ref is — the domain path may be\n * multi-segment, and this repo has exactly one place that spelling lives.\n */\nexport function pipeRefOf(node: GraphSpecNode): string | undefined {\n if (isBindingNode(node) || !node.pipe_code || !node.domain_code) return undefined;\n return makePipeRef(node.domain_code, node.pipe_code);\n}\n\n/**\n * Find a data item by digest, preferring the pipe that PRODUCED it.\n *\n * Two passes, and the order is the point rather than an optimization. The same\n * digest appears on the producer's `outputs` and again on every consumer's\n * `inputs`, and only the producer's copy is guaranteed to carry the payload —\n * a consumer's may be a reference with `data` elided. Taking the first match in\n * spec order would therefore hand back an emptier copy of the same stuff\n * depending on how the nodes happened to be sorted.\n *\n * The second pass exists for the items no pipe produced: a method's declared\n * inputs appear only as some pipe's `inputs`, so a digest-first walk that\n * searched outputs alone would report the top of every graph as missing.\n */\nexport function findStuffByDigest(spec: GraphSpec, digest: string): StuffLocation | null {\n const consumer = findConsumer(spec, digest);\n for (const node of spec.nodes) {\n for (const output of node.io?.outputs ?? []) {\n if (output.digest === digest) {\n const producerPipeRef = pipeRefOf(node);\n return {\n item: output,\n ...(producerPipeRef ? { producerPipeRef } : {}),\n ...(consumer ? { consumer } : {}),\n };\n }\n }\n }\n for (const node of spec.nodes) {\n for (const input of node.io?.inputs ?? []) {\n if (input.digest === digest) return { item: input, ...(consumer ? { consumer } : {}) };\n }\n }\n return null;\n}\n\n/** The first pipe reading this digest, and the slot name it reads it as. */\nfunction findConsumer(\n spec: GraphSpec,\n digest: string,\n): { pipeRef: string; slotName: string } | undefined {\n for (const node of spec.nodes) {\n const pipeRef = pipeRefOf(node);\n if (!pipeRef) continue;\n for (const input of node.io?.inputs ?? []) {\n if (input.digest === digest) return { pipeRef, slotName: input.name };\n }\n }\n return undefined;\n}\n","import type { ElkNode, ElkPort, ElkExtendedEdge, LayoutOptions } from \"elkjs/lib/elk-api\";\nimport type {\n GraphNode,\n GraphEdge,\n GraphSpec,\n DataflowAnalysis,\n EdgeRoute,\n GraphPoint,\n LayoutConfig,\n GraphDirection,\n} from \"./types\";\nimport {\n NODE_TYPE_PIPE_CARD,\n CONTROLLER_PADDING_X,\n CONTROLLER_PADDING_TOP,\n CONTROLLER_PADDING_BOTTOM,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\n\n// ─── Direction mapping ──────────────────────────────────────────────────────\n\nfunction elkDirection(direction: GraphDirection): string {\n switch (direction) {\n case \"LR\":\n return \"RIGHT\";\n case \"RL\":\n return \"LEFT\";\n case \"BT\":\n return \"UP\";\n default:\n return \"DOWN\"; // TB\n }\n}\n\n// ─── Port helpers ───────────────────────────────────────────────────────────\n\nconst INPUT_PORT_SUFFIX = \"_in\";\nconst OUTPUT_PORT_SUFFIX = \"_out\";\n\nexport function inputPortId(nodeId: string): string {\n return nodeId + INPUT_PORT_SUFFIX;\n}\n\nexport function outputPortId(nodeId: string): string {\n return nodeId + OUTPUT_PORT_SUFFIX;\n}\n\nfunction makePorts(nodeId: string, dims: NodeDimensions, direction: GraphDirection): ElkPort[] {\n // Port sides must match the flow direction so ELK computes edge attachment\n // on the same side that ReactFlow renders handles.\n // LR: input=WEST, output=EAST | RL: input=EAST, output=WEST\n // TB: input=NORTH, output=SOUTH | BT: input=SOUTH, output=NORTH\n const portSides: Record<GraphDirection, { inSide: string; outSide: string }> = {\n LR: { inSide: \"WEST\", outSide: \"EAST\" },\n RL: { inSide: \"EAST\", outSide: \"WEST\" },\n TB: { inSide: \"NORTH\", outSide: \"SOUTH\" },\n BT: { inSide: \"SOUTH\", outSide: \"NORTH\" },\n };\n const { inSide, outSide } = portSides[direction];\n\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n // Pin ports at the exact center of each side so ELK computes layout\n // with the same edge attachment point that ReactFlow will render.\n const inX = isHorizontal ? (direction === \"LR\" ? 0 : dims.width) : dims.width / 2;\n const inY = isHorizontal ? dims.height / 2 : direction === \"TB\" ? 0 : dims.height;\n const outX = isHorizontal ? (direction === \"LR\" ? dims.width : 0) : dims.width / 2;\n const outY = isHorizontal ? dims.height / 2 : direction === \"TB\" ? dims.height : 0;\n\n return [\n {\n id: inputPortId(nodeId),\n x: inX,\n y: inY,\n width: 1,\n height: 1,\n layoutOptions: { \"elk.port.side\": inSide },\n },\n {\n id: outputPortId(nodeId),\n x: outX,\n y: outY,\n width: 1,\n height: 1,\n layoutOptions: { \"elk.port.side\": outSide },\n },\n ];\n}\n\n// ─── Node dimension estimation ──────────────────────────────────────────────\n// ELK needs dimensions upfront to compute layout.\n\ninterface NodeDimensions {\n width: number;\n height: number;\n}\n\n// ─── Pipe card layout constants (keep in sync with graph-core.css) ──────────\n// If you change these, update the matching rules in graph-core.css.\nconst PIPE_CARD_HEIGHT_CAP = 320;\n\n// Horizontal padding (12 + 12 = 24) + vertical padding (12 + 12 = 24)\nconst PIPE_CARD_PADDING_X = 28; // padding-left + padding-right (14 + 14)\nconst PIPE_CARD_PADDING_Y = 24; // padding-top + padding-bottom (12 + 12)\nconst PIPE_CARD_GAP = 8; // gap between flex children (header / description / io sections)\n\n// Header:\n// - TB: single row with badge + code + status (~22px)\n// - LR: badge/status row + pipe code on its own line below + flex gap (~50px)\nconst PIPE_CARD_HEADER_HEIGHT_TB = 22;\nconst PIPE_CARD_HEADER_HEIGHT_LR = 50;\n\n// Description: 11.5px font × 1.4 line-height ≈ 16.1px per line\nconst PIPE_CARD_DESC_LINE_HEIGHT = 16;\nconst PIPE_CARD_DESC_MAX_LINES_LR = 3;\n\n// I/O section heights (label + first row of pills)\nconst PIPE_CARD_IO_SECTION_HEIGHT_LR = 38; // stacked: label on top, pills below\nconst PIPE_CARD_IO_SECTION_HEIGHT_TB = 30; // inline: label on left, pills on right\nconst PIPE_CARD_IO_EXTRA_ROW_HEIGHT = 22; // each additional wrapping row of pills\n\n// Pill dimension caps (keep in sync with .pipe-card--tb .pipe-card-io-pill-name/concept max-width)\nconst PIPE_CARD_PILL_NAME_MAX_WIDTH = 140;\nconst PIPE_CARD_PILL_CONCEPT_MAX_WIDTH = 100;\nconst PIPE_CARD_PILL_CHROME_WIDTH = 17; // pill padding (10) + name/concept gap (3) + inter-pill gap (4)\nconst PIPE_CARD_IO_LABEL_WIDTH_TB = 58; // min-width 52 + gap 6\n\n// Character width estimates for Inter font\nconst CHAR_WIDTH_DESC = 5.5; // 11.5px font\nconst CHAR_WIDTH_PILL_NAME = 5.0; // 10px font (pill-name)\nconst CHAR_WIDTH_PILL_CONCEPT = 4.5; // 9px font (pill-concept)\n\nconst MAX_VISIBLE_INPUTS = 4;\n\n/** Estimate how many lines the description will wrap to, given direction + text length. */\nfunction estimateDescriptionLines(\n description: string,\n isHorizontal: boolean,\n cardWidth: number,\n): number {\n if (!description) return 0;\n if (!isHorizontal) return 1; // TB always clamps to 1 line (CSS handles ellipsis)\n const textWidth = cardWidth - PIPE_CARD_PADDING_X;\n const charsPerLine = Math.max(1, Math.floor(textWidth / CHAR_WIDTH_DESC));\n const neededLines = Math.ceil(description.length / charsPerLine);\n return Math.min(PIPE_CARD_DESC_MAX_LINES_LR, Math.max(1, neededLines));\n}\n\n/** Estimate the rendered width of a single pill in TB mode. */\nfunction estimateTbPillWidth(name: string, concept: string): number {\n const nameWidth = Math.min(\n PIPE_CARD_PILL_NAME_MAX_WIDTH,\n Math.ceil(name.length * CHAR_WIDTH_PILL_NAME),\n );\n const conceptWidth = Math.min(\n PIPE_CARD_PILL_CONCEPT_MAX_WIDTH,\n Math.ceil(concept.length * CHAR_WIDTH_PILL_CONCEPT),\n );\n return nameWidth + conceptWidth + PIPE_CARD_PILL_CHROME_WIDTH;\n}\n\n/** Count how many wrapping rows a set of pills will occupy in TB mode.\n * Uses a simple first-fit bin-packing against the available pill area width.\n */\nfunction countTbPillRows(\n pills: readonly { name: string; concept: string }[],\n cardWidth: number,\n): number {\n if (pills.length === 0) return 0;\n const availableWidth = cardWidth - PIPE_CARD_PADDING_X - PIPE_CARD_IO_LABEL_WIDTH_TB;\n let rows = 1;\n let currentRowWidth = 0;\n for (const pill of pills) {\n const pillWidth = estimateTbPillWidth(pill.name, pill.concept);\n if (currentRowWidth === 0) {\n currentRowWidth = pillWidth;\n continue;\n }\n if (currentRowWidth + pillWidth <= availableWidth) {\n currentRowWidth += pillWidth;\n } else {\n rows += 1;\n currentRowWidth = pillWidth;\n }\n }\n return rows;\n}\n\nexport function estimateNodeDimensions(node: GraphNode, isHorizontal: boolean): NodeDimensions {\n const nodeData = node.data || {};\n // A producer that knows its node's text metrics fixes the size itself.\n if (nodeData.layoutSize) return { ...nodeData.layoutSize };\n const isStuff = nodeData.isStuff;\n const labelText = nodeData.labelText || \"\";\n const isPipeCard = node.type === NODE_TYPE_PIPE_CARD;\n\n const pipeCardMinWidth = isHorizontal ? 180 : 280;\n const pipeCardMaxWidth = isHorizontal ? 240 : 400;\n // Stuff nodes are visually aligned with pipe cards — they must never be wider\n // than the pipe card max for the current direction, otherwise the graph looks\n // lopsided (a 400px stuff node next to a 240px pipe card in LR mode).\n const estimatedWidth = Math.max(180, Math.min(pipeCardMaxWidth, labelText.length * 8 + 60));\n\n let width: number;\n if (isStuff) {\n width = Math.max(180, estimatedWidth);\n } else if (isPipeCard && nodeData.pipeCardData) {\n width = pipeCardMaxWidth;\n } else {\n width = Math.max(isPipeCard ? pipeCardMinWidth : 200, estimatedWidth);\n }\n\n let height: number;\n if (isStuff) {\n height = 60;\n } else if (isPipeCard && nodeData.pipeCardData) {\n const pcd = nodeData.pipeCardData;\n const inputs = pcd.inputs ?? [];\n const outputs = pcd.outputs ?? [];\n const description = pcd.description || nodeData.nodeData?.description || \"\";\n\n // Header\n let total =\n PIPE_CARD_PADDING_Y +\n (isHorizontal ? PIPE_CARD_HEADER_HEIGHT_LR : PIPE_CARD_HEADER_HEIGHT_TB);\n\n // Description: actual lines needed\n const descLines = estimateDescriptionLines(description, isHorizontal, width);\n if (descLines > 0) {\n total += PIPE_CARD_GAP + descLines * PIPE_CARD_DESC_LINE_HEIGHT;\n }\n\n // Inputs — cap visible to MAX_VISIBLE_INPUTS (rest collapses behind \"+N more\")\n const visibleInputs = inputs.slice(0, MAX_VISIBLE_INPUTS);\n if (visibleInputs.length > 0) {\n total += PIPE_CARD_GAP;\n if (isHorizontal) {\n // LR: one pill per row, always stacked\n total +=\n PIPE_CARD_IO_SECTION_HEIGHT_LR +\n (visibleInputs.length - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n } else {\n // TB: bin-pack pills horizontally, each extra row adds height\n const rows = countTbPillRows(visibleInputs, width);\n total += PIPE_CARD_IO_SECTION_HEIGHT_TB + (rows - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n }\n }\n\n // Outputs — same logic as inputs\n if (outputs.length > 0) {\n total += PIPE_CARD_GAP;\n if (isHorizontal) {\n total +=\n PIPE_CARD_IO_SECTION_HEIGHT_LR + (outputs.length - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n } else {\n const rows = countTbPillRows(outputs, width);\n total += PIPE_CARD_IO_SECTION_HEIGHT_TB + (rows - 1) * PIPE_CARD_IO_EXTRA_ROW_HEIGHT;\n }\n }\n\n height = Math.min(PIPE_CARD_HEIGHT_CAP, total);\n } else {\n height = isPipeCard ? 120 : 70;\n }\n\n return { width, height };\n}\n\n// ─── Controller nesting depth ───────────────────────────────────────────────\n\nfunction computeDepths(\n controllerNodeIds: ReadonlySet<string>,\n containmentTree: Readonly<Record<string, readonly string[]>>,\n): Record<string, number> {\n const depthCache: Record<string, number> = {};\n const visiting = new Set<string>();\n\n function getDepth(ctrlId: string): number {\n if (depthCache[ctrlId] !== undefined) return depthCache[ctrlId];\n if (visiting.has(ctrlId)) return 0;\n visiting.add(ctrlId);\n const children = containmentTree[ctrlId] || [];\n let maxChildDepth = -1;\n for (const childId of children) {\n if (controllerNodeIds.has(childId)) {\n maxChildDepth = Math.max(maxChildDepth, getDepth(childId));\n }\n }\n visiting.delete(ctrlId);\n depthCache[ctrlId] = maxChildDepth + 1;\n return depthCache[ctrlId];\n }\n\n for (const id of controllerNodeIds) getDepth(id);\n return depthCache;\n}\n\n// ─── Leaf node builder ──────────────────────────────────────────────────────\n\nfunction makeLeafNode(nodeId: string, dims: NodeDimensions, direction: GraphDirection): ElkNode {\n return {\n id: nodeId,\n width: dims.width,\n height: dims.height,\n ports: makePorts(nodeId, dims, direction),\n layoutOptions: {\n \"elk.portConstraints\": \"FIXED_POS\",\n },\n };\n}\n\n// ─── Edge builder ───────────────────────────────────────────────────────────\n\n/**\n * An edge from its source's output port to its target's input port. When the\n * layout routes edges, a labelled edge with a known label size carries its\n * label, so the layout reserves room for it.\n */\nfunction makeElkEdge(edge: GraphEdge, routeEdges: boolean): ElkExtendedEdge {\n const elkEdge: ElkExtendedEdge = {\n id: edge.id,\n sources: [outputPortId(edge.source)],\n targets: [inputPortId(edge.target)],\n };\n if (routeEdges && edge.label && edge.labelSize) {\n elkEdge.labels = [\n {\n id: `${edge.id}_label`,\n text: edge.label,\n width: edge.labelSize.width,\n height: edge.labelSize.height,\n },\n ];\n }\n return elkEdge;\n}\n\n// ─── Build ELK graph ────────────────────────────────────────────────────────\n\nexport function buildElkGraph(\n nodes: GraphNode[],\n edges: GraphEdge[],\n graphspec: GraphSpec | null,\n analysis: DataflowAnalysis | null,\n direction: GraphDirection,\n layoutConfig?: LayoutConfig,\n): { elkGraph: ElkNode; dimensionMap: Record<string, NodeDimensions> } {\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n const nodesep = layoutConfig?.nodesep ?? 80;\n const ranksep = layoutConfig?.ranksep ?? 70;\n const routeEdges = layoutConfig?.routeEdges ?? false;\n const groupPadding = layoutConfig?.groupPadding ?? {\n x: CONTROLLER_PADDING_X,\n top: CONTROLLER_PADDING_TOP,\n bottom: CONTROLLER_PADDING_BOTTOM,\n };\n const elkDir = elkDirection(direction);\n\n const edgeNodeSpacing = \"30\";\n\n const rootLayoutOptions: LayoutOptions = {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": elkDir,\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.spacing.nodeNode\": String(nodesep),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(ranksep),\n \"elk.spacing.edgeNode\": edgeNodeSpacing,\n \"elk.spacing.edgeEdge\": \"20\",\n \"elk.layered.spacing.edgeNodeBetweenLayers\": edgeNodeSpacing,\n \"elk.layered.spacing.edgeEdgeBetweenLayers\": \"15\",\n \"elk.layered.nodePlacement.favorStraightEdges\": \"true\",\n };\n if (routeEdges) {\n // Routes and label boxes in root coordinates, whatever node an edge's\n // route is computed in, so a drawn route needs no offset.\n rootLayoutOptions[\"elk.json.edgeCoords\"] = \"ROOT\";\n }\n\n // Fast path: no hierarchy info → flat layout\n if (!graphspec || !analysis || analysis.controllerNodeIds.size === 0) {\n const dimensionMap: Record<string, NodeDimensions> = {};\n const elkChildren: ElkNode[] = nodes.map((node) => {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n return makeLeafNode(node.id, dims, direction);\n });\n\n const elkEdges: ElkExtendedEdge[] = edges.map((edge) => makeElkEdge(edge, routeEdges));\n\n return {\n elkGraph: {\n id: \"root\",\n layoutOptions: rootLayoutOptions,\n children: elkChildren,\n edges: elkEdges,\n },\n dimensionMap,\n };\n }\n\n // Hierarchical layout: build tree from containment analysis\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n const depths = computeDepths(analysis.controllerNodeIds, analysis.containmentTree);\n const dimensionMap: Record<string, NodeDimensions> = {};\n\n // Create a map from node ID to its ELK leaf node\n const nodeById = new Map<string, GraphNode>();\n for (const node of nodes) nodeById.set(node.id, node);\n\n // Build controller compound nodes (bottom-up: leaf controllers first)\n const controllerElkNodes: Record<string, ElkNode> = {};\n const controllerIds = Array.from(analysis.controllerNodeIds);\n controllerIds.sort((a, b) => (depths[a] ?? 0) - (depths[b] ?? 0));\n\n for (const ctrlId of controllerIds) {\n const depth = depths[ctrlId] ?? 0;\n const depthScale = 1 + depth * 0.15;\n const padX = Math.round(groupPadding.x * depthScale);\n const padTop = Math.round(groupPadding.top * depthScale);\n const padBottom = Math.round(groupPadding.bottom * depthScale);\n\n const ctrlLayoutOptions: LayoutOptions = {\n \"elk.padding\": `[top=${padTop},left=${padX},bottom=${padBottom},right=${padX}]`,\n \"elk.spacing.nodeNode\": String(nodesep),\n \"elk.layered.spacing.nodeNodeBetweenLayers\": String(ranksep),\n \"elk.spacing.edgeNode\": edgeNodeSpacing,\n \"elk.layered.spacing.edgeNodeBetweenLayers\": edgeNodeSpacing,\n };\n\n const children: ElkNode[] = [];\n const directChildren = analysis.containmentTree[ctrlId] || [];\n\n for (const childId of directChildren) {\n if (analysis.controllerNodeIds.has(childId)) {\n // Child is a controller — use its already-built compound node\n const childElk = controllerElkNodes[childId];\n if (childElk) children.push(childElk);\n } else {\n // Child is an operator — create leaf node with ports\n const graphNode = nodeById.get(childId);\n if (graphNode) {\n const dims = estimateNodeDimensions(graphNode, isHorizontal);\n dimensionMap[childId] = dims;\n children.push(makeLeafNode(childId, dims, direction));\n }\n }\n }\n\n // Add stuff nodes that belong to this controller\n for (const node of nodes) {\n if (node.data.isStuff && childToCtrl[node.id] === ctrlId) {\n if (!children.some((c) => c.id === node.id)) {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n children.push(makeLeafNode(node.id, dims, direction));\n }\n }\n }\n\n controllerElkNodes[ctrlId] = {\n id: ctrlId,\n layoutOptions: ctrlLayoutOptions,\n children,\n };\n }\n\n // Build root children: top-level controllers + loose nodes\n const rootChildren: ElkNode[] = [];\n\n // Add top-level controllers (those not contained by another controller)\n for (const ctrlId of controllerIds) {\n if (!childToCtrl[ctrlId]) {\n const elkNode = controllerElkNodes[ctrlId];\n if (elkNode) rootChildren.push(elkNode);\n }\n }\n\n // Add loose nodes (not inside any controller and not controllers themselves)\n for (const node of nodes) {\n if (!childToCtrl[node.id] && !analysis.controllerNodeIds.has(node.id)) {\n const dims = estimateNodeDimensions(node, isHorizontal);\n dimensionMap[node.id] = dims;\n rootChildren.push(makeLeafNode(node.id, dims, direction));\n }\n }\n\n // All edges at root level — INCLUDE_CHILDREN handles cross-hierarchy routing\n const nodeIdSet = new Set(nodes.map((n) => n.id));\n const elkEdges: ElkExtendedEdge[] = edges\n .filter((e) => nodeIdSet.has(e.source) && nodeIdSet.has(e.target))\n .map((edge) => makeElkEdge(edge, routeEdges));\n\n return {\n elkGraph: {\n id: \"root\",\n layoutOptions: rootLayoutOptions,\n children: rootChildren,\n edges: elkEdges,\n },\n dimensionMap,\n };\n}\n\n// ─── Extract absolute positions from ELK output ────────────────────────────\n\nexport interface ElkPositionResult {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport function extractAbsolutePositions(elkResult: ElkNode): Record<string, ElkPositionResult> {\n const positions: Record<string, ElkPositionResult> = {};\n\n function walk(node: ElkNode, parentX: number, parentY: number) {\n const absX = parentX + (node.x ?? 0);\n const absY = parentY + (node.y ?? 0);\n\n if (node.id !== \"root\") {\n positions[node.id] = {\n x: absX,\n y: absY,\n width: node.width ?? 0,\n height: node.height ?? 0,\n };\n }\n\n for (const child of node.children ?? []) {\n walk(child, absX, absY);\n }\n }\n\n walk(elkResult, 0, 0);\n return positions;\n}\n\n// ─── Extract edge routes from ELK output ────────────────────────────────────\n\n/**\n * Every edge's route, keyed by edge id, from a layout run with `routeEdges`\n * (its coordinates are then the root's). An edge ELK reports no section for is\n * left out, and is drawn as a free curve.\n */\nexport function extractEdgeRoutes(elkResult: ElkNode): Record<string, EdgeRoute> {\n const routes: Record<string, EdgeRoute> = {};\n\n function walk(node: ElkNode) {\n for (const edge of node.edges ?? []) {\n const points: GraphPoint[] = [];\n for (const section of edge.sections ?? []) {\n for (const point of [section.startPoint, ...(section.bendPoints ?? []), section.endPoint]) {\n const last = points[points.length - 1];\n if (!last || last.x !== point.x || last.y !== point.y) {\n points.push({ x: point.x, y: point.y });\n }\n }\n }\n if (points.length < 2) continue;\n const route: EdgeRoute = { points };\n const label = edge.labels?.[0];\n if (label && label.x !== undefined && label.y !== undefined) {\n route.label = {\n x: label.x,\n y: label.y,\n width: label.width ?? 0,\n height: label.height ?? 0,\n };\n }\n routes[edge.id] = route;\n }\n for (const child of node.children ?? []) walk(child);\n }\n\n walk(elkResult);\n return routes;\n}\n","import ELK from \"elkjs/lib/elk.bundled.js\";\nimport type {\n GraphNode,\n GraphEdge,\n GraphSpec,\n DataflowAnalysis,\n LayoutConfig,\n GraphDirection,\n} from \"./types\";\nimport {\n buildElkGraph,\n extractAbsolutePositions,\n extractEdgeRoutes,\n estimateNodeDimensions,\n} from \"./elkGraphBuilder\";\nimport type { ElkPositionResult } from \"./elkGraphBuilder\";\n\n// Cache ELK instance at module level to avoid repeated WASM initialization\nlet elkInstance: InstanceType<typeof ELK> | null = null;\nfunction getElk(): InstanceType<typeof ELK> {\n if (!elkInstance) elkInstance = new ELK();\n return elkInstance;\n}\n\nexport interface LayoutResult {\n nodes: GraphNode[];\n edges: GraphEdge[];\n /** ELK-computed positions/sizes for controller compound nodes. */\n controllerPositions: Record<string, ElkPositionResult>;\n}\n\nexport async function getLayoutedElements(\n nodes: GraphNode[],\n edges: GraphEdge[],\n direction: GraphDirection,\n layoutConfig?: LayoutConfig,\n graphspec?: GraphSpec | null,\n analysis?: DataflowAnalysis | null,\n): Promise<LayoutResult> {\n if (nodes.length === 0) return { nodes: [], edges, controllerPositions: {} };\n\n const isHorizontal = direction === \"LR\" || direction === \"RL\";\n\n const { elkGraph, dimensionMap } = buildElkGraph(\n nodes,\n edges,\n graphspec ?? null,\n analysis ?? null,\n direction,\n layoutConfig,\n );\n\n const layoutResult = await getElk().layout(elkGraph);\n const positions = extractAbsolutePositions(layoutResult);\n\n // Extract controller positions from ELK output\n const controllerPositions: Record<string, ElkPositionResult> = {};\n if (analysis) {\n for (const ctrlId of analysis.controllerNodeIds) {\n if (positions[ctrlId]) {\n controllerPositions[ctrlId] = positions[ctrlId];\n }\n }\n }\n\n const result = nodes.map((node) => {\n const pos = positions[node.id];\n const dims = dimensionMap[node.id] ?? estimateNodeDimensions(node, isHorizontal);\n\n const width = dims.width;\n const height = dims.height;\n\n // Inject direction into pipeCardData so the card component can adjust orientation\n const pipeCardData = node.data.pipeCardData;\n const cardDirection = isHorizontal ? (\"LR\" as const) : (\"TB\" as const);\n const updatedPipeCardData = pipeCardData\n ? { ...pipeCardData, direction: cardDirection }\n : undefined;\n\n return {\n ...node,\n data: {\n ...node.data,\n _estimatedWidth: width,\n _estimatedHeight: height,\n pipeCardData: updatedPipeCardData,\n },\n // Lock the ReactFlow node wrapper to the exact dimensions ELK used for layout.\n // This ensures ReactFlow's Handle (centered on the DOM element) matches\n // the port position ELK computed (centered on the estimated dimensions).\n style: {\n ...node.style,\n width: width + \"px\",\n height: height + \"px\",\n },\n position: {\n x: pos ? pos.x : 0,\n y: pos ? pos.y : 0,\n },\n sourcePosition: (isHorizontal\n ? direction === \"LR\"\n ? \"right\"\n : \"left\"\n : direction === \"TB\"\n ? \"bottom\"\n : \"top\") as GraphNode[\"sourcePosition\"],\n targetPosition: (isHorizontal\n ? direction === \"LR\"\n ? \"left\"\n : \"right\"\n : direction === \"TB\"\n ? \"top\"\n : \"bottom\") as GraphNode[\"targetPosition\"],\n };\n });\n\n if (!layoutConfig?.routeEdges) return { nodes: result, edges, controllerPositions };\n const routes = extractEdgeRoutes(layoutResult);\n const routedEdges = edges.map((edge) => {\n const route = routes[edge.id];\n return route ? { ...edge, route } : edge;\n });\n return { nodes: result, edges: routedEdges, controllerPositions };\n}\n","// Drawing an edge along the route the layout computed for it. Pure and\n// React-free: the routed edge component calls it with the handle positions\n// ReactFlow measured, which may sit a few pixels from the ports the layout\n// assumed, or anywhere at all once a node has been dragged.\n\nimport type { EdgeRoute, GraphPoint } from \"./types\";\n\n/**\n * How far a measured handle may sit from the port the layout routed from\n * before the route is stale: a node dragged away. Handles are drawn a few\n * pixels outside their node, which this absorbs.\n */\nexport const ROUTE_ENDPOINT_TOLERANCE_PX = 8;\n\n/** The radius each bend of a route is rounded with, shrunk on short segments. */\nexport const ROUTE_CORNER_RADIUS_PX = 8;\n\nconst SAME_AXIS_EPSILON = 0.5;\n\nfunction near(a: GraphPoint, b: GraphPoint, tolerance: number): boolean {\n return Math.abs(a.x - b.x) <= tolerance && Math.abs(a.y - b.y) <= tolerance;\n}\n\n/**\n * A route's points with its ends moved onto the measured handles, or null\n * when either handle is too far from the port the layout routed from. The\n * point next to a moved end moves with it along the segment's axis, so an\n * orthogonal route stays orthogonal.\n */\nexport function fitRoute(\n points: readonly GraphPoint[],\n source: GraphPoint,\n target: GraphPoint,\n tolerance: number = ROUTE_ENDPOINT_TOLERANCE_PX,\n): GraphPoint[] | null {\n if (points.length < 2) return null;\n const first = points[0];\n const last = points[points.length - 1];\n if (!near(first, source, tolerance) || !near(last, target, tolerance)) return null;\n\n const fitted = points.map((p) => ({ x: p.x, y: p.y }));\n const end = fitted.length - 1;\n if (fitted.length > 2) {\n alignNeighbour(fitted, 0, 1, source);\n alignNeighbour(fitted, end, end - 1, target);\n }\n fitted[0] = { x: source.x, y: source.y };\n fitted[end] = { x: target.x, y: target.y };\n return fitted;\n}\n\n/** Move the neighbour of an end along with it, on the axis their segment shares. */\nfunction alignNeighbour(\n points: GraphPoint[],\n endIndex: number,\n neighbourIndex: number,\n to: GraphPoint,\n) {\n const end = points[endIndex];\n const neighbour = points[neighbourIndex];\n if (Math.abs(end.y - neighbour.y) <= SAME_AXIS_EPSILON) neighbour.y = to.y;\n else if (Math.abs(end.x - neighbour.x) <= SAME_AXIS_EPSILON) neighbour.x = to.x;\n}\n\n/**\n * An SVG path through a polyline, each bend rounded by a quadratic curve. A\n * bend's radius shrinks to half its shorter segment, so close bends never\n * overshoot each other.\n */\nexport function roundedPolylinePath(\n points: readonly GraphPoint[],\n radius: number = ROUTE_CORNER_RADIUS_PX,\n): string {\n if (points.length === 0) return \"\";\n const parts = [`M ${points[0].x},${points[0].y}`];\n for (let i = 1; i < points.length - 1; i++) {\n const previous = points[i - 1];\n const bend = points[i];\n const next = points[i + 1];\n const inLength = Math.hypot(bend.x - previous.x, bend.y - previous.y);\n const outLength = Math.hypot(next.x - bend.x, next.y - bend.y);\n const r = Math.min(radius, inLength / 2, outLength / 2);\n if (r <= 0) {\n parts.push(`L ${bend.x},${bend.y}`);\n continue;\n }\n const before = {\n x: bend.x + ((previous.x - bend.x) / inLength) * r,\n y: bend.y + ((previous.y - bend.y) / inLength) * r,\n };\n const after = {\n x: bend.x + ((next.x - bend.x) / outLength) * r,\n y: bend.y + ((next.y - bend.y) / outLength) * r,\n };\n parts.push(`L ${before.x},${before.y}`, `Q ${bend.x},${bend.y} ${after.x},${after.y}`);\n }\n const last = points[points.length - 1];\n parts.push(`L ${last.x},${last.y}`);\n return parts.join(\" \");\n}\n\n/** The point halfway along a polyline, for a label the layout did not place. */\nexport function polylineMidpoint(points: readonly GraphPoint[]): GraphPoint {\n if (points.length === 0) return { x: 0, y: 0 };\n let total = 0;\n for (let i = 1; i < points.length; i++) {\n total += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);\n }\n let remaining = total / 2;\n for (let i = 1; i < points.length; i++) {\n const a = points[i - 1];\n const b = points[i];\n const length = Math.hypot(b.x - a.x, b.y - a.y);\n if (length >= remaining && length > 0) {\n const t = remaining / length;\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n }\n remaining -= length;\n }\n return points[points.length - 1];\n}\n\n/** Where a routed edge's label is centred: the box the layout placed, or the route's midpoint. */\nexport function routeLabelCenter(route: EdgeRoute, fitted: readonly GraphPoint[]): GraphPoint {\n if (route.label) {\n return {\n x: route.label.x + route.label.width / 2,\n y: route.label.y + route.label.height / 2,\n };\n }\n return polylineMidpoint(fitted);\n}\n","import type {\n GraphSpec,\n DataflowAnalysis,\n FoldToggleOptions,\n GraphNode,\n GraphEdge,\n PipeCallNode,\n PipeType,\n} from \"./types\";\nimport {\n CONTROLLER_PADDING_X,\n CONTROLLER_PADDING_TOP,\n CONTROLLER_PADDING_BOTTOM,\n NODE_TYPE_CONTROLLER,\n graphSpecMode,\n nodeWidth,\n nodeHeight,\n isStuffNodeId,\n} from \"./types\";\nimport { buildChildToControllerMap } from \"./graphAnalysis\";\nimport { asPipeCallNode, GraphSpecValidationError } from \"./validateGraphSpec\";\n\n/** Max visible children before a parallel/batch controller auto-collapses. */\nexport const MAX_VISIBLE_CONTROLLER_CHILDREN = 5;\n\n/**\n * Collect all descendant node IDs of a controller (recursive).\n */\nfunction getDescendants(\n controllerId: string,\n containmentTree: Readonly<Record<string, readonly string[]>>,\n controllerNodeIds: ReadonlySet<string>,\n): Set<string> {\n const result = new Set<string>();\n const stack = [controllerId];\n while (stack.length > 0) {\n const id = stack.pop()!;\n for (const childId of containmentTree[id] || []) {\n result.add(childId);\n if (controllerNodeIds.has(childId)) stack.push(childId);\n }\n }\n return result;\n}\n\n/** Position and size for a controller, as computed by the layout engine. */\nexport interface ControllerRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Build controller group nodes that wrap child operators/stuff nodes.\n *\n * When `controllerPositions` is provided (from ELK layout), uses those exact\n * positions/sizes instead of recomputing from child bounding boxes. This ensures\n * controller containers match the layout engine's spacing and edge routing.\n *\n * **Side effect:** mutates `layoutedNodes` entries to set `parentId`, `extent`,\n * and convert positions to parent-relative coordinates.\n */\nexport function buildControllerNodes(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n layoutedNodes: GraphNode[],\n controllerPositions?: Record<string, ControllerRect>,\n): GraphNode[] {\n const nodeById: Record<string, GraphNode> = {};\n const graphMode = graphSpecMode(graphspec);\n for (const n of layoutedNodes) {\n nodeById[n.id] = n;\n }\n\n // A controller node is a pipe-call node; the guard makes a malformed spec\n // fail loudly here rather than surfacing later as an undefined pipe_code.\n const controllerInfo: Record<string, PipeCallNode> = {};\n for (const node of graphspec.nodes) {\n if (analysis.controllerNodeIds.has(node.id)) {\n controllerInfo[node.id] = asPipeCallNode(node, `nodes[${node.id}]`);\n }\n }\n\n const depthCache: Record<string, number> = {};\n const depthVisiting = new Set<string>();\n function getDepth(controllerId: string): number {\n if (depthCache[controllerId] !== undefined) return depthCache[controllerId];\n if (depthVisiting.has(controllerId)) {\n throw new Error(\n `Cycle detected in containment tree: controller \"${controllerId}\" is part of a containment cycle`,\n );\n }\n depthVisiting.add(controllerId);\n const children = analysis.containmentTree[controllerId] || [];\n let maxChildDepth = -1;\n for (const childId of children) {\n if (analysis.controllerNodeIds.has(childId)) {\n maxChildDepth = Math.max(maxChildDepth, getDepth(childId));\n }\n }\n depthVisiting.delete(controllerId);\n depthCache[controllerId] = maxChildDepth + 1;\n return depthCache[controllerId];\n }\n\n const childToController = buildChildToControllerMap(graphspec, analysis);\n const controllerStuffChildren: Record<string, string[]> = {};\n for (const [nodeId, ctrlId] of Object.entries(childToController)) {\n if (!isStuffNodeId(nodeId)) continue;\n if (!nodeById[nodeId]) continue;\n if (!controllerStuffChildren[ctrlId]) controllerStuffChildren[ctrlId] = [];\n controllerStuffChildren[ctrlId].push(nodeId);\n }\n\n const controllerIds = Array.from(analysis.controllerNodeIds);\n for (const id of controllerIds) getDepth(id);\n controllerIds.sort((a, b) => depthCache[a] - depthCache[b]);\n\n const controllerNodes: GraphNode[] = [];\n const childToParent: Record<string, string> = {};\n\n for (const controllerId of controllerIds) {\n const directChildren = analysis.containmentTree[controllerId] || [];\n const renderedChildren = directChildren.filter((cid) => nodeById[cid]);\n const stuffChildren = controllerStuffChildren[controllerId] || [];\n const allChildren = [...renderedChildren, ...stuffChildren];\n\n if (allChildren.length === 0) continue;\n\n let groupX: number, groupY: number, groupW: number, groupH: number;\n\n const elkPos = controllerPositions?.[controllerId];\n if (elkPos) {\n // Use ELK's computed position and size — accounts for edge routing and spacing\n groupX = elkPos.x;\n groupY = elkPos.y;\n groupW = elkPos.width;\n groupH = elkPos.height;\n } else {\n // Fallback: compute bounding box from child positions\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const childId of allChildren) {\n const child = nodeById[childId];\n const pos = child.position;\n const w = nodeWidth(child);\n const h = nodeHeight(child);\n minX = Math.min(minX, pos.x);\n minY = Math.min(minY, pos.y);\n maxX = Math.max(maxX, pos.x + w);\n maxY = Math.max(maxY, pos.y + h);\n }\n\n const depth = depthCache[controllerId] ?? 0;\n const depthScale = 1 + depth * 0.15;\n const padX = Math.round(CONTROLLER_PADDING_X * depthScale);\n const padTop = Math.round(CONTROLLER_PADDING_TOP * depthScale);\n const padBottom = Math.round(CONTROLLER_PADDING_BOTTOM * depthScale);\n\n groupX = minX - padX;\n groupY = minY - padTop;\n groupW = maxX - minX + 2 * padX;\n groupH = maxY - minY + padTop + padBottom;\n }\n\n const info = controllerInfo[controllerId];\n if (!info) {\n // controllerId comes from a `contains` edge source with no matching node.\n throw new GraphSpecValidationError(\n `nodes[${controllerId}]`,\n `controller \"${controllerId}\" is referenced by a \"contains\" edge but ` +\n `has no corresponding node in graphspec.nodes`,\n );\n }\n const pipeCode = info.pipe_code;\n const groupNode: GraphNode = {\n id: controllerId,\n type: NODE_TYPE_CONTROLLER,\n data: {\n label: pipeCode,\n pipeType: info.pipe_type,\n isController: true,\n isPipe: false,\n isStuff: false,\n pipeCode: pipeCode,\n labelText: pipeCode,\n graphMode,\n },\n position: { x: groupX, y: groupY },\n style: {\n width: groupW + \"px\",\n height: groupH + \"px\",\n padding: \"0\",\n },\n };\n\n controllerNodes.push(groupNode);\n nodeById[controllerId] = groupNode;\n\n for (const childId of allChildren) {\n childToParent[childId] = controllerId;\n }\n }\n\n // Convert child positions to parent-relative and set parentId\n for (const [childId, parentId] of Object.entries(childToParent)) {\n const child = nodeById[childId];\n const parent = nodeById[parentId];\n if (!child || !parent) continue;\n child.position = {\n x: child.position.x - parent.position.x,\n y: child.position.y - parent.position.y,\n };\n child.parentId = parentId;\n child.extent = \"parent\";\n }\n\n return controllerNodes;\n}\n\n/**\n * Apply controller containers to layouted nodes if showControllers is enabled.\n *\n * When `expandedControllers` is provided, PipeParallel/PipeBatch controllers with\n * more than MAX_VISIBLE_CONTROLLER_CHILDREN children are auto-collapsed unless the\n * controller ID is in the expanded set.\n */\nexport function applyControllers(\n layoutedNodes: GraphNode[],\n layoutedEdges: GraphEdge[],\n graphspec: GraphSpec | null,\n analysis: DataflowAnalysis | null,\n showControllers: boolean,\n expandedControllers?: ReadonlySet<string>,\n onToggleCollapse?: (controllerId: string) => void,\n controllerPositions?: Record<string, ControllerRect>,\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void,\n): { nodes: GraphNode[]; edges: GraphEdge[] } {\n if (!showControllers || !analysis || !graphspec) {\n return { nodes: layoutedNodes, edges: layoutedEdges };\n }\n\n // ─── Compute child counts and effective collapse state ─────────────────\n const childCounts: Record<string, number> = {};\n const collapsedSet = new Set<string>();\n\n // A controller is a pipe-call node; the guard narrows its type the way\n // `buildControllerNodes` does, and fails loudly on a malformed spec.\n const controllerTypeMap: Record<string, PipeType> = {};\n for (const node of graphspec.nodes) {\n if (analysis.controllerNodeIds.has(node.id)) {\n controllerTypeMap[node.id] = asPipeCallNode(node, `nodes[${node.id}]`).pipe_type;\n }\n }\n\n for (const ctrlId of analysis.controllerNodeIds) {\n const directChildren = analysis.containmentTree[ctrlId] || [];\n childCounts[ctrlId] = directChildren.length;\n const pipeType = controllerTypeMap[ctrlId];\n const isCollapsible = pipeType === \"PipeParallel\" || pipeType === \"PipeBatch\";\n if (\n isCollapsible &&\n directChildren.length > MAX_VISIBLE_CONTROLLER_CHILDREN &&\n !expandedControllers?.has(ctrlId)\n ) {\n collapsedSet.add(ctrlId);\n }\n }\n\n // ─── Filter hidden children for collapsed controllers ──────────────────\n let filteredNodes = layoutedNodes;\n let filteredEdges = layoutedEdges;\n\n if (collapsedSet.size > 0) {\n const hiddenNodes = new Set<string>();\n\n for (const ctrlId of collapsedSet) {\n const directChildren = analysis.containmentTree[ctrlId] || [];\n const toHide = directChildren.slice(MAX_VISIBLE_CONTROLLER_CHILDREN);\n for (const childId of toHide) {\n hiddenNodes.add(childId);\n if (analysis.controllerNodeIds.has(childId)) {\n for (const d of getDescendants(\n childId,\n analysis.containmentTree,\n analysis.controllerNodeIds,\n )) {\n hiddenNodes.add(d);\n }\n }\n }\n }\n\n // Also hide stuff nodes inside collapsed controllers that have no visible\n // pipe connection. We ignore stuff-to-stuff edges (batch_item/batch_aggregate)\n // because those would keep orphaned branch stuff nodes alive.\n const childToCtrl = buildChildToControllerMap(graphspec, analysis);\n for (const node of layoutedNodes) {\n if (!isStuffNodeId(node.id) || hiddenNodes.has(node.id)) continue;\n const ctrlId = childToCtrl[node.id];\n if (!ctrlId || (!collapsedSet.has(ctrlId) && !hiddenNodes.has(ctrlId))) continue;\n const pipeEdges = layoutedEdges.filter((e) => {\n if (e.source !== node.id && e.target !== node.id) return false;\n const other = e.source === node.id ? e.target : e.source;\n return !isStuffNodeId(other); // only pipe connections\n });\n if (\n pipeEdges.length === 0 ||\n pipeEdges.every((e) => {\n const other = e.source === node.id ? e.target : e.source;\n return hiddenNodes.has(other);\n })\n ) {\n hiddenNodes.add(node.id);\n }\n }\n\n filteredNodes = layoutedNodes.filter((n) => !hiddenNodes.has(n.id));\n filteredEdges = layoutedEdges.filter(\n (e) => !hiddenNodes.has(e.source) && !hiddenNodes.has(e.target),\n );\n }\n\n // ─── Build controller group nodes from visible children ────────────────\n const controllerNodes = buildControllerNodes(\n graphspec,\n analysis,\n filteredNodes,\n controllerPositions,\n );\n if (controllerNodes.length === 0) {\n return { nodes: filteredNodes, edges: filteredEdges };\n }\n\n // Inject collapse + fold metadata into controller node data\n for (const cn of controllerNodes) {\n const count = childCounts[cn.id] ?? 0;\n const isCollapsed = collapsedSet.has(cn.id);\n cn.data.childCount = count;\n cn.data.isCollapsed = isCollapsed;\n if (onToggleCollapse) {\n const id = cn.id;\n cn.data.onToggleCollapse = () => onToggleCollapse(id);\n }\n if (onToggleFold) {\n const id = cn.id;\n cn.data.onToggleFold = (options?: FoldToggleOptions) => onToggleFold(id, options);\n }\n }\n\n return { nodes: sortParentsFirst([...controllerNodes, ...filteredNodes]), edges: filteredEdges };\n}\n\n/**\n * Order nodes so every group node comes before its children, which ReactFlow\n * requires. Stable within a nesting depth. Throws on a cycle in the parent\n * chain rather than looping.\n */\nexport function sortParentsFirst(nodes: GraphNode[]): GraphNode[] {\n const nodeMap: Record<string, GraphNode> = {};\n for (const n of nodes) nodeMap[n.id] = n;\n const depthOf: Record<string, number> = {};\n const depthVisiting = new Set<string>();\n function getContainmentDepth(id: string): number {\n if (depthOf[id] !== undefined) return depthOf[id];\n if (depthVisiting.has(id)) {\n throw new Error(\n `Cycle detected in node parent chain: node \"${id}\" references itself as an ancestor`,\n );\n }\n depthVisiting.add(id);\n const n = nodeMap[id];\n depthOf[id] = n && n.parentId ? 1 + getContainmentDepth(n.parentId) : 0;\n depthVisiting.delete(id);\n return depthOf[id];\n }\n for (const n of nodes) getContainmentDepth(n.id);\n return [...nodes].sort((a, b) => depthOf[a.id] - depthOf[b.id]);\n}\n","import type { GraphStyleId, LayoutConfig } from \"@graph/types\";\nimport { GRAPH_STYLE } from \"@graph/types\";\nimport { DEFAULT_GRAPH_CONFIG } from \"@graph/graphConfig\";\n\n// ─── The style registry ─────────────────────────────────────────────────────\n// A closed, typed table: adding a style is a new `GRAPH_STYLE` id, after which\n// the compiler names every table keyed by `GraphStyleId` that needs a row — the\n// descriptor below, the drawing pipeline (`stylePipelines.ts`), the node\n// components and the menu icon in the React layer.\n\n/** Which of the toolbar's controls make sense for a style. */\nexport interface GraphStyleCapabilities {\n /**\n * The toolbar's show/hide controllers toggle applies: the style draws\n * controller frames on demand. A style that decides its own frames hides it.\n */\n controllerFrameToggle: boolean;\n /**\n * Fold-all and expand-all apply. In a style that also has the frame toggle\n * they apply only while the frames are shown, since a fold is undone from a\n * frame.\n */\n foldAll: boolean;\n}\n\n/** What the registry says of one style: what it is called, what it is for, and what it supports. */\nexport interface GraphStyleDescriptor {\n id: GraphStyleId;\n /** The style's name in the toolbar menu. */\n name: string;\n /** One line saying what the style shows, under its name in the menu. */\n description: string;\n capabilities: GraphStyleCapabilities;\n /**\n * How the style lays out: its spacing, which replaces `config.nodesep` and\n * `config.ranksep` for it, the room its frames keep around their steps, and\n * whether its edges follow routes around the nodes in their way. A style\n * that leaves it undefined keeps the host's spacing and the controller\n * groups' padding, and draws free curves of the host's `edgeType`.\n */\n layout?: Required<LayoutConfig>;\n}\n\nexport const GRAPH_STYLES: Record<GraphStyleId, GraphStyleDescriptor> = {\n [GRAPH_STYLE.DETAILED]: {\n id: GRAPH_STYLE.DETAILED,\n name: \"Detailed\",\n description: \"Every pipe with its inputs, outputs and settings\",\n capabilities: { controllerFrameToggle: true, foldAll: true },\n },\n [GRAPH_STYLE.SIMPLE]: {\n id: GRAPH_STYLE.SIMPLE,\n name: \"Simple\",\n description: \"The method's steps in plain words\",\n capabilities: { controllerFrameToggle: false, foldAll: true },\n layout: {\n nodesep: 28,\n ranksep: 44,\n routeEdges: true,\n groupPadding: { x: 20, top: 40, bottom: 16 },\n },\n },\n};\n\n/** Every registered style, in the order the menu lists them. */\nexport const GRAPH_STYLE_IDS: readonly GraphStyleId[] = [GRAPH_STYLE.DETAILED, GRAPH_STYLE.SIMPLE];\n\n/**\n * Whether a value names a registered style. For values that cross an untyped\n * boundary — an editor setting, the standalone page's config embed, a host\n * written in plain JavaScript — before they reach the viewer.\n */\nexport function isGraphStyleId(value: unknown): value is GraphStyleId {\n return typeof value === \"string\" && Object.hasOwn(GRAPH_STYLES, value);\n}\n\n/**\n * Resolve the active style from `(graphStyle prop, config.graphStyle, default)`.\n * Pure + exported so the controlled/reactive precedence is unit-testable\n * without React, under the same contract `resolveExternalThemeMode` holds:\n * - the prop wins when it names a style,\n * - falls back to `config.graphStyle` when the prop is undefined (so a host can\n * hand control back to config after passing the prop),\n * - falls back to the library default (`detailed`) when neither is set.\n *\n * A value naming no registered style is treated as unset rather than thrown\n * on: a style is a preference, and a host relaying a setting written for a\n * newer version of this package should still get a graph.\n */\nexport function resolveGraphStyle(styleProp: unknown, configStyle: unknown): GraphStyleId {\n if (isGraphStyleId(styleProp)) return styleProp;\n if (isGraphStyleId(configStyle)) return configStyle;\n return DEFAULT_GRAPH_CONFIG.graphStyle;\n}\n\n/**\n * The styles the toolbar menu offers, from the viewer's `styleMenu` prop:\n * nothing for `false` or `undefined` (the menu is opt-in), every registered\n * style in registry order for `true`, and for a list the registered styles it\n * names, in its order and without repeats. A menu needs two styles to choose\n * between, so fewer than two offer none.\n */\nexport function resolveStyleMenu(\n styleMenu: boolean | readonly GraphStyleId[] | undefined,\n): GraphStyleId[] {\n if (styleMenu === undefined || styleMenu === false) return [];\n const requested: readonly unknown[] = styleMenu === true ? GRAPH_STYLE_IDS : styleMenu;\n const offered: GraphStyleId[] = [];\n for (const id of requested) {\n if (isGraphStyleId(id) && !offered.includes(id)) offered.push(id);\n }\n return offered.length >= 2 ? offered : [];\n}\n","// Plain words from identifiers: the simple style's fallback when a method\n// declares nothing better. Pure, deterministic and tested — never a stand-in\n// for an authored description, and never a guess at meaning: an identifier is\n// split, cased and spelled, nothing more.\n\n/**\n * Words spelled as acronyms although they hold a vowel. A word with no vowel at\n * all (`cv`, `pdf`, `rfp`, `html`) is spelled as an acronym without being\n * listed, unless it is one of the `VOWELLESS_WORDS`.\n */\nconst VOWELED_ACRONYMS: ReadonlySet<string> = new Set([\n \"ai\",\n \"api\",\n \"faq\",\n \"iban\",\n \"id\",\n \"io\",\n \"ip\",\n \"json\",\n \"kpi\",\n \"ocr\",\n \"roi\",\n \"seo\",\n \"ui\",\n \"uri\",\n \"url\",\n \"uuid\",\n \"ux\",\n]);\n\nconst VOWELS = /[aeiouy]/;\n\n/**\n * Words with no vowel that are written in lowercase rather than spelled out:\n * abbreviations, units and ordinal suffixes found in identifiers (`weight_kg`\n * is \"Weight kg\", `nth_item` is \"Nth item\"). A plural is covered by its\n * singular (`hrs` by `hr`).\n */\nconst VOWELLESS_WORDS: ReadonlySet<string> = new Set([\n \"cfg\",\n \"cm\",\n \"cnt\",\n \"ctx\",\n \"dr\",\n \"dst\",\n \"fn\",\n \"ft\",\n \"hmm\",\n \"hr\",\n \"kg\",\n \"km\",\n \"lb\",\n \"mg\",\n \"ml\",\n \"mm\",\n \"mr\",\n \"mrs\",\n \"ms\",\n \"msg\",\n \"nd\",\n \"nth\",\n \"pkg\",\n \"pt\",\n \"rd\",\n \"src\",\n \"st\",\n \"th\",\n \"tmp\",\n \"vs\",\n]);\n\n/** Vowelless acronyms that end in `s` without being plurals (`css` is not two `cs`). */\nconst SINGULAR_S_ACRONYMS: ReadonlySet<string> = new Set([\n \"cms\",\n \"css\",\n \"dns\",\n \"gps\",\n \"rss\",\n \"sms\",\n]);\n\n/**\n * The plain names of the natives whose code does not read as words once split\n * (`YesNo` is not \"Yes no\"). Every other native reads well humanized.\n */\nconst NATIVE_PLAIN_NAMES: Readonly<Record<string, string>> = {\n Html: \"HTML\",\n JSON: \"JSON\",\n TextAndImages: \"Text and images\",\n YesNo: \"Yes or no\",\n SearchResult: \"Search result\",\n Composite: \"Combined results\",\n Dynamic: \"Data\",\n};\n\n/**\n * Natives that say what kind of value something is without saying what it is:\n * a final output of one of these is named by its variable, not its concept.\n */\nconst GENERIC_NATIVES: ReadonlySet<string> = new Set([\"Anything\", \"Dynamic\", \"Composite\"]);\n\nfunction isAcronymWord(lower: string): boolean {\n if (lower.length < 2 || !/^[a-z]+$/.test(lower)) return false;\n if (VOWELLESS_WORDS.has(lower)) return false;\n if (lower.endsWith(\"s\") && VOWELLESS_WORDS.has(lower.slice(0, -1))) return false;\n return !VOWELS.test(lower) || VOWELED_ACRONYMS.has(lower);\n}\n\n/** Spell one lowercase word: an acronym in capitals (a plural keeps its `s`), anything else as is. */\nfunction spellWord(lower: string, wasAllCaps: boolean): string {\n if (/^\\d+$/.test(lower)) return lower;\n if (wasAllCaps && lower.length >= 2) return lower.toUpperCase();\n // A plural acronym keeps its lowercase `s`: `cvs` is \"CVs\", not \"CVS\".\n if (lower.endsWith(\"s\") && !SINGULAR_S_ACRONYMS.has(lower) && isAcronymWord(lower.slice(0, -1))) {\n return lower.slice(0, -1).toUpperCase() + \"s\";\n }\n if (isAcronymWord(lower)) return lower.toUpperCase();\n return lower;\n}\n\n/**\n * Split an identifier into its words: on `_`, `-`, `.` and spaces, and at the\n * case boundaries of camel and Pascal case, keeping a run of capitals together\n * (`PDFDocument` is `PDF` and `Document`).\n */\nfunction splitIdentifier(identifier: string): { word: string; wasAllCaps: boolean }[] {\n const parts = identifier\n .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .replace(/([A-Za-z])(\\d)/g, \"$1 $2\")\n .split(/[\\s_\\-.]+/)\n .filter((p) => p.length > 0);\n return parts.map((p) => ({\n word: p.toLowerCase(),\n wasAllCaps: p.length >= 2 && p === p.toUpperCase() && /[A-Z]/.test(p),\n }));\n}\n\n/** Capitalize the first letter, leaving the rest as written. */\nfunction capitalizeFirst(text: string): string {\n return text.length === 0 ? text : text.charAt(0).toUpperCase() + text.slice(1);\n}\n\n/** The last segment of a dotted ref: the code without its domain (`recruitment.CandidateProfile` → `CandidateProfile`). */\nexport function stripDomain(ref: string): string {\n const dot = ref.lastIndexOf(\".\");\n return dot === -1 ? ref : ref.slice(dot + 1);\n}\n\n/**\n * An identifier as words in sentence case: `job_offer` → \"Job offer\",\n * `CandidateProfile` → \"Candidate profile\", `pr_url` → \"PR URL\",\n * `cvs` → \"CVs\". A dotted ref is humanized by its last segment.\n */\nexport function humanizeIdentifier(identifier: string): string {\n const words = splitIdentifier(stripDomain(identifier.trim()));\n if (words.length === 0) return \"\";\n return capitalizeFirst(words.map((w) => spellWord(w.word, w.wasAllCaps)).join(\" \"));\n}\n\n/** A concept's plain name, from its code or ref: a native's own plain name, or the humanized code. */\nexport function conceptPlainName(conceptRef: string): string {\n const code = stripDomain(conceptRef.trim());\n return NATIVE_PLAIN_NAMES[code] ?? humanizeIdentifier(code);\n}\n\n/** Whether a concept is a native that names no particular kind of content (`Anything`, `Dynamic`, `Composite`). */\nexport function isGenericConcept(conceptRef: string): boolean {\n return GENERIC_NATIVES.has(stripDomain(conceptRef.trim()));\n}\n\n/**\n * Authored text as a title: whitespace collapsed (descriptions are often\n * written across lines), the first letter capitalized, and a single closing\n * period dropped, since a label in a box reads as a heading. Nothing else is\n * touched: the words are the author's.\n */\nexport function sentenceCase(text: string): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n const trimmed =\n collapsed.endsWith(\".\") && !collapsed.endsWith(\"..\") ? collapsed.slice(0, -1) : collapsed;\n return capitalizeFirst(trimmed);\n}\n\n/** The outcome names that read better as other words: a yes-or-no test's, and the default. */\nconst OUTCOME_WORDS: Readonly<Record<string, string>> = {\n true: \"Yes\",\n false: \"No\",\n default: \"Otherwise\",\n};\n\n/**\n * A condition's outcome as an arrow label: `english` → \"English\",\n * `high_priority` → \"High priority\", a yes-or-no test's `true` and `false` →\n * \"Yes\" and \"No\", and the `default` outcome → \"Otherwise\". A branch taken on\n * several outcomes, written `no_fit | default` as the static builder labels it,\n * reads \"No fit or otherwise\".\n */\nexport function outcomeLabel(outcome: string): string {\n const outcomes = outcome\n .split(\"|\")\n .map((o) => o.trim())\n .filter((o) => o.length > 0);\n const labels = outcomes.map((o, index) => {\n const label = OUTCOME_WORDS[o.toLowerCase()] ?? humanizeIdentifier(o);\n return index === 0 ? label : label.charAt(0).toLowerCase() + label.slice(1);\n });\n return labels.join(\" or \");\n}\n","// How wide text renders before the DOM exists: what the layout sizes the\n// simple style's boxes and arrow labels from. Pure and deterministic.\n//\n// The widths are per glyph, as a fraction of the font size, measured in\n// Chromium for the stack `--font-sans` names (Inter, then the system sans of\n// each platform, then the generic one) and kept at the widest of them, at the\n// heaviest weight the style draws text in. An estimate that errs wide costs a\n// box a little spare room; one that errs narrow cuts a title's last line.\n\nconst GLYPH_EM_GROUPS: readonly (readonly [number, string])[] = [\n [0.28, \"il .,'\"],\n [0.33, \"fjt:;!()\"],\n [0.35, \"/\"],\n [0.39, \"I\"],\n [0.44, \"r-\"],\n [0.51, \"1z\"],\n [0.56, '7aceksvxyJ\"'],\n [0.59, \"25\"],\n [0.6, \"8\"],\n [0.61, \"369bdghnopquF?\"],\n [0.64, \"04\"],\n [0.67, \"ELPSTZ\"],\n [0.72, \"ABCDNRUVXY\"],\n [0.78, \"GHKOQ\"],\n [0.8, \"w\"],\n [0.83, \"&\"],\n [0.89, \"m\"],\n [0.94, \"M\"],\n [1, \"W%\"],\n];\n\nconst GLYPH_EM: ReadonlyMap<string, number> = new Map(\n GLYPH_EM_GROUPS.flatMap(([em, glyphs]) => [...glyphs].map((glyph) => [glyph, em] as const)),\n);\n\n/** A glyph outside the table (an accented letter, a symbol): as wide as the wide end of its case. */\nconst UNLISTED_LOWER_EM = 0.62;\nconst UNLISTED_UPPER_EM = 0.78;\n\nfunction glyphEm(glyph: string): number {\n const listed = GLYPH_EM.get(glyph);\n if (listed !== undefined) return listed;\n return glyph !== glyph.toLowerCase() ? UNLISTED_UPPER_EM : UNLISTED_LOWER_EM;\n}\n\n/** A text's rendered width at a font size, in pixels. */\nexport function textWidthPx(text: string, fontPx: number): number {\n let em = 0;\n for (const glyph of text) em += glyphEm(glyph);\n return em * fontPx;\n}\n\n/** How many lines a text wraps to at a width, and whether it fits in `maxLines`. */\nexport interface WrapEstimate {\n /** Lines the text needs, unclamped. */\n lines: number;\n /** Lines it is drawn on: `lines` clamped to `maxLines`. */\n drawnLines: number;\n /** Whether the text is cut, so its box must carry the full text in a tooltip. */\n clamped: boolean;\n}\n\n/**\n * Estimate a text's wrapped line count by greedy word wrap at a line width,\n * the way the browser wraps it. A word wider than a line is broken across as\n * many lines as it needs (`overflow-wrap: anywhere`), starting on a line of\n * its own.\n */\nexport function estimateWrap(\n text: string,\n lineWidthPx: number,\n fontPx: number,\n maxLines: number,\n): WrapEstimate {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const spacePx = textWidthPx(\" \", fontPx);\n let lines = words.length === 0 ? 0 : 1;\n let used = 0;\n for (const word of words) {\n const wordPx = textWidthPx(word, fontPx);\n if (wordPx > lineWidthPx) {\n if (used > 0) lines += 1;\n used = 0;\n for (const glyph of word) {\n const glyphPx = textWidthPx(glyph, fontPx);\n if (used > 0 && used + glyphPx > lineWidthPx) {\n lines += 1;\n used = 0;\n }\n used += glyphPx;\n }\n continue;\n }\n const needed = used === 0 ? wordPx : used + spacePx + wordPx;\n if (needed <= lineWidthPx) {\n used = needed;\n } else {\n lines += 1;\n used = wordPx;\n }\n }\n const drawnLines = Math.min(lines, maxLines);\n return { lines, drawnLines, clamped: lines > maxLines };\n}\n","// The simple style: a flowchart of the method's steps in the words its author\n// declared. A projection of the same GraphSpec the detailed style draws — no\n// new facts, and nothing guessed:\n//\n// - every operator is a step, titled by its pipe's description;\n// - the method's inputs and its final outputs are the only data drawn as nodes;\n// every other value becomes the arrows from the step that produced it to each\n// step that reads it;\n// - a condition is a decision whose arrows carry its outcomes;\n// - a batch runs \"for each\" item: its branch is drawn once, in a frame when it\n// has several steps, as one marked step when it has one;\n// - sequences and parallels have no frame: the arrows are the order, and\n// parallel branches are side by side because nothing joins them until a step\n// reads both;\n// - a binding step is plumbing: the arrow passes through it, unless it failed,\n// when it is a step, so a run that stopped there shows where;\n// - a folded controller is one step, saying how many it holds.\n//\n// Pure and React-free. It runs on the dataflow graph after the folds, so the\n// fold machinery stays the detailed style's, and its output feeds the same\n// ELK layout through each node's fixed `layoutSize`.\n\nimport type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphSpecNode,\n GraphSpecNodeIoItem,\n NodePipeType,\n PipeStatus,\n SimpleNodePayload,\n SimpleTitleFit,\n StepCategory,\n} from \"@graph/types\";\nimport {\n ARROW_CLOSED_MARKER,\n BINDING_STEP_TYPE,\n EDGE_TYPE_ROUTED,\n NODE_TYPE_PIPE_CARD,\n NODE_TYPE_SIMPLE_DECISION,\n NODE_TYPE_SIMPLE_FRAME,\n NODE_TYPE_SIMPLE_STEP,\n NODE_TYPE_SIMPLE_TERMINAL,\n STEP_CATEGORY,\n graphSpecMode,\n isPluralMultiplicity,\n stuffNodeId,\n} from \"@graph/types\";\nimport { getPipeBlueprint, resolveConceptRef } from \"@graph/graphAnalysis\";\nimport { makePipeRef, parsePipeRef } from \"@graph/pipeRefs\";\nimport { buildControllerNodes, sortParentsFirst } from \"@graph/graphControllers\";\nimport type { ControllerRect } from \"@graph/graphControllers\";\nimport {\n conceptPlainName,\n humanizeIdentifier,\n isGenericConcept,\n outcomeLabel,\n sentenceCase,\n stripDomain,\n} from \"./humanize\";\nimport { estimateWrap, textWidthPx, type WrapEstimate } from \"./textMetrics\";\n\n/** The domain of the concepts the standard defines, as a spec's concept registry records them. */\nconst NATIVE_DOMAIN = \"native\";\n\n// ─── Metrics (keep in sync with SimpleStyle.css) ────────────────────────────\n// The layout sizes every node before the DOM exists, so these mirror the\n// stylesheet: box widths, paddings, font sizes and line heights.\n\nexport const SIMPLE_STEP_WIDTH = 160;\n/**\n * The wider box a step takes when its title fits it whole at full size and\n * not the narrow one: one step of width, not a box per title, so a column of\n * steps keeps its rhythm.\n */\nexport const SIMPLE_STEP_WIDE_WIDTH = 200;\nconst STEP_PADDING_X = 12;\nconst STEP_PADDING_Y = 10;\nconst STEP_BORDER = 1.5;\nconst STEP_HEADER_HEIGHT = 16;\nconst STEP_GAP = 6;\n/**\n * The ways a step title may be set, in order of preference: a title takes the\n * first it fits whole in. Full size in the narrow box, then in the wide one,\n * then smaller type back in the narrow box, with more lines at each size down,\n * so a long description is read rather than cut. Smaller type stays narrow\n * because the drawing's width is what fit view scales by: a left-to-right\n * method of wide boxes would shrink every title on the canvas, not only the\n * long one. The last is the floor: a title too long even for it is set there\n * all the same and cut with an ellipsis, its full text in a tooltip and in the\n * detail panel. The drawing sets each title from the fit its payload carries,\n * so the stylesheet holds no copy of these.\n */\nexport const SIMPLE_STEP_TITLE_FITS: readonly SimpleTitleFit[] = [\n { boxWidth: SIMPLE_STEP_WIDTH, fontPx: 15, lineHeightPx: 20, maxLines: 3 },\n { boxWidth: SIMPLE_STEP_WIDE_WIDTH, fontPx: 15, lineHeightPx: 20, maxLines: 3 },\n { boxWidth: SIMPLE_STEP_WIDTH, fontPx: 13, lineHeightPx: 17, maxLines: 5 },\n { boxWidth: SIMPLE_STEP_WIDTH, fontPx: 12, lineHeightPx: 16, maxLines: 7 },\n];\nconst STEP_MARKER_HEIGHT = 18;\nconst STEP_INNER_HEIGHT = 22;\n\n// An arrow's label: an outcome of a decision.\nconst EDGE_LABEL_FONT_PX = 11;\nconst EDGE_LABEL_LINE_HEIGHT = 13;\nconst EDGE_LABEL_PADDING: readonly [number, number] = [5, 3];\n\nexport const SIMPLE_TERMINAL_WIDTH = 124;\nconst TERMINAL_PADDING_X = 12;\nconst TERMINAL_PADDING_TOP = 10;\n/** The wave at the foot of the document shape. */\nconst TERMINAL_PADDING_BOTTOM = 16;\nconst TERMINAL_TITLE_FONT_PX = 13;\nconst TERMINAL_TITLE_LINE_HEIGHT = 17;\nconst TERMINAL_TITLE_MAX_LINES = 2;\nconst TERMINAL_SUBTITLE_HEIGHT = 17;\nconst TERMINAL_MIN_HEIGHT = 52;\n\n/**\n * The widths a decision diamond may take, narrowest first: it takes the first\n * at which its text fits in `DECISION_TITLE_MAX_LINES`, so a long question\n * widens the diamond rather than losing its end.\n */\nexport const SIMPLE_DECISION_WIDTHS: readonly number[] = [188, 220, 252, 284];\nconst DECISION_TITLE_FONT_PX = 13;\nconst DECISION_TITLE_LINE_HEIGHT = 16;\nconst DECISION_TITLE_MAX_LINES = 3;\n/**\n * The share of a diamond's width its text may use. A centered rectangle fits\n * in a rhombus when its width and height shares sum to at most one, so the\n * text block's height share is what remains, minus a margin.\n */\nconst DECISION_TEXT_WIDTH_SHARE = 0.56;\nconst DECISION_TEXT_HEIGHT_SHARE = 0.4;\nconst DECISION_MIN_HEIGHT = 84;\n\n/**\n * Room kept free at the end of every line the layout wraps text for: the\n * browser breaks a line the moment it overflows, and a fraction of a pixel of\n * rounding must not cost a title its last line.\n */\nconst WRAP_SLACK_PX = 2;\n\n/** The width a step's title wraps at in a box of a width: inside its border and padding. */\nfunction stepTitleWidth(boxWidth: number): number {\n return boxWidth - 2 * (STEP_PADDING_X + STEP_BORDER) - WRAP_SLACK_PX;\n}\nconst TERMINAL_TITLE_WIDTH = SIMPLE_TERMINAL_WIDTH - 2 * TERMINAL_PADDING_X - WRAP_SLACK_PX;\n\nfunction decisionTextWidth(diamondWidth: number): number {\n return Math.floor(diamondWidth * DECISION_TEXT_WIDTH_SHARE) - WRAP_SLACK_PX;\n}\n\n/**\n * The controller depth from which the simple style folds a sub-method by\n * default (the method's own controller is depth 0). Deep nesting is where a\n * flowchart stops reading as one; a folded sub-method is one step a reader\n * can open. Only sequences fold this way: a decision, a loop or a parallel is\n * structure the reader came to see, however deep it sits.\n */\nexport const SIMPLE_FOLD_DEPTH = 3;\n\n// ─── Step categories ────────────────────────────────────────────────────────\n\n/**\n * The category each card class reads as. Exhaustive over `NodePipeType`, so a\n * new pipe class must say what it is. A binding step is drawn only when it\n * failed, as the field it was picking.\n */\nexport const STEP_CATEGORY_BY_PIPE_TYPE: Record<NodePipeType, StepCategory> = {\n PipeLLM: STEP_CATEGORY.AI,\n PipeStructure: STEP_CATEGORY.AI,\n PipeJudge: STEP_CATEGORY.JUDGE,\n PipeExtract: STEP_CATEGORY.EXTRACT,\n PipeImgGen: STEP_CATEGORY.IMAGE,\n PipeSearch: STEP_CATEGORY.SEARCH,\n PipeFunc: STEP_CATEGORY.CODE,\n PipeCompose: STEP_CATEGORY.TEMPLATE,\n PipeDocGen: STEP_CATEGORY.DOCUMENT,\n PipeSignature: STEP_CATEGORY.PLANNED,\n PipeSequence: STEP_CATEGORY.STEPS,\n PipeParallel: STEP_CATEGORY.PARALLEL,\n PipeCondition: STEP_CATEGORY.DECISION,\n PipeBatch: STEP_CATEGORY.REPEAT,\n BindingStep: STEP_CATEGORY.PICK,\n};\n\n/** The plain word each category is shown with. */\nexport const STEP_CATEGORY_WORDS: Record<StepCategory, string> = {\n ai: \"AI\",\n extract: \"Extract\",\n image: \"Image\",\n search: \"Search\",\n code: \"Code\",\n template: \"Template\",\n document: \"Document\",\n judge: \"Judge\",\n planned: \"To build\",\n steps: \"Steps\",\n parallel: \"Side by side\",\n decision: \"Decision\",\n repeat: \"For each\",\n pick: \"Pick\",\n};\n\n// ─── Sizes ──────────────────────────────────────────────────────────────────\n\n/**\n * The fit a step title is set at: the first of `SIMPLE_STEP_TITLE_FITS` it\n * fits whole in, or the floor, where it is cut.\n */\nexport function simpleTitleFit(title: string): { fit: SimpleTitleFit; wrap: WrapEstimate } {\n for (const fit of SIMPLE_STEP_TITLE_FITS) {\n const wrap = simpleTitleWrap(title, fit);\n if (!wrap.clamped) return { fit, wrap };\n }\n const floor = SIMPLE_STEP_TITLE_FITS[SIMPLE_STEP_TITLE_FITS.length - 1];\n return { fit: floor, wrap: simpleTitleWrap(title, floor) };\n}\n\n/** The size of a step box for its title's fit and its markers. */\nexport function simpleStepSize(\n title: string,\n options: { forEach?: boolean; innerSteps?: boolean } = {},\n): { width: number; height: number } {\n const { fit, wrap } = simpleTitleFit(title);\n let height =\n 2 * STEP_PADDING_Y +\n STEP_HEADER_HEIGHT +\n STEP_GAP +\n Math.max(1, wrap.drawnLines) * fit.lineHeightPx;\n if (options.forEach) height += STEP_GAP + STEP_MARKER_HEIGHT;\n if (options.innerSteps) height += STEP_GAP + STEP_INNER_HEIGHT;\n return { width: fit.boxWidth, height: Math.ceil(height + 2 * STEP_BORDER) };\n}\n\n/** The size of an input or output's document shape. */\nexport function simpleTerminalSize(\n title: string,\n hasSubtitle: boolean,\n): { width: number; height: number } {\n const wrap = estimateWrap(\n title,\n TERMINAL_TITLE_WIDTH,\n TERMINAL_TITLE_FONT_PX,\n TERMINAL_TITLE_MAX_LINES,\n );\n const height =\n TERMINAL_PADDING_TOP +\n Math.max(1, wrap.drawnLines) * TERMINAL_TITLE_LINE_HEIGHT +\n (hasSubtitle ? TERMINAL_SUBTITLE_HEIGHT : 0) +\n TERMINAL_PADDING_BOTTOM;\n return { width: SIMPLE_TERMINAL_WIDTH, height: Math.max(TERMINAL_MIN_HEIGHT, height) };\n}\n\n/**\n * The size of a decision diamond for its text: the narrowest of\n * `SIMPLE_DECISION_WIDTHS` its text fits in, and the height that keeps the\n * text block inside the rhombus.\n */\nexport function simpleDecisionSize(title: string): { width: number; height: number } {\n let width = SIMPLE_DECISION_WIDTHS[0];\n let wrap = estimateWrap(\n title,\n decisionTextWidth(width),\n DECISION_TITLE_FONT_PX,\n DECISION_TITLE_MAX_LINES,\n );\n for (const candidate of SIMPLE_DECISION_WIDTHS.slice(1)) {\n if (!wrap.clamped) break;\n width = candidate;\n wrap = estimateWrap(\n title,\n decisionTextWidth(width),\n DECISION_TITLE_FONT_PX,\n DECISION_TITLE_MAX_LINES,\n );\n }\n const textHeight = Math.max(1, wrap.drawnLines) * DECISION_TITLE_LINE_HEIGHT;\n const height = Math.max(DECISION_MIN_HEIGHT, Math.ceil(textHeight / DECISION_TEXT_HEIGHT_SHARE));\n return { width, height };\n}\n\n/**\n * How a step title wraps at a fit: what the readability rubric checks, at the\n * fit the step is drawn at.\n */\nexport function simpleTitleWrap(title: string, fit: SimpleTitleFit): WrapEstimate {\n return estimateWrap(title, stepTitleWidth(fit.boxWidth), fit.fontPx, fit.maxLines);\n}\n\n// ─── Status ─────────────────────────────────────────────────────────────────\n\n/** Worst first: the status a set of runs of one step shows. */\nconst STATUS_PRECEDENCE: readonly PipeStatus[] = [\n \"failed\",\n \"running\",\n \"scheduled\",\n \"canceled\",\n \"skipped\",\n \"succeeded\",\n];\n\n/** The status a step drawn once stands for when it ran several times (once per batch item). */\nexport function aggregateStatus(statuses: readonly PipeStatus[]): PipeStatus | undefined {\n for (const status of STATUS_PRECEDENCE) {\n if (statuses.includes(status)) return status;\n }\n return undefined;\n}\n\n// ─── The projection ─────────────────────────────────────────────────────────\n\nexport interface SimpleProjectionInput {\n graphspec: GraphSpec;\n /** The dataflow graph after the folds: what the detailed style would lay out. */\n nodes: GraphNode[];\n /** The analysis after the folds (a folded controller is one producer and consumer). */\n analysis: DataflowAnalysis;\n /** The analysis before the folds: the full containment, which a fold hides. */\n rawAnalysis: DataflowAnalysis;\n}\n\nexport interface SimpleProjection {\n nodes: GraphNode[];\n edges: GraphEdge[];\n /**\n * The analysis the layout and the frames read: the \"for each\" frames are its\n * only controllers, holding the nodes drawn inside them, and it names no\n * producer or consumer, since every value the style draws is a method input\n * or a final output and sits outside every frame.\n */\n analysis: DataflowAnalysis;\n /** Each spec node the style does not draw, mapped to the drawn node that stands for it. */\n standIns: ReadonlyMap<string, string>;\n}\n\nfunction parentMap(\n containmentTree: Readonly<Record<string, readonly string[]>>,\n): Record<string, string> {\n const parentOf: Record<string, string> = {};\n for (const [ctrlId, children] of Object.entries(containmentTree)) {\n for (const child of children) parentOf[child] = ctrlId;\n }\n return parentOf;\n}\n\nfunction unique<T>(items: readonly T[]): T[] {\n return [...new Set(items)];\n}\n\n/**\n * Which node of a run stands in for which: a batch runs its branch once per\n * item, and the flowchart draws each step of it once. Two runs are the same\n * step when the same pipes lead to them from the top of the method, a pipe\n * known by its full reference so that two domains' pipes of one code stay two\n * steps, siblings outside a batch told apart by their order among calls to the\n * same pipe and the items of a batch not told apart at all. The run that shows\n * the step best stands in for the others, the first in containment order among\n * equals, so a first item that skipped a step, leaving it nothing to show, or\n * whose loop ran over an empty list, does not hide the items that ran it. A\n * static spec draws each batch's branch once, so nothing in it stands in for\n * anything.\n *\n * `drawability` ranks a run: 0 when it cannot be drawn, higher the more of the\n * step it shows. `representativeOf` holds only the runs another stands in for;\n * `order` is every node in containment order, parents first.\n */\nfunction batchRepresentatives(\n graphspec: GraphSpec,\n analysis: DataflowAnalysis,\n drawability: (id: string) => number,\n): { representativeOf: Map<string, string>; order: string[] } {\n const specById = new Map(graphspec.nodes.map((n) => [n.id, n]));\n const runsWithPath = new Map<string, string[]>();\n const representativeOf = new Map<string, string>();\n const order: string[] = [];\n\n function visit(ids: readonly string[], parentPath: string, inBatch: boolean): void {\n const seen = new Map<string, number>();\n for (const id of ids) {\n const spec = specById.get(id);\n const pipe = spec ? stepIdentity(spec) : id;\n const occurrence = seen.get(pipe) ?? 0;\n seen.set(pipe, occurrence + 1);\n const path = `${parentPath}/${pipe}#${inBatch ? \"*\" : occurrence}`;\n const runs = runsWithPath.get(path);\n if (runs) runs.push(id);\n else runsWithPath.set(path, [id]);\n order.push(id);\n visit(analysis.containmentTree[id] ?? [], path, spec?.pipe_type === \"PipeBatch\");\n }\n }\n visit(\n graphspec.nodes.filter((n) => !analysis.childNodeIds.has(n.id)).map((n) => n.id),\n \"\",\n false,\n );\n for (const runs of runsWithPath.values()) {\n if (runs.length < 2) continue;\n const best = Math.max(...runs.map(drawability));\n const representative = runs.find((id) => drawability(id) === best) ?? runs[0];\n for (const id of runs) if (id !== representative) representativeOf.set(id, representative);\n }\n return { representativeOf, order };\n}\n\n/**\n * What makes two runs the same step: the node's kind and the pipe it calls, by\n * its full reference where the spec gives a domain, or a binding's path.\n */\nfunction stepIdentity(spec: GraphSpecNode): string {\n const ref =\n spec.domain_code && spec.pipe_code\n ? makePipeRef(spec.domain_code, spec.pipe_code)\n : (spec.pipe_code ?? spec.pipe_type);\n return `${spec.kind}:${ref}`;\n}\n\n/** \"For each page\": an item name inside a sentence, keeping an acronym's capitals. */\nfunction forEachLabel(itemName: string): string {\n const words = humanizeIdentifier(itemName);\n const first = words.split(\" \")[0] ?? \"\";\n const inSentence =\n first.length >= 2 && first === first.toUpperCase()\n ? words\n : words.charAt(0).toLowerCase() + words.slice(1);\n return `For each ${inSentence}`;\n}\n\n/**\n * Project the folded dataflow graph into the simple style's nodes and edges.\n * Node ids stay the GraphSpec's — a step keeps its pipe node's id and a method\n * input or final output keeps its stuff node's — so selection, the detail\n * panel, run status and validation decorations find them as in any style.\n */\nexport function projectSimpleGraph(input: SimpleProjectionInput): SimpleProjection {\n const { graphspec, nodes, analysis, rawAnalysis } = input;\n const graphMode = graphSpecMode(graphspec);\n const specById = new Map<string, GraphSpecNode>(graphspec.nodes.map((n) => [n.id, n]));\n\n const cards = new Map<string, GraphNode>();\n for (const node of nodes) {\n if (node.type === NODE_TYPE_PIPE_CARD) cards.set(node.id, node);\n }\n const liveControllers = analysis.controllerNodeIds;\n const pipeTypeOf = (id: string): string | undefined => specById.get(id)?.pipe_type;\n\n // ── A run draws a batch's branch once per item; the flowchart draws it once.\n // Every run of a step stands in for the run of the same step that shows it\n // best, and only that run is drawn. A step only a later item reached, such as\n // the branch of a decision the first item did not take, is drawn too, where\n // its first run sits. A run shows its step fully when it is drawn with what\n // it holds: an operator, a folded step, or a controller the folds left open.\n // A controller run that held nothing, such as a loop over an empty list, is\n // drawn empty, so it stands in only where no run held anything. A controller\n // hidden inside a fold cannot be drawn at all.\n const { representativeOf, order } = batchRepresentatives(graphspec, rawAnalysis, (id) => {\n if (liveControllers.has(id)) return 2;\n const card = cards.get(id);\n if (!card) return 0;\n return card.data.isController !== true && specById.get(id)?.kind === \"controller\" ? 1 : 2;\n });\n const repOf = (id: string): string => representativeOf.get(id) ?? id;\n // The containment the flowchart draws: a later run's own steps hang from the\n // run drawn for what holds them.\n const parentOf: Record<string, string> = {};\n for (const [id, parent] of Object.entries(parentMap(rawAnalysis.containmentTree))) {\n parentOf[id] = repOf(parent);\n }\n // A fold hides what it holds in every item. Where the run drawn for a step is\n // folded and a later item's run of it is not, as after a fold of that run\n // alone, the later run's steps hang from the folded step and are drawn as it.\n const foldedAbove = new Map<string, string>();\n for (const id of order) {\n for (let current = parentOf[id]; current; current = parentOf[current]) {\n if (cards.get(current)?.data.isController === true) {\n foldedAbove.set(id, current);\n break;\n }\n }\n }\n /** The drawn node a run of the spec is drawn as. */\n const drawnAs = (id: string): string => foldedAbove.get(id) ?? repOf(id);\n const hidden = new Set([...representativeOf.keys(), ...foldedAbove.keys()]);\n const childrenOf: Record<string, string[]> = {};\n for (const id of order) {\n const parent = parentOf[id];\n if (parent && !hidden.has(id)) (childrenOf[parent] ??= []).push(id);\n }\n\n function isInside(id: string, ancestorId: string): boolean {\n let current = parentOf[id];\n while (current) {\n if (current === ancestorId) return true;\n current = parentOf[current];\n }\n return false;\n }\n\n // A batch's branch is the run drawn for its first item's: a later item's\n // when the first one's has nothing to show.\n const batchBranch: Record<string, string> = {};\n for (const ctrlId of liveControllers) {\n if (hidden.has(ctrlId) || pipeTypeOf(ctrlId) !== \"PipeBatch\") continue;\n const first = analysis.containmentTree[ctrlId]?.[0];\n if (first) batchBranch[ctrlId] = repOf(first);\n }\n\n const decisions = [...liveControllers].filter(\n (id) => !hidden.has(id) && pipeTypeOf(id) === \"PipeCondition\",\n );\n const decisionSet = new Set(decisions);\n // A batch whose branch is a sub-method gets a frame; one whose branch is a\n // single step marks that step instead.\n const frameSet = new Set(\n Object.entries(batchBranch)\n .filter(([batchId, branchId]) => !hidden.has(batchId) && liveControllers.has(branchId))\n .map(([batchId]) => batchId),\n );\n const markedSteps: Record<string, string> = {};\n for (const [batchId, branchId] of Object.entries(batchBranch)) {\n if (!hidden.has(batchId) && cards.has(branchId)) markedSteps[branchId] = batchId;\n }\n\n const runStatuses = new Map<string, PipeStatus[]>();\n for (const spec of graphspec.nodes) {\n const rep = repOf(spec.id);\n const statuses = runStatuses.get(rep);\n if (statuses) statuses.push(spec.status);\n else runStatuses.set(rep, [spec.status]);\n }\n\n /** A node's status; one drawn once for every batch item stands for all of its runs. */\n function stepStatus(spec: GraphSpecNode): PipeStatus {\n return aggregateStatus(runStatuses.get(spec.id) ?? []) ?? spec.status;\n }\n\n // A binding step is plumbing, drawn only when it failed: the run stopped\n // there, and the step is where a reader finds the error.\n const drawnSteps = [...cards.values()].filter(\n (card) =>\n !hidden.has(card.id) &&\n (card.data.pipeType !== BINDING_STEP_TYPE ||\n aggregateStatus(runStatuses.get(card.id) ?? []) === \"failed\"),\n );\n const drawnStepIds = new Set(drawnSteps.map((card) => card.id));\n\n // ── The method's inputs and final outputs: the root pipes' io. Where a spec\n // has several roots, a value one root writes and another reads passes\n // between them, and is neither.\n const roots = graphspec.nodes.filter(\n (n) =>\n !rawAnalysis.childNodeIds.has(n.id) && (n.kind === \"controller\" || n.kind === \"operator\"),\n );\n const rootReads = new Set(roots.flatMap((r) => r.io.inputs.map((item) => item.digest)));\n const rootWrites = new Set(roots.flatMap((r) => r.io.outputs.map((item) => item.digest)));\n const inputItems = new Map<string, GraphSpecNodeIoItem>();\n const outputItems = new Map<string, GraphSpecNodeIoItem>();\n for (const root of roots) {\n for (const item of root.io.inputs) {\n if (!item.digest || inputItems.has(item.digest) || rootWrites.has(item.digest)) continue;\n inputItems.set(item.digest, item);\n }\n }\n for (const root of roots) {\n for (const item of root.io.outputs) {\n if (!item.digest || inputItems.has(item.digest) || outputItems.has(item.digest)) continue;\n if (rootReads.has(item.digest)) continue;\n outputItems.set(item.digest, item);\n }\n }\n\n // ── Where each value comes from, among the nodes drawn.\n // A value no step writes is combined from others: a parallel's combined\n // output, a batch's list of results, or the item a batch hands one branch.\n // Indexed once, since a batch run has one such value per item.\n const combinedFrom = new Map<string, string[]>();\n for (const edge of graphspec.edges) {\n const from = edge.source_stuff_digest;\n const into = edge.target_stuff_digest;\n if (!from || !into || from === into) continue;\n if (\n edge.kind !== \"parallel_combine\" &&\n edge.kind !== \"batch_aggregate\" &&\n edge.kind !== \"batch_item\"\n ) {\n continue;\n }\n const list = combinedFrom.get(into);\n if (list) list.push(from);\n else combinedFrom.set(into, [from]);\n }\n const sourceMemo = new Map<string, string[]>();\n function sourcesOf(digest: string, visiting: Set<string> = new Set()): string[] {\n const memo = sourceMemo.get(digest);\n if (memo) return memo;\n if (inputItems.has(digest)) return [stuffNodeId(digest)];\n if (visiting.has(digest)) return [];\n visiting.add(digest);\n const found: string[] = [];\n const producers = analysis.stuffProducers[digest] ?? [];\n for (const producerId of producers) {\n const card = cards.get(drawnAs(producerId));\n if (!card) continue;\n if (card.data.pipeType === BINDING_STEP_TYPE && !drawnStepIds.has(card.id)) {\n // A binding passes its root through under a new name.\n for (const item of specById.get(producerId)?.io.inputs ?? []) {\n if (item.digest) found.push(...sourcesOf(item.digest, visiting));\n }\n } else {\n found.push(card.id);\n }\n }\n if (producers.length === 0) {\n for (const from of combinedFrom.get(digest) ?? []) found.push(...sourcesOf(from, visiting));\n }\n visiting.delete(digest);\n const result = unique(found);\n sourceMemo.set(digest, result);\n return result;\n }\n\n // ── Decisions: the outcome each of their branches is taken on.\n function branchOf(decisionId: string, nodeId: string): string {\n let current = nodeId;\n while (parentOf[current] && parentOf[current] !== decisionId) current = parentOf[current];\n return current;\n }\n\n function outcomeFor(decisionId: string, branchId: string): string | undefined {\n const branch = specById.get(branchId);\n const tagged = branch?.tags?.outcome;\n if (tagged) return outcomeLabel(tagged);\n const contains = graphspec.edges.find(\n (e) =>\n e.kind === \"contains\" && repOf(e.source) === decisionId && e.target === branchId && e.label,\n );\n if (contains?.label) return outcomeLabel(contains.label);\n const decision = specById.get(decisionId);\n if (!decision?.domain_code || !decision.pipe_code || !branch?.pipe_code) return undefined;\n const blueprint = getPipeBlueprint(\n graphspec,\n makePipeRef(decision.domain_code, decision.pipe_code),\n );\n if (blueprint?.type !== \"PipeCondition\") return undefined;\n // An outcome is the branch's when it names the branch's pipe: its code, and\n // its domain, which a bare reference takes from the condition's own.\n const conditionDomain = decision.domain_code;\n const branchCode = branch.pipe_code;\n function namesBranch(pipeRef: string): boolean {\n const parsed = parsePipeRef(pipeRef);\n if (!parsed) return stripDomain(pipeRef) === branchCode;\n if (parsed.pipeCode !== branchCode) return false;\n const domain = parsed.domainPath ?? conditionDomain;\n return branch?.domain_code === undefined || branch.domain_code === domain;\n }\n const outcomes = Object.entries(blueprint.outcome_map)\n .filter(([, pipeRef]) => namesBranch(pipeRef))\n .map(([outcome]) => outcome);\n if (blueprint.default_outcome && namesBranch(blueprint.default_outcome)) {\n outcomes.push(\"default\");\n }\n return outcomes.length > 0 ? outcomeLabel(outcomes.join(\"|\")) : undefined;\n }\n\n function decisionsAbove(nodeId: string): string[] {\n const chain: string[] = [];\n let current = parentOf[nodeId];\n while (current) {\n if (decisionSet.has(current)) chain.unshift(current);\n current = parentOf[current];\n }\n return chain;\n }\n\n // ── Edges.\n const edges = new Map<string, GraphEdge>();\n let edgeCount = 0;\n function addEdge(source: string, target: string): void {\n if (source === target) return;\n const key = `${source}->${target}`;\n if (edges.has(key)) return;\n const label = decisionSet.has(source)\n ? outcomeFor(source, branchOf(source, target))\n : undefined;\n edges.set(key, simpleEdge(`simple_edge_${edgeCount++}`, source, target, label));\n }\n\n /**\n * Draw the arrows that bring a value from its sources to a node. Where the\n * node sits in a decision's branch and the value comes from outside that\n * decision, the value goes to the decision, and the decision's arrow, labelled\n * with the branch's outcome, goes on — decision by decision, outermost first.\n */\n function route(sources: readonly string[], target: string): void {\n let froms = [...sources];\n for (const decisionId of decisionsAbove(target)) {\n const outside = froms.filter((s) => !isInside(s, decisionId));\n if (outside.length === 0) continue;\n for (const source of outside) addEdge(source, decisionId);\n froms = [decisionId, ...froms.filter((s) => isInside(s, decisionId))];\n }\n for (const source of froms) addEdge(source, target);\n }\n\n const danglingInputs = new Map<string, GraphSpecNodeIoItem>();\n function sourcesOrDangling(digest: string): string[] {\n const sources = sourcesOf(digest);\n if (sources.length > 0) return sources;\n // A value read with no step drawn behind it is shown as an input of its\n // own rather than left hanging.\n const entry = analysis.stuffRegistry[digest];\n if (!entry) return [];\n danglingInputs.set(digest, {\n name: entry.name,\n digest,\n ...(entry.concept !== undefined ? { concept: entry.concept } : {}),\n ...(entry.multiplicity !== undefined ? { multiplicity: entry.multiplicity } : {}),\n });\n return [stuffNodeId(digest)];\n }\n\n // A later run reads its own item's values, which come from the same steps as\n // the first run's, so it only adds an arrow where its item went another way.\n // A value with nothing drawn behind it is shown once, for the first run.\n function routeRead(digest: string, readerId: string): void {\n const target = drawnAs(readerId);\n if (target === readerId) route(sourcesOrDangling(digest), target);\n else route(sourcesOf(digest), target);\n }\n for (const [digest, consumers] of Object.entries(analysis.stuffConsumers)) {\n for (const consumerId of consumers) {\n if (drawnStepIds.has(drawnAs(consumerId))) routeRead(digest, consumerId);\n }\n }\n for (const conditionId of liveControllers) {\n if (!decisionSet.has(drawnAs(conditionId))) continue;\n for (const item of specById.get(conditionId)?.io.inputs ?? []) {\n if (item.digest) routeRead(item.digest, conditionId);\n }\n }\n for (const digest of outputItems.keys()) {\n for (const source of sourcesOf(digest)) addEdge(source, stuffNodeId(digest));\n }\n\n // A decision's arrow goes to the first steps of a branch only. A later step\n // of the branch that also reads a value from before the decision is reached\n // through the steps before it, so a second arrow with the same outcome would\n // say the branch is taken twice.\n for (const [key, edge] of [...edges]) {\n if (!decisionSet.has(edge.source)) continue;\n const branchId = branchOf(edge.source, edge.target);\n const followsInBranch = [...edges.values()].some(\n (other) =>\n other.target === edge.target &&\n other.source !== edge.source &&\n isInside(other.source, branchId),\n );\n if (followsInBranch) edges.delete(key);\n }\n\n // Every branch a decision can take gets its arrow, even one that reads\n // nothing from before the decision.\n function firstDrawnIn(id: string): string | undefined {\n if (drawnStepIds.has(id) || decisionSet.has(id)) return id;\n for (const child of childrenOf[id] ?? []) {\n const found = firstDrawnIn(child);\n if (found) return found;\n }\n return undefined;\n }\n for (const decisionId of decisions) {\n for (const branchId of childrenOf[decisionId] ?? []) {\n const reached = [...edges.values()].some(\n (e) => e.source === decisionId && (e.target === branchId || isInside(e.target, branchId)),\n );\n const entry = reached ? undefined : firstDrawnIn(branchId);\n if (entry) addEdge(decisionId, entry);\n }\n }\n\n // ── Nodes, in the spec's order: inputs, then steps and decisions, then outputs.\n const simpleNodes: GraphNode[] = [];\n\n /**\n * Whether a concept is one the standard defines (`Text`, `Image`, …), as the\n * spec's concept registry records it, which says what kind of value\n * something is without saying what it is.\n */\n function isBuiltInConcept(conceptRef: string): boolean {\n const info = resolveConceptRef(graphspec, conceptRef);\n if (info) return info.domain_code === NATIVE_DOMAIN;\n return conceptRef.startsWith(`${NATIVE_DOMAIN}.`) || isGenericConcept(conceptRef);\n }\n\n /**\n * A method input is named by its variable, with its concept's plain name\n * beneath when that says more. The final output is named by its concept when\n * the author defined it (\"Bid summary\"), and like an input when the concept\n * is built in, since a shape reading \"Text\" says nothing.\n */\n function terminalNode(item: GraphSpecNodeIoItem, kind: \"input\" | \"output\"): GraphNode {\n const digest = item.digest as string;\n const concept = item.concept;\n const isList = isPluralMultiplicity(item.multiplicity);\n let title: string;\n let subtitle: string | undefined;\n if (kind === \"output\" && concept && !isBuiltInConcept(concept)) {\n title = conceptPlainName(concept);\n } else {\n title = humanizeIdentifier(item.name);\n const plain = concept ? conceptPlainName(concept) : undefined;\n subtitle = plain && plain.toLowerCase() !== title.toLowerCase() ? plain : undefined;\n }\n const simple: SimpleNodePayload = { kind, title, isList, ...(subtitle ? { subtitle } : {}) };\n return {\n id: stuffNodeId(digest),\n type: NODE_TYPE_SIMPLE_TERMINAL,\n data: {\n isPipe: false,\n isStuff: true,\n labelText: title,\n stuffRole: kind,\n stuffDigest: digest,\n graphMode,\n simple,\n layoutSize: simpleTerminalSize(title, subtitle !== undefined),\n },\n position: { x: 0, y: 0 },\n };\n }\n\n for (const item of [...inputItems.values(), ...danglingInputs.values()]) {\n simpleNodes.push(terminalNode(item, \"input\"));\n }\n\n /**\n * The name of the item a batch hands its branch: the blueprint's, unless it\n * is unwritten yet, else the name the branch reads its item under, else the\n * item's own.\n */\n function itemName(batchId: string): string | undefined {\n const batch = specById.get(batchId);\n if (batch?.domain_code && batch.pipe_code) {\n const blueprint = getPipeBlueprint(\n graphspec,\n makePipeRef(batch.domain_code, batch.pipe_code),\n );\n if (blueprint?.type === \"PipeBatch\" && blueprint.batch_params.input_item_stuff_name) {\n return blueprint.batch_params.input_item_stuff_name;\n }\n }\n // The branch drawn may be a later item's run, so the items of every run of\n // the batch are looked for, not only the first run's.\n const runsOfBatch = new Set([batchId]);\n for (const [id, representative] of representativeOf) {\n if (representative === batchId) runsOfBatch.add(id);\n }\n const itemEdges = graphspec.edges.filter(\n (e) => e.kind === \"batch_item\" && runsOfBatch.has(e.source) && e.target_stuff_digest,\n );\n const itemDigests = new Set(itemEdges.map((e) => e.target_stuff_digest));\n const branchId = batchBranch[batchId] ?? rawAnalysis.containmentTree[batchId]?.[0];\n const branch = branchId ? specById.get(branchId) : undefined;\n const read = branch?.io.inputs.find((i) => itemDigests.has(i.digest));\n if (read?.name) return read.name;\n const firstItem = itemEdges.find((e) => e.source === batchId) ?? itemEdges[0];\n return firstItem?.target_stuff_digest\n ? analysis.stuffRegistry[firstItem.target_stuff_digest]?.name || undefined\n : undefined;\n }\n\n function stepsInside(id: string): number {\n const spec = specById.get(id);\n const children = rawAnalysis.containmentTree[id] ?? [];\n if (children.length === 0) return spec && spec.kind === \"operator\" ? 1 : 0;\n const counted = spec?.pipe_type === \"PipeBatch\" ? children.slice(0, 1) : children;\n return counted.reduce((sum, child) => sum + stepsInside(child), 0);\n }\n\n /**\n * What a step box says: its description, or for a folded batch, its\n * branch's. A binding step, drawn only when it failed, names the field it was\n * picking, the last segment of its path, since its description is the\n * runtime's own sentence about names.\n */\n function stepTitle(spec: GraphSpecNode, folded: boolean): string {\n if (spec.pipe_type === BINDING_STEP_TYPE) {\n return humanizeIdentifier(spec.pipe_code?.split(\".\").pop() ?? \"\");\n }\n let source = spec;\n if (folded && spec.pipe_type === \"PipeBatch\") {\n const branch = specById.get(rawAnalysis.containmentTree[spec.id]?.[0] ?? \"\");\n if (branch) source = branch;\n }\n const described = sentenceCase(source.description ?? \"\");\n return described || humanizeIdentifier(source.pipe_code ?? \"\");\n }\n\n const stepById = new Map(drawnSteps.map((card) => [card.id, card]));\n for (const spec of graphspec.nodes) {\n const card = stepById.get(spec.id);\n if (card) {\n const folded = card.data.isController === true;\n const pipeType: NodePipeType = spec.pipe_type;\n const title = stepTitle(spec, folded);\n const markerBatch =\n markedSteps[spec.id] ?? (folded && pipeType === \"PipeBatch\" ? spec.id : undefined);\n const markerItem = markerBatch ? itemName(markerBatch) : undefined;\n const forEach = markerBatch ? forEachLabel(markerItem || \"item\") : undefined;\n const innerStepCount = folded ? stepsInside(spec.id) : undefined;\n const onExpand = card.data.pipeCardData?.onExpand;\n const simple: SimpleNodePayload = {\n kind: \"step\",\n title,\n titleFit: simpleTitleFit(title).fit,\n category: STEP_CATEGORY_BY_PIPE_TYPE[pipeType],\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n ...(forEach ? { forEach } : {}),\n ...(innerStepCount !== undefined ? { innerStepCount } : {}),\n ...(onExpand ? { onExpand } : {}),\n };\n simpleNodes.push({\n id: spec.id,\n type: NODE_TYPE_SIMPLE_STEP,\n data: {\n nodeData: spec,\n isPipe: !folded,\n isStuff: false,\n isController: folded,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple,\n layoutSize: simpleStepSize(title, {\n forEach: forEach !== undefined,\n innerSteps: innerStepCount !== undefined,\n }),\n },\n position: { x: 0, y: 0 },\n });\n continue;\n }\n if (decisionSet.has(spec.id)) {\n const title =\n sentenceCase(spec.description ?? \"\") || humanizeIdentifier(spec.pipe_code ?? \"\");\n simpleNodes.push({\n id: spec.id,\n type: NODE_TYPE_SIMPLE_DECISION,\n data: {\n nodeData: spec,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple: {\n kind: \"decision\",\n title,\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n },\n layoutSize: simpleDecisionSize(title),\n },\n position: { x: 0, y: 0 },\n });\n }\n }\n\n for (const item of outputItems.values()) {\n simpleNodes.push(terminalNode(item, \"output\"));\n }\n\n // ── The frames: each \"for each\" batch holds the nodes drawn in its branch.\n const nearestFrame = (id: string): string | undefined => {\n let current = parentOf[id];\n while (current) {\n if (frameSet.has(current)) return current;\n current = parentOf[current];\n }\n return undefined;\n };\n const containmentTree: Record<string, string[]> = {};\n for (const frameId of frameSet) containmentTree[frameId] = [];\n for (const node of simpleNodes) {\n const frameId = node.data.isStuff ? undefined : nearestFrame(node.id);\n if (frameId) containmentTree[frameId].push(node.id);\n }\n for (const frameId of frameSet) {\n const outer = nearestFrame(frameId);\n if (outer) containmentTree[outer].push(frameId);\n }\n const childNodeIds = new Set(Object.values(containmentTree).flat());\n\n // Each frame is carried as a placeholder holding what it says, which the\n // frame pass turns into the positioned group once the layout has sized it.\n for (const frameId of frameSet) {\n const spec = specById.get(frameId);\n if (!spec) continue;\n const title = forEachLabel(itemName(frameId) || \"item\");\n simpleNodes.push({\n id: frameId,\n type: NODE_TYPE_SIMPLE_FRAME,\n data: {\n nodeData: spec,\n isPipe: false,\n isStuff: false,\n isController: true,\n labelText: title,\n pipeCode: spec.pipe_code,\n pipeType: spec.pipe_type,\n graphMode,\n simple: {\n kind: \"frame\",\n title,\n status: stepStatus(spec),\n ...(graphMode !== undefined ? { graphMode } : {}),\n },\n },\n position: { x: 0, y: 0 },\n });\n }\n\n // ── What stands for a node the flowchart does not draw, so that a validation\n // issue pinned to it still badges a drawn node: a later item's run stands for\n // the run drawn, or for the folded step it is drawn as, a batch of one step\n // for its marked step, a binding step for\n // the first drawn node that reads its result (or, when nothing does, for what\n // stands for the sub-method holding it), and a sub-method drawn as its steps\n // for the first of them.\n const drawnIds = new Set(simpleNodes.map((n) => n.id));\n const standIns = new Map<string, string>();\n const resolving = new Set<string>();\n function standIn(id: string): string | undefined {\n if (drawnIds.has(id)) return id;\n if (standIns.has(id)) return standIns.get(id);\n if (resolving.has(id)) return undefined;\n resolving.add(id);\n let found: string | undefined;\n const representative = foldedAbove.get(id) ?? representativeOf.get(id);\n const branch = batchBranch[id];\n if (representative) {\n found = standIn(representative);\n } else if (branch && markedSteps[branch] === id) {\n found = standIn(branch);\n } else if (pipeTypeOf(id) === BINDING_STEP_TYPE) {\n for (const item of specById.get(id)?.io.outputs ?? []) {\n if (!item.digest) continue;\n for (const reader of rawAnalysis.stuffConsumers[item.digest] ?? []) {\n found ??= standIn(reader);\n }\n if (!found && drawnIds.has(stuffNodeId(item.digest))) found = stuffNodeId(item.digest);\n if (found) break;\n }\n // A result nothing reads: what stands for the sub-method holding it.\n const parent = parentOf[id];\n if (!found && parent) found = standIn(parent);\n } else {\n for (const child of childrenOf[id] ?? []) {\n found = standIn(child);\n if (found) break;\n }\n }\n resolving.delete(id);\n if (found) standIns.set(id, found);\n return found;\n }\n for (const spec of graphspec.nodes) standIn(spec.id);\n\n return {\n nodes: simpleNodes,\n edges: [...edges.values()],\n standIns,\n analysis: {\n stuffRegistry: analysis.stuffRegistry,\n stuffProducers: {},\n stuffConsumers: {},\n controllerNodeIds: frameSet,\n childNodeIds,\n containmentTree,\n },\n };\n}\n\n/**\n * The size an arrow label is drawn at, background included, so the layout\n * reserves room for it: the text at `EDGE_LABEL_FONT_PX`, padded by\n * `EDGE_LABEL_PADDING`.\n */\nexport function simpleEdgeLabelSize(label: string): { width: number; height: number } {\n const [padX, padY] = EDGE_LABEL_PADDING;\n return {\n width: Math.ceil(textWidthPx(label, EDGE_LABEL_FONT_PX)) + 2 * padX,\n height: EDGE_LABEL_LINE_HEIGHT + 2 * padY,\n };\n}\n\n// The simple style draws every arrow along the layout's route (`EDGE_TYPE_ROUTED`),\n// whatever curve the host picked for the detailed style: a flowchart's arrows\n// go around the steps between their ends, never through them.\nfunction simpleEdge(\n id: string,\n source: string,\n target: string,\n label: string | undefined,\n): GraphEdge {\n const edge: GraphEdge = {\n id,\n source,\n target,\n type: EDGE_TYPE_ROUTED,\n animated: false,\n style: { stroke: \"var(--color-edge)\", strokeWidth: 1.5 },\n markerEnd: { type: ARROW_CLOSED_MARKER, color: \"var(--color-edge)\" },\n };\n if (label) {\n edge.label = label;\n edge.labelStyle = {\n fontSize: `${EDGE_LABEL_FONT_PX}px`,\n fontFamily: \"var(--font-sans)\",\n fontWeight: 600,\n fill: \"var(--ctrl-condition-text)\",\n };\n edge.labelBgStyle = { fill: \"var(--color-bg)\", fillOpacity: 0.92 };\n edge.labelBgPadding = [...EDGE_LABEL_PADDING];\n edge.labelBgBorderRadius = 4;\n edge.labelSize = simpleEdgeLabelSize(label);\n }\n return edge;\n}\n\n/**\n * The controllers the simple style folds when a graph is first drawn: every\n * sub-method (a sequence) from `SIMPLE_FOLD_DEPTH` levels down. The host's own fold mode\n * still applies on top.\n */\nexport function simpleDefaultFolds(\n graphspec: GraphSpec,\n rawAnalysis: DataflowAnalysis,\n): Set<string> {\n const parentOf = parentMap(rawAnalysis.containmentTree);\n const sequenceIds = new Set(\n graphspec.nodes.filter((n) => n.pipe_type === \"PipeSequence\").map((n) => n.id),\n );\n const folded = new Set<string>();\n for (const ctrlId of rawAnalysis.controllerNodeIds) {\n let depth = 0;\n let current = parentOf[ctrlId];\n while (current) {\n depth += 1;\n current = parentOf[current];\n }\n if (depth >= SIMPLE_FOLD_DEPTH && sequenceIds.has(ctrlId)) folded.add(ctrlId);\n }\n return folded;\n}\n\n/**\n * Wrap the laid-out nodes in the simple style's \"for each\" frames: the group\n * nodes are built by the same pass that builds the detailed style's controller\n * groups, at the positions and sizes the layout gave them, and take what they\n * say from the projection's placeholders. Mutates the content nodes' `parentId`\n * and positions as that pass does, so callers pass a copy of the layout.\n */\nexport function applySimpleFrames(args: {\n nodes: GraphNode[];\n edges: GraphEdge[];\n graphspec: GraphSpec;\n analysis: DataflowAnalysis;\n controllerPositions?: Record<string, ControllerRect>;\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void;\n}): { nodes: GraphNode[]; edges: GraphEdge[] } {\n const { nodes, edges, graphspec, analysis, controllerPositions, onToggleFold } = args;\n const placeholders = new Map<string, GraphNode>();\n const content: GraphNode[] = [];\n for (const node of nodes) {\n if (node.type === NODE_TYPE_SIMPLE_FRAME) placeholders.set(node.id, node);\n else content.push(node);\n }\n if (analysis.controllerNodeIds.size === 0) return { nodes: content, edges };\n const groups = buildControllerNodes(graphspec, analysis, content, controllerPositions);\n const frames = groups.map((group): GraphNode => {\n const placeholder = placeholders.get(group.id);\n const id = group.id;\n const simple = placeholder?.data.simple;\n const withFold =\n simple?.kind === \"frame\" && onToggleFold\n ? { ...simple, onFold: (options?: FoldToggleOptions) => onToggleFold(id, options) }\n : simple;\n return {\n ...group,\n type: NODE_TYPE_SIMPLE_FRAME,\n data: { ...(placeholder?.data ?? group.data), simple: withFold },\n };\n });\n return { nodes: sortParentsFirst([...frames, ...content]), edges };\n}\n","import type {\n DataflowAnalysis,\n FoldToggleOptions,\n GraphEdge,\n GraphNode,\n GraphSpec,\n GraphStyleId,\n} from \"@graph/types\";\nimport { GRAPH_STYLE } from \"@graph/types\";\nimport { applyControllers } from \"@graph/graphControllers\";\nimport type { ControllerRect } from \"@graph/graphControllers\";\nimport { applySimpleFrames, projectSimpleGraph, simpleDefaultFolds } from \"./simpleStyle\";\n\n/** What a style's projection receives: the dataflow graph after the folds, for one spec. */\nexport interface StyleProjectionArgs {\n graphspec: GraphSpec;\n nodes: GraphNode[];\n edges: GraphEdge[];\n /** The analysis after the folds; null for a spec with no data flow. */\n analysis: DataflowAnalysis | null;\n /** The analysis before the folds; null for a spec with no data flow. */\n rawAnalysis: DataflowAnalysis | null;\n}\n\n/** What the layout lays out for a style, and the analysis its frames are built from. */\nexport interface StyleProjection {\n nodes: GraphNode[];\n edges: GraphEdge[];\n analysis: DataflowAnalysis | null;\n /**\n * For a style that draws some of the spec's nodes through others: each node\n * it does not draw, mapped to the drawn node that stands for it, so that a\n * validation issue pinned to the first still badges the second.\n */\n standIns?: ReadonlyMap<string, string>;\n}\n\n/** What a style's frame pass receives: the laid-out nodes, and the viewer's frame and fold state. */\nexport interface StyleFrameArgs {\n nodes: GraphNode[];\n edges: GraphEdge[];\n graphspec: GraphSpec | null;\n analysis: DataflowAnalysis | null;\n controllerPositions?: Record<string, ControllerRect>;\n showControllers: boolean;\n expandedControllers?: ReadonlySet<string>;\n onToggleCollapse?: (controllerId: string) => void;\n onToggleFold?: (controllerId: string, options?: FoldToggleOptions) => void;\n}\n\n/**\n * The drawing pipeline a style contributes, in the order the viewer runs it:\n * the controllers it folds on a fresh graph, the projection of the folded\n * dataflow graph into the nodes and edges it lays out, and the pass that wraps\n * the laid-out nodes in its frames. Pure: the node components that draw the\n * result are the React layer's.\n */\nexport interface GraphStylePipeline {\n defaultFolds(graphspec: GraphSpec, rawAnalysis: DataflowAnalysis): Set<string>;\n project(args: StyleProjectionArgs): StyleProjection;\n frame(args: StyleFrameArgs): { nodes: GraphNode[]; edges: GraphEdge[] };\n}\n\nexport const GRAPH_STYLE_PIPELINES: Record<GraphStyleId, GraphStylePipeline> = {\n // Today's drawing: the dataflow graph as built, framed by controller groups\n // when the viewer shows them.\n [GRAPH_STYLE.DETAILED]: {\n defaultFolds: () => new Set(),\n project: ({ nodes, edges, analysis }) => ({ nodes, edges, analysis }),\n frame: (args) =>\n applyControllers(\n args.nodes,\n args.edges,\n args.graphspec,\n args.analysis,\n args.showControllers,\n args.expandedControllers,\n args.onToggleCollapse,\n args.controllerPositions,\n args.onToggleFold,\n ),\n },\n [GRAPH_STYLE.SIMPLE]: {\n defaultFolds: simpleDefaultFolds,\n project: ({ graphspec, nodes, analysis, rawAnalysis }) =>\n analysis && rawAnalysis\n ? projectSimpleGraph({ graphspec, nodes, analysis, rawAnalysis })\n : { nodes: [], edges: [], analysis: null },\n // Its frames are its own decision, whatever the controllers toggle says.\n frame: (args) =>\n args.graphspec && args.analysis\n ? applySimpleFrames({\n nodes: args.nodes,\n edges: args.edges,\n graphspec: args.graphspec,\n analysis: args.analysis,\n controllerPositions: args.controllerPositions,\n onToggleFold: args.onToggleFold,\n })\n : { nodes: args.nodes, edges: args.edges },\n },\n};\n"],"mappings":"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","names":["dimensionMap","elkEdges","_a","_b","_a","_b","_c","_d","_e","_f"]}