@eventcatalog/visualiser 5.0.0 → 5.1.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/layout.ts","../src/utils/utils/utils.ts","../src/utils/elk-layout.ts","../src/utils/hide-messages.ts","../src/nodes/node-types.ts"],"sourcesContent":["/**\n * Graph layout and transforms, without React components: used by EventCatalog\n * to lay graphs out on the server (`@eventcatalog/visualiser/layout`).\n */\nexport {\n layoutWithElk,\n getNodeSize,\n type LayoutGraph,\n type LayoutSize,\n type EdgeRoute,\n} from \"./utils/elk-layout\";\nexport {\n hideMessageNodes,\n hideNodes,\n getMessagesLabel,\n isCarrier,\n isMessageNode,\n} from \"./utils/hide-messages\";\n","// Can't use the CollectionEntry type from astro:content because a client component is using this util\n\nimport { MarkerType, Position, type Edge, type Node } from \"@xyflow/react\";\n\ninterface BaseCollectionData {\n id: string;\n version: string;\n}\n\ninterface CollectionItem {\n collection: string;\n data: BaseCollectionData;\n}\n\ninterface MessageCollectionItem extends CollectionItem {\n collection: \"commands\" | \"events\" | \"queries\";\n}\n\nexport const generateIdForNode = (node: CollectionItem) => {\n return `${node.data.id}-${node.data.version}`;\n};\nexport const generateIdForNodes = (nodes: any) => {\n return nodes\n .map((node: any) => `${node.data.id}-${node.data.version}`)\n .join(\"-\");\n};\nexport const generatedIdForEdge = (\n source: CollectionItem,\n target: CollectionItem,\n) => {\n return `${source.data.id}-${source.data.version}-${target.data.id}-${target.data.version}`;\n};\n\nexport const getColorFromString = (id: string) => {\n // Takes the given id (string) and returns a custom hex color based on the id\n // Create a hash from the string\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = id.charCodeAt(i) + ((hash << 5) - hash);\n }\n\n // Convert the hash into a hex color\n let color = \"#\";\n for (let i = 0; i < 3; i++) {\n const value = (hash >> (i * 8)) & 0xff;\n color += value.toString(16).padStart(2, \"0\");\n }\n\n return color;\n};\n\nexport const getEdgeLabelForServiceAsTarget = (data: MessageCollectionItem) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"invokes\";\n case \"events\":\n return \"publishes \\nevent\";\n case \"queries\":\n return \"requests\";\n default:\n return \"sends to\";\n }\n};\nexport const getEdgeLabelForMessageAsSource = (\n data: MessageCollectionItem,\n throughChannel = false,\n) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"accepts\";\n case \"events\":\n return throughChannel ? \"subscribed to\" : \"subscribed by\";\n case \"queries\":\n return \"accepts\";\n default:\n return \"sends to\";\n }\n};\n\n/**\n * Rough size of an edge label, so the layout can leave room for it on its edge\n */\nexport const getEdgeLabelSize = (label: unknown) => {\n if (typeof label !== \"string\" || !label.trim())\n return { width: 0, height: 0 };\n const lines = label.split(\"\\n\");\n const longestLine = Math.max(...lines.map((line) => line.trim().length));\n return { width: longestLine * 6 + 20, height: lines.length * 14 + 12 };\n};\n\nexport const createEdge = (edgeOptions: Edge): Edge => {\n return {\n label: \"subscribed by\",\n animated: false,\n // markerStart: {\n // type: MarkerType.Arrow,\n // width: 40,\n // height: 40,\n // },\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 40,\n height: 40,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n style: {\n strokeWidth: 1.5,\n stroke: \"rgb(var(--ec-page-text-muted))\",\n strokeDasharray: \"5 5\",\n },\n ...edgeOptions,\n };\n};\n\nexport const createNode = (values: Node): Node => {\n return {\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n ...values,\n };\n};\n","import type { Edge, Node } from \"@xyflow/react\";\nimport type {\n ELK,\n ElkExtendedEdge,\n ElkNode,\n ElkPort,\n LayoutOptions,\n} from \"elkjs/lib/elk-api\";\nimport { getEdgeLabelSize } from \"./utils/utils\";\n\nexport type LayoutGraph = { nodes: Node[]; edges: Edge[] };\nexport type LayoutSize = { width: number; height: number };\nexport type EdgeRoute = {\n points: { x: number; y: number }[];\n label?: { x: number; y: number };\n /** Where the edge's source and target were laid out (top-left, in the flow) */\n source?: { x: number; y: number };\n target?: { x: number; y: number };\n};\n\n// The size each node type renders at (at most), for nodes that haven't been\n// rendered (and measured) yet, e.g. when laying a graph out on the server\nconst DEFAULT_SIZE: LayoutSize = { width: 240, height: 112 };\nconst NODE_SIZES: Record<string, LayoutSize> = {\n events: { width: 240, height: 104 },\n commands: { width: 240, height: 126 },\n queries: { width: 240, height: 126 },\n services: { width: 240, height: 112 },\n data: { width: 240, height: 108 },\n systems: { width: 240, height: 120 },\n system: { width: 240, height: 120 },\n \"context-domain\": { width: 240, height: 112 },\n \"context-actor\": { width: 180, height: 80 },\n actor: { width: 180, height: 80 },\n note: { width: 200, height: 150 },\n step: { width: 240, height: 80 },\n // Groups with nothing in them (groups with children are sized to fit them)\n \"domain-group\": { width: 320, height: 160 },\n \"system-group\": { width: 320, height: 160 },\n};\n\n// Nodes sized by their style, like groups: an empty one needs a size too\nconst GROUP_TYPES = new Set([\"domain-group\", \"system-group\", \"group\"]);\n\n/** The size a node is laid out at: as rendered, or an estimate for its type */\nexport const getNodeSize = (node: Node): LayoutSize => {\n if (node.measured?.width && node.measured?.height) {\n return { width: node.measured.width, height: node.measured.height };\n }\n if (typeof node.width === \"number\" && typeof node.height === \"number\") {\n return { width: node.width, height: node.height };\n }\n // Domains list their services, so grow with them\n if (node.type === \"domains\" || node.type === \"domain\") {\n const services = (node.data as any)?.domain?.data?.services || [];\n return { width: 260, height: 60 + services.length * 40 };\n }\n return NODE_SIZES[node.type || \"\"] ?? DEFAULT_SIZE;\n};\n\n// Room for a group's header above its children\nconst GROUP_PADDING = \"[top=100,left=40,bottom=40,right=40]\";\n\n// Space between nodes and edges. Set on every group as well as the graph, as\n// groups don't take it from the graph (they'd use ELK's much tighter defaults)\nconst SPACING = {\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"60\",\n \"elk.spacing.nodeNode\": \"40\",\n \"elk.spacing.edgeNode\": \"25\",\n \"elk.spacing.edgeEdge\": \"15\",\n};\n\n// Large graphs (e.g. a big domain) use cheaper settings: the nicest node\n// placement takes minutes on a few thousand edges, these take a second or two\nconst LARGE_GRAPH_NODES = 80;\nconst LARGE_GRAPH_EDGES = 200;\nconst LARGE_GRAPH_OPTIONS: LayoutOptions = {\n \"elk.layered.nodePlacement.strategy\": \"BRANDES_KOEPF\",\n \"elk.layered.thoroughness\": \"1\",\n \"elk.layered.crossingMinimization.greedySwitch.type\": \"OFF\",\n};\n\nlet elk: ELK | undefined;\n// Loaded when first needed, so pages that never lay out in the browser don't download it\nconst getElk = async () => {\n if (!elk) {\n const { default: ELK } = await import(\"elkjs/lib/elk.bundled.js\");\n elk = new ELK();\n }\n return elk;\n};\n\n/**\n * Lays a graph out with ELK (left to right). Nested groups (nodes other nodes\n * have as their `parentId`) are laid out in one pass with their children.\n * Edges are routed at right angles around nodes, each edge with its own\n * connection point so edges don't run on top of each other, and labels are\n * placed on their edge. Each edge keeps its type and gets its route (and label\n * position) in `data.route`, which the visualiser's edges are drawn along.\n *\n * Node positions are top-left corners (nodes get `origin: [0, 0]`), children\n * relative to their group. Groups are sized to fit their children.\n */\nexport const layoutWithElk = async (\n { nodes, edges }: LayoutGraph,\n { sizeOf = getNodeSize }: { sizeOf?: (node: Node) => LayoutSize } = {},\n): Promise<LayoutGraph> => {\n const nodesById = new Map(nodes.map((node) => [node.id, node]));\n const groupIds = new Set(\n nodes\n .map((node) => node.parentId)\n .filter((id): id is string => !!id && nodesById.has(id)),\n );\n const parentOf = (node: Node) =>\n node.parentId && groupIds.has(node.parentId) ? node.parentId : undefined;\n\n // Each edge gets its own port, leaving from the right of its source and\n // arriving at the left of its target, so ELK spreads edges out along a\n // node's side rather than routing them all through one point. Edges to a\n // specific handle (e.g. a service listed in a domain) share that handle's port.\n const layoutEdges = edges.filter(\n (edge) => nodesById.has(edge.source) && nodesById.has(edge.target),\n );\n const ports = new Map<string, ElkPort[]>();\n const addPort = (\n nodeId: string,\n id: string,\n side: \"EAST\" | \"WEST\",\n ): string => {\n const nodePorts = ports.get(nodeId) || [];\n if (!nodePorts.some((port) => port.id === id)) {\n ports.set(nodeId, [\n ...nodePorts,\n { id, layoutOptions: { \"elk.port.side\": side } },\n ]);\n }\n return id;\n };\n const edgePorts = layoutEdges.map((edge, index) => ({\n source: addPort(\n edge.source,\n edge.sourceHandle\n ? `${edge.source}__${edge.sourceHandle}`\n : `e${index}__out`,\n \"EAST\",\n ),\n target: addPort(\n edge.target,\n edge.targetHandle\n ? `${edge.target}__${edge.targetHandle}`\n : `e${index}__in`,\n \"WEST\",\n ),\n }));\n\n const toElkNode = (node: Node): ElkNode => ({\n id: node.id,\n ...(groupIds.has(node.id)\n ? {\n children: nodes\n .filter((child) => parentOf(child) === node.id)\n .map(toElkNode),\n }\n : sizeOf(node)),\n layoutOptions: {\n \"elk.portConstraints\": \"FIXED_SIDE\",\n ...(groupIds.has(node.id)\n ? { \"elk.padding\": GROUP_PADDING, ...SPACING }\n : {}),\n // A node's own options (e.g. a group's padding) win\n ...((node.data as { layoutOptions?: LayoutOptions } | undefined)\n ?.layoutOptions ?? {}),\n },\n ports: ports.get(node.id) || [],\n });\n\n const layout = await (\n await getElk()\n ).layout({\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"RIGHT\",\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // Edge routes in absolute coordinates (node positions stay relative to their parent)\n \"elk.json.edgeCoords\": \"ROOT\",\n \"elk.edgeLabels.placement\": \"CENTER\",\n ...SPACING,\n ...(nodes.length >= LARGE_GRAPH_NODES || edges.length >= LARGE_GRAPH_EDGES\n ? LARGE_GRAPH_OPTIONS\n : { \"elk.layered.nodePlacement.strategy\": \"NETWORK_SIMPLEX\" }),\n },\n children: nodes.filter((node) => !parentOf(node)).map(toElkNode),\n edges: layoutEdges.map((edge, index) => ({\n id: `e${index}`,\n sources: [edgePorts[index].source],\n targets: [edgePorts[index].target],\n // Labels sit on their edge, rather than beside it (only works set per label)\n labels: edge.label\n ? [\n {\n text: String(edge.label),\n ...getEdgeLabelSize(edge.label),\n layoutOptions: { \"elk.edgeLabels.inline\": \"true\" },\n },\n ]\n : [],\n })),\n });\n\n const laidOut = new Map<string, ElkNode>();\n const collect = (elkNode: ElkNode) =>\n (elkNode.children || []).forEach((child) => {\n laidOut.set(child.id, child);\n collect(child);\n });\n collect(layout);\n\n // Where a node was laid out in the flow (ELK positions are relative to the parent)\n const absolute = (nodeId: string): { x: number; y: number } => {\n const node = nodesById.get(nodeId);\n const parent = node && parentOf(node);\n const offset = parent ? absolute(parent) : { x: 0, y: 0 };\n const elkNode = laidOut.get(nodeId);\n return { x: offset.x + (elkNode?.x ?? 0), y: offset.y + (elkNode?.y ?? 0) };\n };\n\n const routes = new Map<Edge, EdgeRoute>();\n (layout.edges || []).forEach((elkEdge: ElkExtendedEdge) => {\n const section = elkEdge.sections?.[0];\n if (!section) return;\n const label = elkEdge.labels?.[0];\n const edge = layoutEdges[Number(elkEdge.id.slice(1))];\n routes.set(edge, {\n points: [\n section.startPoint,\n ...(section.bendPoints || []),\n section.endPoint,\n ],\n label: label\n ? {\n x: (label.x ?? 0) + (label.width ?? 0) / 2,\n y: (label.y ?? 0) + (label.height ?? 0) / 2,\n }\n : undefined,\n source: absolute(edge.source),\n target: absolute(edge.target),\n });\n });\n\n const depth = (node?: Node): number => {\n const parent = node && parentOf(node);\n return parent ? depth(nodesById.get(parent)) + 1 : 0;\n };\n\n return {\n // Parents must come before their children in the array for React Flow\n nodes: [...nodes]\n .sort((a, b) => depth(a) - depth(b))\n .map((node) => {\n const elkNode = laidOut.get(node.id);\n if (!elkNode) return node;\n return {\n ...node,\n origin: [0, 0] as [number, number],\n position: { x: elkNode.x ?? 0, y: elkNode.y ?? 0 },\n ...(groupIds.has(node.id) ||\n (GROUP_TYPES.has(node.type || \"\") &&\n typeof node.style?.width !== \"number\")\n ? {\n style: {\n ...node.style,\n width: elkNode.width,\n height: elkNode.height,\n },\n // Re-measured at its new size\n measured: undefined,\n }\n : {}),\n };\n }),\n edges: edges.map((edge) => {\n const route = routes.get(edge);\n return route ? { ...edge, data: { ...edge.data, route } } : edge;\n }),\n };\n};\n","import { MarkerType, type Edge, type Node } from \"@xyflow/react\";\nimport { COMMAND, EVENT, MESSAGE, QUERY } from \"../nodes/node-types\";\nimport { createEdge } from \"./utils/utils\";\nimport type { EdgeRoute } from \"./elk-layout\";\n\nconst MESSAGE_NODE_TYPES = [...MESSAGE, \"messageGroup\", \"messageGroupExpanded\"];\n\nexport const isMessageNode = (node: Node) =>\n MESSAGE_NODE_TYPES.includes(node.type || \"\");\n\nconst getCollection = (type = \"\") => {\n if (EVENT.includes(type)) return \"events\";\n if (COMMAND.includes(type)) return \"commands\";\n if (QUERY.includes(type)) return \"queries\";\n return \"messages\";\n};\n\n// Returns the messages a node represents, as [name, collection] pairs\nconst getMessages = (node: Node): [string, string][] => {\n const data = node.data as any;\n if (node.type === \"messageGroup\") {\n return (data?.messages || []).map(({ message }: any) => [\n message?.data?.name || message?.data?.id,\n getCollection(message?.collection),\n ]);\n }\n const message = data?.message;\n return message\n ? [[message.name || message.id, getCollection(node.type)]]\n : [];\n};\n\nconst VERBS: Record<string, string> = {\n events: \"publishes\",\n commands: \"invokes\",\n queries: \"requests\",\n};\n\n/**\n * Label for an edge carrying messages (name -> collection), e.g.\n * \"publishes\\nProduct Created\", \"publishes\\n3 events\", \"sends\\n3 messages\"\n */\nexport const getMessagesLabel = (messages: Map<string, string>) => {\n if (messages.size === 0) return undefined;\n const collections = new Set(messages.values());\n const collection =\n collections.size === 1 ? Array.from(collections)[0] : \"messages\";\n const verb = VERBS[collection] || \"sends\";\n const sent =\n messages.size === 1\n ? Array.from(messages.keys())[0]\n : `${messages.size} ${collection}`;\n return `${verb}\\n${sent}`;\n};\n\n// Messages and channels carry what's sent between the nodes either side of them\nexport const isCarrier = (node: Node) =>\n isMessageNode(node) || node.type === \"channels\";\n\nconst isCrossDomain = (edge: Edge) =>\n !!(edge.data as { crossDomain?: boolean } | undefined)?.crossDomain;\n\nconst getRoute = (edge: Edge) =>\n (edge.data as { route?: EdgeRoute } | undefined)?.route;\n\n/**\n * The route of an edge bridging hidden nodes: along the routes of the edges it\n * replaces (through where the hidden nodes were), labelled where the first\n * hidden node was. None unless they're all laid out.\n */\nconst joinRoutes = (routes: (EdgeRoute | undefined)[]) => {\n if (!routes.every((route) => route && route.points.length > 0)) return;\n const laidOut = routes as EdgeRoute[];\n const arrives = laidOut[0].points[laidOut[0].points.length - 1];\n const leaves = laidOut[1].points[0];\n return {\n points: laidOut.flatMap((route) => route.points),\n label: { x: (arrives.x + leaves.x) / 2, y: (arrives.y + leaves.y) / 2 },\n source: laidOut[0].source,\n target: laidOut[laidOut.length - 1].target,\n };\n};\n\n/**\n * Removes the nodes `isHidden` picks. Messages and channels removed are\n * bridged: the nodes either side of them are connected directly, labelled with\n * the messages between them. Other edges to removed nodes go with them.\n * e.g. hiding events, ServiceA -> OrderPlaced -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideNodes = (\n nodes: Node[],\n edges: Edge[],\n isHidden: (node: Node) => boolean,\n) => {\n const hiddenNodes = new Map(\n nodes.filter(isHidden).map((node) => [node.id, node]),\n );\n if (hiddenNodes.size === 0) return { nodes, edges };\n\n const outgoingEdges = new Map<string, Edge[]>();\n for (const edge of edges) {\n const outgoing = outgoingEdges.get(edge.source) || [];\n outgoing.push(edge);\n outgoingEdges.set(edge.source, outgoing);\n }\n\n const keptEdges: Edge[] = [];\n const bridgedEdges = new Map<\n string,\n {\n source: string;\n target: string;\n messages: Map<string, string>;\n route?: EdgeRoute;\n crossDomain: boolean;\n }\n >();\n\n for (const edge of edges) {\n if (hiddenNodes.has(edge.source)) continue;\n const target = hiddenNodes.get(edge.target);\n if (!target) {\n keptEdges.push(edge);\n continue;\n }\n if (!isCarrier(target)) continue;\n\n // Follow the edge through hidden messages and channels until we reach\n // visible nodes, collecting the names of the messages along the way\n const stack = [\n {\n id: edge.target,\n messages: [] as [string, string][],\n routes: [getRoute(edge)],\n crossDomain: isCrossDomain(edge),\n },\n ];\n const visited = new Set<string>();\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (visited.has(current.id)) continue;\n visited.add(current.id);\n const messages = [\n ...current.messages,\n ...getMessages(hiddenNodes.get(current.id)!),\n ];\n\n for (const next of outgoingEdges.get(current.id) || []) {\n const routes = [...current.routes, getRoute(next)];\n const crossDomain = current.crossDomain || isCrossDomain(next);\n const nextNode = hiddenNodes.get(next.target);\n if (nextNode) {\n if (isCarrier(nextNode))\n stack.push({ id: next.target, messages, routes, crossDomain });\n continue;\n }\n if (next.target === edge.source) continue;\n\n // Prefixed, so it can't clash with an edge directly between the two\n const id = `bridged-${edge.source}-${next.target}`;\n const bridged = bridgedEdges.get(id) || {\n source: edge.source,\n target: next.target,\n messages: new Map<string, string>(),\n route: joinRoutes(routes),\n crossDomain: false,\n };\n bridged.crossDomain ||= crossDomain;\n messages.forEach(([name, collection]) =>\n bridged.messages.set(name, collection),\n );\n bridgedEdges.set(id, bridged);\n }\n }\n }\n\n const newEdges = Array.from(bridgedEdges, ([id, bridged]) =>\n createEdge({\n id,\n source: bridged.source,\n target: bridged.target,\n label: getMessagesLabel(bridged.messages),\n // The same size arrow as the rest of the graph's edges\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 20,\n height: 20,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n // Along the same route, and crossing domains if any edge it replaces did\n data: {\n ...(bridged.route ? { route: bridged.route } : {}),\n ...(bridged.crossDomain ? { crossDomain: true } : {}),\n },\n }),\n );\n\n return {\n nodes: nodes.filter((node) => !hiddenNodes.has(node.id)),\n edges: [...keptEdges, ...newEdges],\n };\n};\n\n/**\n * Removes message nodes (events, commands, queries) and the channels they\n * travel through, connecting the nodes on either side directly.\n * e.g. ServiceA -> OrderPlaced -> orders-topic -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideMessageNodes = (nodes: Node[], edges: Edge[]) =>\n hideNodes(nodes, edges, isCarrier);\n","export const SERVICE = [\"service\", \"services\"];\nexport const AGENT = [\"agent\", \"agents\"];\nexport const AGENT_TOOL = [\"agentTool\", \"agent-tool\"];\nexport const EVENT = [\"event\", \"events\"];\nexport const QUERY = [\"queries\", \"query\", \"querie\"];\nexport const COMMAND = [\"command\", \"commands\"];\nexport const CHANNEL = [\"channel\", \"channels\"];\nexport const ACTOR = [\"actor\", \"actors\"];\nexport const DATA = [\"data\"];\nexport const VIEW = [\"view\"];\n\nexport const MESSAGE = [...EVENT, ...COMMAND, ...QUERY];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAA2D;AAkFpD,IAAM,mBAAmB,CAAC,UAAmB;AAClD,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK;AAC3C,WAAO,EAAE,OAAO,GAAG,QAAQ,EAAE;AAC/B,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,QAAM,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,EAAE,MAAM,CAAC;AACvE,SAAO,EAAE,OAAO,cAAc,IAAI,IAAI,QAAQ,MAAM,SAAS,KAAK,GAAG;AACvE;AAEO,IAAM,aAAa,CAAC,gBAA4B;AACrD,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,WAAW;AAAA,MACT,MAAM,wBAAW;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,IACA,OAAO;AAAA,MACL,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,iBAAiB;AAAA,IACnB;AAAA,IACA,GAAG;AAAA,EACL;AACF;;;AC5FA,IAAM,eAA2B,EAAE,OAAO,KAAK,QAAQ,IAAI;AAC3D,IAAM,aAAyC;AAAA,EAC7C,QAAQ,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAClC,UAAU,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EACpC,SAAS,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EACnC,UAAU,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EACpC,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAChC,SAAS,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EACnC,QAAQ,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAClC,kBAAkB,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC5C,iBAAiB,EAAE,OAAO,KAAK,QAAQ,GAAG;AAAA,EAC1C,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG;AAAA,EAChC,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAChC,MAAM,EAAE,OAAO,KAAK,QAAQ,GAAG;AAAA;AAAA,EAE/B,gBAAgB,EAAE,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC1C,gBAAgB,EAAE,OAAO,KAAK,QAAQ,IAAI;AAC5C;AAGA,IAAM,cAAc,oBAAI,IAAI,CAAC,gBAAgB,gBAAgB,OAAO,CAAC;AAG9D,IAAM,cAAc,CAAC,SAA2B;AACrD,MAAI,KAAK,UAAU,SAAS,KAAK,UAAU,QAAQ;AACjD,WAAO,EAAE,OAAO,KAAK,SAAS,OAAO,QAAQ,KAAK,SAAS,OAAO;AAAA,EACpE;AACA,MAAI,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,WAAW,UAAU;AACrE,WAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,EAClD;AAEA,MAAI,KAAK,SAAS,aAAa,KAAK,SAAS,UAAU;AACrD,UAAM,WAAY,KAAK,MAAc,QAAQ,MAAM,YAAY,CAAC;AAChE,WAAO,EAAE,OAAO,KAAK,QAAQ,KAAK,SAAS,SAAS,GAAG;AAAA,EACzD;AACA,SAAO,WAAW,KAAK,QAAQ,EAAE,KAAK;AACxC;AAGA,IAAM,gBAAgB;AAItB,IAAM,UAAU;AAAA,EACd,6CAA6C;AAAA,EAC7C,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,wBAAwB;AAC1B;AAIA,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAC1B,IAAM,sBAAqC;AAAA,EACzC,sCAAsC;AAAA,EACtC,4BAA4B;AAAA,EAC5B,sDAAsD;AACxD;AAEA,IAAI;AAEJ,IAAM,SAAS,YAAY;AACzB,MAAI,CAAC,KAAK;AACR,UAAM,EAAE,SAAS,IAAI,IAAI,MAAM,OAAO,0BAA0B;AAChE,UAAM,IAAI,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAaO,IAAM,gBAAgB,OAC3B,EAAE,OAAO,MAAM,GACf,EAAE,SAAS,YAAY,IAA6C,CAAC,MAC5C;AACzB,QAAM,YAAY,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC9D,QAAM,WAAW,IAAI;AAAA,IACnB,MACG,IAAI,CAAC,SAAS,KAAK,QAAQ,EAC3B,OAAO,CAAC,OAAqB,CAAC,CAAC,MAAM,UAAU,IAAI,EAAE,CAAC;AAAA,EAC3D;AACA,QAAM,WAAW,CAAC,SAChB,KAAK,YAAY,SAAS,IAAI,KAAK,QAAQ,IAAI,KAAK,WAAW;AAMjE,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,SAAS,UAAU,IAAI,KAAK,MAAM,KAAK,UAAU,IAAI,KAAK,MAAM;AAAA,EACnE;AACA,QAAM,QAAQ,oBAAI,IAAuB;AACzC,QAAM,UAAU,CACd,QACA,IACA,SACW;AACX,UAAM,YAAY,MAAM,IAAI,MAAM,KAAK,CAAC;AACxC,QAAI,CAAC,UAAU,KAAK,CAAC,SAAS,KAAK,OAAO,EAAE,GAAG;AAC7C,YAAM,IAAI,QAAQ;AAAA,QAChB,GAAG;AAAA,QACH,EAAE,IAAI,eAAe,EAAE,iBAAiB,KAAK,EAAE;AAAA,MACjD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACA,QAAM,YAAY,YAAY,IAAI,CAAC,MAAM,WAAW;AAAA,IAClD,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,KAAK,eACD,GAAG,KAAK,MAAM,KAAK,KAAK,YAAY,KACpC,IAAI,KAAK;AAAA,MACb;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,KAAK,eACD,GAAG,KAAK,MAAM,KAAK,KAAK,YAAY,KACpC,IAAI,KAAK;AAAA,MACb;AAAA,IACF;AAAA,EACF,EAAE;AAEF,QAAM,YAAY,CAAC,UAAyB;AAAA,IAC1C,IAAI,KAAK;AAAA,IACT,GAAI,SAAS,IAAI,KAAK,EAAE,IACpB;AAAA,MACE,UAAU,MACP,OAAO,CAAC,UAAU,SAAS,KAAK,MAAM,KAAK,EAAE,EAC7C,IAAI,SAAS;AAAA,IAClB,IACA,OAAO,IAAI;AAAA,IACf,eAAe;AAAA,MACb,uBAAuB;AAAA,MACvB,GAAI,SAAS,IAAI,KAAK,EAAE,IACpB,EAAE,eAAe,eAAe,GAAG,QAAQ,IAC3C,CAAC;AAAA;AAAA,MAEL,GAAK,KAAK,MACN,iBAAiB,CAAC;AAAA,IACxB;AAAA,IACA,OAAO,MAAM,IAAI,KAAK,EAAE,KAAK,CAAC;AAAA,EAChC;AAEA,QAAM,SAAS,OACb,MAAM,OAAO,GACb,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,eAAe;AAAA,MACb,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,yBAAyB;AAAA,MACzB,mBAAmB;AAAA;AAAA,MAEnB,uBAAuB;AAAA,MACvB,4BAA4B;AAAA,MAC5B,GAAG;AAAA,MACH,GAAI,MAAM,UAAU,qBAAqB,MAAM,UAAU,oBACrD,sBACA,EAAE,sCAAsC,kBAAkB;AAAA,IAChE;AAAA,IACA,UAAU,MAAM,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,IAAI,SAAS;AAAA,IAC/D,OAAO,YAAY,IAAI,CAAC,MAAM,WAAW;AAAA,MACvC,IAAI,IAAI,KAAK;AAAA,MACb,SAAS,CAAC,UAAU,KAAK,EAAE,MAAM;AAAA,MACjC,SAAS,CAAC,UAAU,KAAK,EAAE,MAAM;AAAA;AAAA,MAEjC,QAAQ,KAAK,QACT;AAAA,QACE;AAAA,UACE,MAAM,OAAO,KAAK,KAAK;AAAA,UACvB,GAAG,iBAAiB,KAAK,KAAK;AAAA,UAC9B,eAAe,EAAE,yBAAyB,OAAO;AAAA,QACnD;AAAA,MACF,IACA,CAAC;AAAA,IACP,EAAE;AAAA,EACJ,CAAC;AAED,QAAM,UAAU,oBAAI,IAAqB;AACzC,QAAM,UAAU,CAAC,aACd,QAAQ,YAAY,CAAC,GAAG,QAAQ,CAAC,UAAU;AAC1C,YAAQ,IAAI,MAAM,IAAI,KAAK;AAC3B,YAAQ,KAAK;AAAA,EACf,CAAC;AACH,UAAQ,MAAM;AAGd,QAAM,WAAW,CAAC,WAA6C;AAC7D,UAAM,OAAO,UAAU,IAAI,MAAM;AACjC,UAAM,SAAS,QAAQ,SAAS,IAAI;AACpC,UAAM,SAAS,SAAS,SAAS,MAAM,IAAI,EAAE,GAAG,GAAG,GAAG,EAAE;AACxD,UAAM,UAAU,QAAQ,IAAI,MAAM;AAClC,WAAO,EAAE,GAAG,OAAO,KAAK,SAAS,KAAK,IAAI,GAAG,OAAO,KAAK,SAAS,KAAK,GAAG;AAAA,EAC5E;AAEA,QAAM,SAAS,oBAAI,IAAqB;AACxC,GAAC,OAAO,SAAS,CAAC,GAAG,QAAQ,CAAC,YAA6B;AACzD,UAAM,UAAU,QAAQ,WAAW,CAAC;AACpC,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,QAAQ,SAAS,CAAC;AAChC,UAAM,OAAO,YAAY,OAAO,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC;AACpD,WAAO,IAAI,MAAM;AAAA,MACf,QAAQ;AAAA,QACN,QAAQ;AAAA,QACR,GAAI,QAAQ,cAAc,CAAC;AAAA,QAC3B,QAAQ;AAAA,MACV;AAAA,MACA,OAAO,QACH;AAAA,QACE,IAAI,MAAM,KAAK,MAAM,MAAM,SAAS,KAAK;AAAA,QACzC,IAAI,MAAM,KAAK,MAAM,MAAM,UAAU,KAAK;AAAA,MAC5C,IACA;AAAA,MACJ,QAAQ,SAAS,KAAK,MAAM;AAAA,MAC5B,QAAQ,SAAS,KAAK,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH,CAAC;AAED,QAAM,QAAQ,CAAC,SAAwB;AACrC,UAAM,SAAS,QAAQ,SAAS,IAAI;AACpC,WAAO,SAAS,MAAM,UAAU,IAAI,MAAM,CAAC,IAAI,IAAI;AAAA,EACrD;AAEA,SAAO;AAAA;AAAA,IAEL,OAAO,CAAC,GAAG,KAAK,EACb,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,EAClC,IAAI,CAAC,SAAS;AACb,YAAM,UAAU,QAAQ,IAAI,KAAK,EAAE;AACnC,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QAAQ,CAAC,GAAG,CAAC;AAAA,QACb,UAAU,EAAE,GAAG,QAAQ,KAAK,GAAG,GAAG,QAAQ,KAAK,EAAE;AAAA,QACjD,GAAI,SAAS,IAAI,KAAK,EAAE,KACvB,YAAY,IAAI,KAAK,QAAQ,EAAE,KAC9B,OAAO,KAAK,OAAO,UAAU,WAC3B;AAAA,UACE,OAAO;AAAA,YACL,GAAG,KAAK;AAAA,YACR,OAAO,QAAQ;AAAA,YACf,QAAQ,QAAQ;AAAA,UAClB;AAAA;AAAA,UAEA,UAAU;AAAA,QACZ,IACA,CAAC;AAAA,MACP;AAAA,IACF,CAAC;AAAA,IACH,OAAO,MAAM,IAAI,CAAC,SAAS;AACzB,YAAM,QAAQ,OAAO,IAAI,IAAI;AAC7B,aAAO,QAAQ,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,MAAM,EAAE,IAAI;AAAA,IAC9D,CAAC;AAAA,EACH;AACF;;;AC/RA,IAAAA,gBAAiD;;;ACG1C,IAAM,QAAQ,CAAC,SAAS,QAAQ;AAChC,IAAM,QAAQ,CAAC,WAAW,SAAS,QAAQ;AAC3C,IAAM,UAAU,CAAC,WAAW,UAAU;AAMtC,IAAM,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK;;;ADNtD,IAAM,qBAAqB,CAAC,GAAG,SAAS,gBAAgB,sBAAsB;AAEvE,IAAM,gBAAgB,CAAC,SAC5B,mBAAmB,SAAS,KAAK,QAAQ,EAAE;AAE7C,IAAM,gBAAgB,CAAC,OAAO,OAAO;AACnC,MAAI,MAAM,SAAS,IAAI,EAAG,QAAO;AACjC,MAAI,QAAQ,SAAS,IAAI,EAAG,QAAO;AACnC,MAAI,MAAM,SAAS,IAAI,EAAG,QAAO;AACjC,SAAO;AACT;AAGA,IAAM,cAAc,CAAC,SAAmC;AACtD,QAAM,OAAO,KAAK;AAClB,MAAI,KAAK,SAAS,gBAAgB;AAChC,YAAQ,MAAM,YAAY,CAAC,GAAG,IAAI,CAAC,EAAE,SAAAC,SAAQ,MAAW;AAAA,MACtDA,UAAS,MAAM,QAAQA,UAAS,MAAM;AAAA,MACtC,cAAcA,UAAS,UAAU;AAAA,IACnC,CAAC;AAAA,EACH;AACA,QAAM,UAAU,MAAM;AACtB,SAAO,UACH,CAAC,CAAC,QAAQ,QAAQ,QAAQ,IAAI,cAAc,KAAK,IAAI,CAAC,CAAC,IACvD,CAAC;AACP;AAEA,IAAM,QAAgC;AAAA,EACpC,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AACX;AAMO,IAAM,mBAAmB,CAAC,aAAkC;AACjE,MAAI,SAAS,SAAS,EAAG,QAAO;AAChC,QAAM,cAAc,IAAI,IAAI,SAAS,OAAO,CAAC;AAC7C,QAAM,aACJ,YAAY,SAAS,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC,IAAI;AACxD,QAAM,OAAO,MAAM,UAAU,KAAK;AAClC,QAAM,OACJ,SAAS,SAAS,IACd,MAAM,KAAK,SAAS,KAAK,CAAC,EAAE,CAAC,IAC7B,GAAG,SAAS,IAAI,IAAI,UAAU;AACpC,SAAO,GAAG,IAAI;AAAA,EAAK,IAAI;AACzB;AAGO,IAAM,YAAY,CAAC,SACxB,cAAc,IAAI,KAAK,KAAK,SAAS;AAEvC,IAAM,gBAAgB,CAAC,SACrB,CAAC,CAAE,KAAK,MAAgD;AAE1D,IAAM,WAAW,CAAC,SACf,KAAK,MAA4C;AAOpD,IAAM,aAAa,CAAC,WAAsC;AACxD,MAAI,CAAC,OAAO,MAAM,CAAC,UAAU,SAAS,MAAM,OAAO,SAAS,CAAC,EAAG;AAChE,QAAM,UAAU;AAChB,QAAM,UAAU,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC,EAAE,OAAO,SAAS,CAAC;AAC9D,QAAM,SAAS,QAAQ,CAAC,EAAE,OAAO,CAAC;AAClC,SAAO;AAAA,IACL,QAAQ,QAAQ,QAAQ,CAAC,UAAU,MAAM,MAAM;AAAA,IAC/C,OAAO,EAAE,IAAI,QAAQ,IAAI,OAAO,KAAK,GAAG,IAAI,QAAQ,IAAI,OAAO,KAAK,EAAE;AAAA,IACtE,QAAQ,QAAQ,CAAC,EAAE;AAAA,IACnB,QAAQ,QAAQ,QAAQ,SAAS,CAAC,EAAE;AAAA,EACtC;AACF;AAQO,IAAM,YAAY,CACvB,OACA,OACA,aACG;AACH,QAAM,cAAc,IAAI;AAAA,IACtB,MAAM,OAAO,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC;AAAA,EACtD;AACA,MAAI,YAAY,SAAS,EAAG,QAAO,EAAE,OAAO,MAAM;AAElD,QAAM,gBAAgB,oBAAI,IAAoB;AAC9C,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,cAAc,IAAI,KAAK,MAAM,KAAK,CAAC;AACpD,aAAS,KAAK,IAAI;AAClB,kBAAc,IAAI,KAAK,QAAQ,QAAQ;AAAA,EACzC;AAEA,QAAM,YAAoB,CAAC;AAC3B,QAAM,eAAe,oBAAI,IASvB;AAEF,aAAW,QAAQ,OAAO;AACxB,QAAI,YAAY,IAAI,KAAK,MAAM,EAAG;AAClC,UAAM,SAAS,YAAY,IAAI,KAAK,MAAM;AAC1C,QAAI,CAAC,QAAQ;AACX,gBAAU,KAAK,IAAI;AACnB;AAAA,IACF;AACA,QAAI,CAAC,UAAU,MAAM,EAAG;AAIxB,UAAM,QAAQ;AAAA,MACZ;AAAA,QACE,IAAI,KAAK;AAAA,QACT,UAAU,CAAC;AAAA,QACX,QAAQ,CAAC,SAAS,IAAI,CAAC;AAAA,QACvB,aAAa,cAAc,IAAI;AAAA,MACjC;AAAA,IACF;AACA,UAAM,UAAU,oBAAI,IAAY;AAChC,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,UAAU,MAAM,IAAI;AAC1B,UAAI,QAAQ,IAAI,QAAQ,EAAE,EAAG;AAC7B,cAAQ,IAAI,QAAQ,EAAE;AACtB,YAAM,WAAW;AAAA,QACf,GAAG,QAAQ;AAAA,QACX,GAAG,YAAY,YAAY,IAAI,QAAQ,EAAE,CAAE;AAAA,MAC7C;AAEA,iBAAW,QAAQ,cAAc,IAAI,QAAQ,EAAE,KAAK,CAAC,GAAG;AACtD,cAAM,SAAS,CAAC,GAAG,QAAQ,QAAQ,SAAS,IAAI,CAAC;AACjD,cAAM,cAAc,QAAQ,eAAe,cAAc,IAAI;AAC7D,cAAM,WAAW,YAAY,IAAI,KAAK,MAAM;AAC5C,YAAI,UAAU;AACZ,cAAI,UAAU,QAAQ;AACpB,kBAAM,KAAK,EAAE,IAAI,KAAK,QAAQ,UAAU,QAAQ,YAAY,CAAC;AAC/D;AAAA,QACF;AACA,YAAI,KAAK,WAAW,KAAK,OAAQ;AAGjC,cAAM,KAAK,WAAW,KAAK,MAAM,IAAI,KAAK,MAAM;AAChD,cAAM,UAAU,aAAa,IAAI,EAAE,KAAK;AAAA,UACtC,QAAQ,KAAK;AAAA,UACb,QAAQ,KAAK;AAAA,UACb,UAAU,oBAAI,IAAoB;AAAA,UAClC,OAAO,WAAW,MAAM;AAAA,UACxB,aAAa;AAAA,QACf;AACA,gBAAQ,gBAAR,QAAQ,cAAgB;AACxB,iBAAS;AAAA,UAAQ,CAAC,CAAC,MAAM,UAAU,MACjC,QAAQ,SAAS,IAAI,MAAM,UAAU;AAAA,QACvC;AACA,qBAAa,IAAI,IAAI,OAAO;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAM;AAAA,IAAK;AAAA,IAAc,CAAC,CAAC,IAAI,OAAO,MACrD,WAAW;AAAA,MACT;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,MAChB,OAAO,iBAAiB,QAAQ,QAAQ;AAAA;AAAA,MAExC,WAAW;AAAA,QACT,MAAM,yBAAW;AAAA,QACjB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,OAAO;AAAA,MACT;AAAA;AAAA,MAEA,MAAM;AAAA,QACJ,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAChD,GAAI,QAAQ,cAAc,EAAE,aAAa,KAAK,IAAI,CAAC;AAAA,MACrD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,MAAM,OAAO,CAAC,SAAS,CAAC,YAAY,IAAI,KAAK,EAAE,CAAC;AAAA,IACvD,OAAO,CAAC,GAAG,WAAW,GAAG,QAAQ;AAAA,EACnC;AACF;AAOO,IAAM,mBAAmB,CAAC,OAAe,UAC9C,UAAU,OAAO,OAAO,SAAS;","names":["import_react","message"]}
1
+ {"version":3,"sources":["../src/layout.ts","../src/utils/utils/utils.ts","../src/utils/elk-layout.ts","../src/utils/hide-messages.ts","../src/nodes/node-types.ts","../src/utils/swimlanes.ts","../src/utils/export-mermaid.ts"],"sourcesContent":["/**\n * Graph layout and transforms, without React components: used by EventCatalog\n * to lay graphs out on the server (`@eventcatalog/visualiser/layout`).\n */\nexport {\n layoutWithElk,\n getNodeSize,\n type LayoutGraph,\n type LayoutSize,\n type EdgeRoute,\n type CopiedEdgeRoute,\n} from \"./utils/elk-layout\";\nexport {\n hideMessageNodes,\n hideNodes,\n getMessagesLabel,\n isCarrier,\n isMessageNode,\n} from \"./utils/hide-messages\";\nexport {\n convertToMermaid,\n type MermaidExportOptions,\n} from \"./utils/export-mermaid\";\n","// Can't use the CollectionEntry type from astro:content because a client component is using this util\n\nimport { MarkerType, Position, type Edge, type Node } from \"@xyflow/react\";\n\ninterface BaseCollectionData {\n id: string;\n version: string;\n}\n\ninterface CollectionItem {\n collection: string;\n data: BaseCollectionData;\n}\n\ninterface MessageCollectionItem extends CollectionItem {\n collection: \"commands\" | \"events\" | \"queries\";\n}\n\nexport const generateIdForNode = (node: CollectionItem) => {\n return `${node.data.id}-${node.data.version}`;\n};\nexport const generateIdForNodes = (nodes: any) => {\n return nodes\n .map((node: any) => `${node.data.id}-${node.data.version}`)\n .join(\"-\");\n};\nexport const generatedIdForEdge = (\n source: CollectionItem,\n target: CollectionItem,\n) => {\n return `${source.data.id}-${source.data.version}-${target.data.id}-${target.data.version}`;\n};\n\nexport const getColorFromString = (id: string) => {\n // Takes the given id (string) and returns a custom hex color based on the id\n // Create a hash from the string\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = id.charCodeAt(i) + ((hash << 5) - hash);\n }\n\n // Convert the hash into a hex color\n let color = \"#\";\n for (let i = 0; i < 3; i++) {\n const value = (hash >> (i * 8)) & 0xff;\n color += value.toString(16).padStart(2, \"0\");\n }\n\n return color;\n};\n\nexport const getEdgeLabelForServiceAsTarget = (data: MessageCollectionItem) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"invokes\";\n case \"events\":\n return \"publishes \\nevent\";\n case \"queries\":\n return \"requests\";\n default:\n return \"sends to\";\n }\n};\nexport const getEdgeLabelForMessageAsSource = (\n data: MessageCollectionItem,\n throughChannel = false,\n) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"accepts\";\n case \"events\":\n return throughChannel ? \"subscribed to\" : \"subscribed by\";\n case \"queries\":\n return \"accepts\";\n default:\n return \"sends to\";\n }\n};\n\n/**\n * Rough size of an edge label, so the layout can leave room for it on its edge\n */\nexport const getEdgeLabelSize = (label: unknown) => {\n if (typeof label !== \"string\" || !label.trim())\n return { width: 0, height: 0 };\n const lines = label.split(\"\\n\");\n const longestLine = Math.max(...lines.map((line) => line.trim().length));\n return { width: longestLine * 6 + 20, height: lines.length * 14 + 12 };\n};\n\nexport const createEdge = (edgeOptions: Edge): Edge => {\n return {\n label: \"subscribed by\",\n animated: false,\n // markerStart: {\n // type: MarkerType.Arrow,\n // width: 40,\n // height: 40,\n // },\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 40,\n height: 40,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n style: {\n strokeWidth: 1.5,\n stroke: \"rgb(var(--ec-page-text-muted))\",\n strokeDasharray: \"5 5\",\n },\n ...edgeOptions,\n };\n};\n\nexport const createNode = (values: Node): Node => {\n return {\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n ...values,\n };\n};\n","import type { Edge, Node } from \"@xyflow/react\";\nimport type {\n ELK,\n ElkExtendedEdge,\n ElkNode,\n ElkPort,\n LayoutOptions,\n} from \"elkjs/lib/elk-api\";\nimport { getEdgeLabelSize } from \"./utils/utils\";\n\nexport type LayoutGraph = { nodes: Node[]; edges: Edge[] };\nexport type LayoutSize = { width: number; height: number };\nexport type EdgeRoute = {\n points: { x: number; y: number }[];\n label?: { x: number; y: number };\n /**\n * Where the edge's source and target were laid out (top-left, in the flow),\n * and how tall: nodes often render shorter than the layout estimated\n */\n source?: { x: number; y: number; height?: number };\n target?: { x: number; y: number; height?: number };\n};\n\n/**\n * A route copied to another canvas (e.g. a diagram opened in Studio), which\n * places the nodes itself: where it goes, and how tall its nodes were laid out\n */\nexport type CopiedEdgeRoute = Pick<EdgeRoute, \"points\" | \"label\"> & {\n sourceHeight?: number;\n targetHeight?: number;\n};\n\n// The size each node type renders at (at most), for nodes that haven't been\n// rendered (and measured) yet, e.g. when laying a graph out on the server\nconst DEFAULT_SIZE: LayoutSize = { width: 240, height: 112 };\nconst NODE_SIZES: Record<string, LayoutSize> = {\n events: { width: 240, height: 104 },\n commands: { width: 240, height: 126 },\n queries: { width: 240, height: 126 },\n services: { width: 240, height: 112 },\n data: { width: 240, height: 108 },\n systems: { width: 240, height: 120 },\n system: { width: 240, height: 120 },\n \"context-domain\": { width: 240, height: 112 },\n \"context-actor\": { width: 180, height: 80 },\n actor: { width: 180, height: 80 },\n note: { width: 200, height: 150 },\n step: { width: 240, height: 80 },\n // Groups with nothing in them (groups with children are sized to fit them)\n \"domain-group\": { width: 320, height: 160 },\n \"system-group\": { width: 320, height: 160 },\n};\n\n// Nodes sized by their style, like groups: an empty one needs a size too\nconst GROUP_TYPES = new Set([\"domain-group\", \"system-group\", \"group\"]);\n\n/** The size a node is laid out at: as rendered, or an estimate for its type */\nexport const getNodeSize = (node: Node): LayoutSize => {\n if (node.measured?.width && node.measured?.height) {\n return { width: node.measured.width, height: node.measured.height };\n }\n if (typeof node.width === \"number\" && typeof node.height === \"number\") {\n return { width: node.width, height: node.height };\n }\n // Domains list their services, so grow with them\n if (node.type === \"domains\" || node.type === \"domain\") {\n const services = (node.data as any)?.domain?.data?.services || [];\n return { width: 260, height: 60 + services.length * 40 };\n }\n return NODE_SIZES[node.type || \"\"] ?? DEFAULT_SIZE;\n};\n\n// Room for a group's header above its children\nconst GROUP_PADDING = \"[top=100,left=40,bottom=40,right=40]\";\n\n// Space between nodes and edges. Set on every group as well as the graph, as\n// groups don't take it from the graph (they'd use ELK's much tighter defaults)\nconst SPACING = {\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"60\",\n \"elk.spacing.nodeNode\": \"40\",\n \"elk.spacing.edgeNode\": \"25\",\n \"elk.spacing.edgeEdge\": \"15\",\n};\n\n// Large graphs (e.g. a big domain) use cheaper settings: the nicest node\n// placement takes minutes on a few thousand edges, these take a second or two\nconst LARGE_GRAPH_NODES = 80;\nconst LARGE_GRAPH_EDGES = 200;\nconst LARGE_GRAPH_OPTIONS: LayoutOptions = {\n \"elk.layered.nodePlacement.strategy\": \"BRANDES_KOEPF\",\n \"elk.layered.thoroughness\": \"1\",\n \"elk.layered.crossingMinimization.greedySwitch.type\": \"OFF\",\n};\n\nlet elk: ELK | undefined;\n// Loaded when first needed, so pages that never lay out in the browser don't download it\nconst getElk = async () => {\n if (!elk) {\n const { default: ELK } = await import(\"elkjs/lib/elk.bundled.js\");\n elk = new ELK();\n }\n return elk;\n};\n\n/**\n * Lays a graph out with ELK (left to right). Nested groups (nodes other nodes\n * have as their `parentId`) are laid out in one pass with their children.\n * Edges are routed at right angles around nodes, each edge with its own\n * connection point so edges don't run on top of each other, and labels are\n * placed on their edge. Each edge keeps its type and gets its route (and label\n * position) in `data.route`, which the visualiser's edges are drawn along.\n *\n * Node positions are top-left corners (nodes get `origin: [0, 0]`), children\n * relative to their group. Groups are sized to fit their children.\n */\nexport const layoutWithElk = async (\n { nodes, edges }: LayoutGraph,\n { sizeOf = getNodeSize }: { sizeOf?: (node: Node) => LayoutSize } = {},\n): Promise<LayoutGraph> => {\n const nodesById = new Map(nodes.map((node) => [node.id, node]));\n const groupIds = new Set(\n nodes\n .map((node) => node.parentId)\n .filter((id): id is string => !!id && nodesById.has(id)),\n );\n const parentOf = (node: Node) =>\n node.parentId && groupIds.has(node.parentId) ? node.parentId : undefined;\n\n // Each edge gets its own port, leaving from the right of its source and\n // arriving at the left of its target, so ELK spreads edges out along a\n // node's side rather than routing them all through one point. Edges to a\n // specific handle (e.g. a service listed in a domain) share that handle's port.\n const layoutEdges = edges.filter(\n (edge) => nodesById.has(edge.source) && nodesById.has(edge.target),\n );\n const ports = new Map<string, ElkPort[]>();\n const addPort = (\n nodeId: string,\n id: string,\n side: \"EAST\" | \"WEST\",\n ): string => {\n const nodePorts = ports.get(nodeId) || [];\n if (!nodePorts.some((port) => port.id === id)) {\n ports.set(nodeId, [\n ...nodePorts,\n { id, layoutOptions: { \"elk.port.side\": side } },\n ]);\n }\n return id;\n };\n const edgePorts = layoutEdges.map((edge, index) => ({\n source: addPort(\n edge.source,\n edge.sourceHandle\n ? `${edge.source}__${edge.sourceHandle}`\n : `e${index}__out`,\n \"EAST\",\n ),\n target: addPort(\n edge.target,\n edge.targetHandle\n ? `${edge.target}__${edge.targetHandle}`\n : `e${index}__in`,\n \"WEST\",\n ),\n }));\n\n const toElkNode = (node: Node): ElkNode => ({\n id: node.id,\n ...(groupIds.has(node.id)\n ? {\n children: nodes\n .filter((child) => parentOf(child) === node.id)\n .map(toElkNode),\n }\n : sizeOf(node)),\n layoutOptions: {\n \"elk.portConstraints\": \"FIXED_SIDE\",\n ...(groupIds.has(node.id)\n ? { \"elk.padding\": GROUP_PADDING, ...SPACING }\n : {}),\n // A node's own options (e.g. a group's padding) win\n ...((node.data as { layoutOptions?: LayoutOptions } | undefined)\n ?.layoutOptions ?? {}),\n },\n ports: ports.get(node.id) || [],\n });\n\n const layout = await (\n await getElk()\n ).layout({\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"RIGHT\",\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // Edge routes in absolute coordinates (node positions stay relative to their parent)\n \"elk.json.edgeCoords\": \"ROOT\",\n \"elk.edgeLabels.placement\": \"CENTER\",\n ...SPACING,\n ...(nodes.length >= LARGE_GRAPH_NODES || edges.length >= LARGE_GRAPH_EDGES\n ? LARGE_GRAPH_OPTIONS\n : { \"elk.layered.nodePlacement.strategy\": \"NETWORK_SIMPLEX\" }),\n },\n children: nodes.filter((node) => !parentOf(node)).map(toElkNode),\n edges: layoutEdges.map((edge, index) => ({\n id: `e${index}`,\n sources: [edgePorts[index].source],\n targets: [edgePorts[index].target],\n // Labels sit on their edge, rather than beside it (only works set per label)\n labels: edge.label\n ? [\n {\n text: String(edge.label),\n ...getEdgeLabelSize(edge.label),\n layoutOptions: { \"elk.edgeLabels.inline\": \"true\" },\n },\n ]\n : [],\n })),\n });\n\n const laidOut = new Map<string, ElkNode>();\n const collect = (elkNode: ElkNode) =>\n (elkNode.children || []).forEach((child) => {\n laidOut.set(child.id, child);\n collect(child);\n });\n collect(layout);\n\n // Where a node was laid out in the flow (ELK positions are relative to the parent)\n const absolute = (nodeId: string): { x: number; y: number } => {\n const node = nodesById.get(nodeId);\n const parent = node && parentOf(node);\n const offset = parent ? absolute(parent) : { x: 0, y: 0 };\n const elkNode = laidOut.get(nodeId);\n return { x: offset.x + (elkNode?.x ?? 0), y: offset.y + (elkNode?.y ?? 0) };\n };\n\n const laidOutAt = (nodeId: string) => ({\n ...absolute(nodeId),\n height: laidOut.get(nodeId)?.height,\n });\n\n const routes = new Map<Edge, EdgeRoute>();\n (layout.edges || []).forEach((elkEdge: ElkExtendedEdge) => {\n const section = elkEdge.sections?.[0];\n if (!section) return;\n const label = elkEdge.labels?.[0];\n const edge = layoutEdges[Number(elkEdge.id.slice(1))];\n routes.set(edge, {\n points: [\n section.startPoint,\n ...(section.bendPoints || []),\n section.endPoint,\n ],\n label: label\n ? {\n x: (label.x ?? 0) + (label.width ?? 0) / 2,\n y: (label.y ?? 0) + (label.height ?? 0) / 2,\n }\n : undefined,\n source: laidOutAt(edge.source),\n target: laidOutAt(edge.target),\n });\n });\n\n const depth = (node?: Node): number => {\n const parent = node && parentOf(node);\n return parent ? depth(nodesById.get(parent)) + 1 : 0;\n };\n\n return {\n // Parents must come before their children in the array for React Flow\n nodes: [...nodes]\n .sort((a, b) => depth(a) - depth(b))\n .map((node) => {\n const elkNode = laidOut.get(node.id);\n if (!elkNode) return node;\n return {\n ...node,\n origin: [0, 0] as [number, number],\n position: { x: elkNode.x ?? 0, y: elkNode.y ?? 0 },\n ...(groupIds.has(node.id) ||\n (GROUP_TYPES.has(node.type || \"\") &&\n typeof node.style?.width !== \"number\")\n ? {\n style: {\n ...node.style,\n width: elkNode.width,\n height: elkNode.height,\n },\n // Re-measured at its new size\n measured: undefined,\n }\n : {}),\n };\n }),\n edges: edges.map((edge) => {\n const route = routes.get(edge);\n return route ? { ...edge, data: { ...edge.data, route } } : edge;\n }),\n };\n};\n","import { MarkerType, type Edge, type Node } from \"@xyflow/react\";\nimport { COMMAND, EVENT, MESSAGE, QUERY } from \"../nodes/node-types\";\nimport { createEdge } from \"./utils/utils\";\nimport type { EdgeRoute } from \"./elk-layout\";\n\nconst MESSAGE_NODE_TYPES = [...MESSAGE, \"messageGroup\", \"messageGroupExpanded\"];\n\nexport const isMessageNode = (node: Node) =>\n MESSAGE_NODE_TYPES.includes(node.type || \"\");\n\nconst getCollection = (type = \"\") => {\n if (EVENT.includes(type)) return \"events\";\n if (COMMAND.includes(type)) return \"commands\";\n if (QUERY.includes(type)) return \"queries\";\n return \"messages\";\n};\n\n// Returns the messages a node represents, as [name, collection] pairs\nconst getMessages = (node: Node): [string, string][] => {\n const data = node.data as any;\n if (node.type === \"messageGroup\") {\n return (data?.messages || []).map(({ message }: any) => [\n message?.data?.name || message?.data?.id,\n getCollection(message?.collection),\n ]);\n }\n const message = data?.message;\n return message\n ? [[message.name || message.id, getCollection(node.type)]]\n : [];\n};\n\nconst VERBS: Record<string, string> = {\n events: \"publishes\",\n commands: \"invokes\",\n queries: \"requests\",\n};\n\n/**\n * Label for an edge carrying messages (name -> collection), e.g.\n * \"publishes\\nProduct Created\", \"publishes\\n3 events\", \"sends\\n3 messages\"\n */\nexport const getMessagesLabel = (messages: Map<string, string>) => {\n if (messages.size === 0) return undefined;\n const collections = new Set(messages.values());\n const collection =\n collections.size === 1 ? Array.from(collections)[0] : \"messages\";\n const verb = VERBS[collection] || \"sends\";\n const sent =\n messages.size === 1\n ? Array.from(messages.keys())[0]\n : `${messages.size} ${collection}`;\n return `${verb}\\n${sent}`;\n};\n\n// Messages and channels carry what's sent between the nodes either side of them\nexport const isCarrier = (node: Node) =>\n isMessageNode(node) || node.type === \"channels\";\n\nconst isCrossDomain = (edge: Edge) =>\n !!(edge.data as { crossDomain?: boolean } | undefined)?.crossDomain;\n\nconst getRoute = (edge: Edge) =>\n (edge.data as { route?: EdgeRoute } | undefined)?.route;\n\n/**\n * The route of an edge bridging hidden nodes: along the routes of the edges it\n * replaces (through where the hidden nodes were), labelled where the first\n * hidden node was. None unless they're all laid out.\n */\nconst joinRoutes = (routes: (EdgeRoute | undefined)[]) => {\n if (!routes.every((route) => route && route.points.length > 0)) return;\n const laidOut = routes as EdgeRoute[];\n const arrives = laidOut[0].points[laidOut[0].points.length - 1];\n const leaves = laidOut[1].points[0];\n return {\n points: laidOut.flatMap((route) => route.points),\n label: { x: (arrives.x + leaves.x) / 2, y: (arrives.y + leaves.y) / 2 },\n source: laidOut[0].source,\n target: laidOut[laidOut.length - 1].target,\n };\n};\n\n/**\n * Removes the nodes `isHidden` picks. Messages and channels removed are\n * bridged: the nodes either side of them are connected directly, labelled with\n * the messages between them. Other edges to removed nodes go with them.\n * e.g. hiding events, ServiceA -> OrderPlaced -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideNodes = (\n nodes: Node[],\n edges: Edge[],\n isHidden: (node: Node) => boolean,\n) => {\n const hiddenNodes = new Map(\n nodes.filter(isHidden).map((node) => [node.id, node]),\n );\n if (hiddenNodes.size === 0) return { nodes, edges };\n\n const outgoingEdges = new Map<string, Edge[]>();\n for (const edge of edges) {\n const outgoing = outgoingEdges.get(edge.source) || [];\n outgoing.push(edge);\n outgoingEdges.set(edge.source, outgoing);\n }\n\n const keptEdges: Edge[] = [];\n const bridgedEdges = new Map<\n string,\n {\n source: string;\n target: string;\n messages: Map<string, string>;\n route?: EdgeRoute;\n crossDomain: boolean;\n }\n >();\n\n for (const edge of edges) {\n if (hiddenNodes.has(edge.source)) continue;\n const target = hiddenNodes.get(edge.target);\n if (!target) {\n keptEdges.push(edge);\n continue;\n }\n if (!isCarrier(target)) continue;\n\n // Follow the edge through hidden messages and channels until we reach\n // visible nodes, collecting the names of the messages along the way\n const stack = [\n {\n id: edge.target,\n messages: [] as [string, string][],\n routes: [getRoute(edge)],\n crossDomain: isCrossDomain(edge),\n },\n ];\n const visited = new Set<string>();\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (visited.has(current.id)) continue;\n visited.add(current.id);\n const messages = [\n ...current.messages,\n ...getMessages(hiddenNodes.get(current.id)!),\n ];\n\n for (const next of outgoingEdges.get(current.id) || []) {\n const routes = [...current.routes, getRoute(next)];\n const crossDomain = current.crossDomain || isCrossDomain(next);\n const nextNode = hiddenNodes.get(next.target);\n if (nextNode) {\n if (isCarrier(nextNode))\n stack.push({ id: next.target, messages, routes, crossDomain });\n continue;\n }\n if (next.target === edge.source) continue;\n\n // Prefixed, so it can't clash with an edge directly between the two\n const id = `bridged-${edge.source}-${next.target}`;\n const bridged = bridgedEdges.get(id) || {\n source: edge.source,\n target: next.target,\n messages: new Map<string, string>(),\n route: joinRoutes(routes),\n crossDomain: false,\n };\n bridged.crossDomain ||= crossDomain;\n messages.forEach(([name, collection]) =>\n bridged.messages.set(name, collection),\n );\n bridgedEdges.set(id, bridged);\n }\n }\n }\n\n const newEdges = Array.from(bridgedEdges, ([id, bridged]) =>\n createEdge({\n id,\n source: bridged.source,\n target: bridged.target,\n label: getMessagesLabel(bridged.messages),\n // The same size arrow as the rest of the graph's edges\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 20,\n height: 20,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n // Along the same route, and crossing domains if any edge it replaces did\n data: {\n ...(bridged.route ? { route: bridged.route } : {}),\n ...(bridged.crossDomain ? { crossDomain: true } : {}),\n },\n }),\n );\n\n return {\n nodes: nodes.filter((node) => !hiddenNodes.has(node.id)),\n edges: [...keptEdges, ...newEdges],\n };\n};\n\n/**\n * Removes message nodes (events, commands, queries) and the channels they\n * travel through, connecting the nodes on either side directly.\n * e.g. ServiceA -> OrderPlaced -> orders-topic -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideMessageNodes = (nodes: Node[], edges: Edge[]) =>\n hideNodes(nodes, edges, isCarrier);\n","export const SERVICE = [\"service\", \"services\"];\nexport const AGENT = [\"agent\", \"agents\"];\nexport const AGENT_TOOL = [\"agentTool\", \"agent-tool\"];\nexport const EVENT = [\"event\", \"events\"];\nexport const QUERY = [\"queries\", \"query\", \"querie\"];\nexport const COMMAND = [\"command\", \"commands\"];\nexport const CHANNEL = [\"channel\", \"channels\"];\nexport const ACTOR = [\"actor\", \"actors\"];\nexport const DATA = [\"data\"];\nexport const VIEW = [\"view\"];\n\nexport const MESSAGE = [...EVENT, ...COMMAND, ...QUERY];\n","import type { Edge, Node } from \"@xyflow/react\";\nimport { getNodeSize, layoutWithElk, type LayoutSize } from \"./elk-layout\";\n\n/** The size a node is laid out at (by default, as last rendered or estimated) */\ntype SizeOf = (node: Node) => LayoutSize;\n\n/** What a flow's swimlanes group its steps by */\nexport type SwimlaneGroupBy = \"domain\" | \"system\" | \"team\";\n/** What a flow's steps can be grouped by: one of those, or domains with their systems in them */\nexport type FlowGroupBy = SwimlaneGroupBy | \"domain-system\";\n/**\n * How the groups are shown: full-width lanes stacked top to bottom, or boxes\n * around their steps (like the pools of a process diagram), placed by the layout\n */\nexport type FlowGroupStyle = \"lanes\" | \"boxes\";\n\nexport const SWIMLANE_NODE_TYPE = \"swimlane\";\n\n/** Every way a flow's steps can be grouped */\nexport const FLOW_GROUP_BYS: FlowGroupBy[] = [\n \"domain\",\n \"system\",\n \"team\",\n \"domain-system\",\n];\nexport const isFlowGroupBy = (value: unknown): value is FlowGroupBy =>\n FLOW_GROUP_BYS.includes(value as FlowGroupBy);\nexport const isFlowGroupStyle = (value: unknown): value is FlowGroupStyle =>\n value === \"lanes\" || value === \"boxes\";\n\nexport const SWIMLANE_GROUP_BY_LABELS: Record<FlowGroupBy, string> = {\n domain: \"Domain\",\n system: \"System\",\n team: \"Team\",\n \"domain-system\": \"Domain, then system\",\n};\n\n/** The lanes a node's resource belongs to (`data.lanes`, set by the catalog) */\ntype NodeLanes = Partial<\n Record<SwimlaneGroupBy, { id: string; name: string }>\n> & { external?: boolean };\n\nexport type SwimlaneNodeData = {\n label: string;\n /** How the graph is grouped */\n groupBy: FlowGroupBy;\n /** What this group is (a domain, a system or a team), for its colour and icon */\n level: SwimlaneGroupBy;\n style: FlowGroupStyle;\n /** The lane holds actors and external systems, or steps with no lane */\n kind: \"lane\" | \"external\" | \"unassigned\";\n /** Its place from the top, so neighbouring lanes can be told apart */\n index: number;\n};\n\nconst EXTERNAL_LANE = \"__external\";\nconst UNASSIGNED_LANE = \"__unassigned\";\n\n// Room on the left of each lane for its name, around its nodes, and between rows\nexport const LANE_HEADER_WIDTH = 96;\nconst LANE_PADDING = { x: 48, y: 48 };\nconst ROW_GAP = 32;\nconst COLUMN_GAP = 24;\n\n// The lane's name runs down its header, next to an icon\nexport const LANE_LABEL = { fontSize: 20, iconSize: 20, gap: 10 };\n// Room above and below the name, so it never touches the lane's edges\nconst LANE_LABEL_PADDING = 40;\n// How long a name renders, roughly (bold, about 0.62em a character)\nconst labelLength = (label: string) =>\n LANE_LABEL.iconSize +\n LANE_LABEL.gap +\n label.length * LANE_LABEL.fontSize * 0.62;\n\nconst lanesOf = (node: Node) =>\n (node.data as { lanes?: NodeLanes } | undefined)?.lanes;\n\nexport const isSwimlaneNode = (node: Node) => node.type === SWIMLANE_NODE_TYPE;\n\n/** Whether any of the nodes belong to a group when grouped this way */\nexport const hasSwimlanes = (nodes: Node[], groupBy: FlowGroupBy): boolean =>\n groupBy === \"domain-system\"\n ? hasSwimlanes(nodes, \"domain\") && hasSwimlanes(nodes, \"system\")\n : nodes.some((node) => !!lanesOf(node)?.[groupBy]);\n\n/**\n * How many groups the nodes make when grouped this way (domains, for domains\n * with their systems in them). Actors, external systems and steps that only\n * take a neighbour's group aren't counted.\n */\nexport const countGroups = (nodes: Node[], groupBy: FlowGroupBy): number => {\n const level = groupBy === \"domain-system\" ? \"domain\" : groupBy;\n return new Set(\n nodes.map((node) => lanesOf(node)?.[level]?.id).filter(Boolean),\n ).size;\n};\n\n/**\n * Which lane each node goes in. Nodes whose resource belongs to a domain,\n * system or team (services, agents, data stores...) go in its lane; actors\n * and external systems in the external lane. Other nodes (messages, custom\n * steps, sub-flows) go in the lane of the step that leads to them, or, for\n * steps at the start of the flow, of the step they lead to. Resources that\n * don't belong to a group of this kind are in no lane.\n */\nexport const assignLanes = (\n nodes: Node[],\n edges: Edge[],\n groupBy: SwimlaneGroupBy,\n) => {\n const laneOf = new Map<string, string>();\n const names = new Map<string, string>();\n nodes.forEach((node) => {\n const lanes = lanesOf(node);\n const lane = lanes?.[groupBy];\n if (lane) {\n laneOf.set(node.id, lane.id);\n names.set(lane.id, lane.name);\n } else if (lanes?.external) {\n laneOf.set(node.id, EXTERNAL_LANE);\n }\n });\n\n const ids = new Set(nodes.map((node) => node.id));\n // Steps for resources belong where the catalog says (or nowhere), never\n // where a neighbour does: a service with a domain but no system isn't in\n // the system of the step before it\n const ownLanes = new Set(\n nodes.filter((node) => lanesOf(node)).map((node) => node.id),\n );\n const links = edges.filter(\n (edge) => ids.has(edge.source) && ids.has(edge.target),\n );\n // Take the lane of a neighbour until nothing changes (a chain of messages\n // and custom steps takes as many passes as it's long)\n const inherit = (from: \"source\" | \"target\", to: \"source\" | \"target\") => {\n let changed = true;\n while (changed) {\n changed = false;\n links.forEach((edge) => {\n const lane = laneOf.get(edge[from]);\n if (lane && !laneOf.has(edge[to]) && !ownLanes.has(edge[to])) {\n laneOf.set(edge[to], lane);\n changed = true;\n }\n });\n }\n };\n inherit(\"source\", \"target\");\n inherit(\"target\", \"source\");\n nodes.forEach((node) => {\n if (!laneOf.has(node.id)) laneOf.set(node.id, UNASSIGNED_LANE);\n });\n\n return { laneOf, names };\n};\n\ntype Box = { x: number; y: number; width: number; height: number };\n\n/**\n * Lays a laid out flow out again in swimlanes: one full-width band per lane,\n * stacked top to bottom in the order the flow first reaches them (external\n * first, unassigned last). Nodes keep their place along the flow (x) and are\n * stacked in rows inside their lane where they'd overlap. Lanes are group\n * nodes their nodes are children of, so edges are drawn over them. Edges lose\n * the route the layout gave them, and are drawn as smooth steps instead.\n *\n * Only top level nodes are put in lanes (children of an expanded sub-flow stay\n * in it).\n */\nexport const layoutSwimlanes = (\n { nodes, edges }: { nodes: Node[]; edges: Edge[] },\n groupBy: SwimlaneGroupBy,\n { sizeOf = getNodeSize }: { sizeOf?: SizeOf } = {},\n): { nodes: Node[]; edges: Edge[] } => {\n const topLevel = nodes.filter((node) => !node.parentId);\n if (topLevel.length === 0) return { nodes, edges };\n const { laneOf, names } = assignLanes(topLevel, edges, groupBy);\n\n const boxes = new Map<string, Box>(\n topLevel.map((node) => [node.id, { ...node.position, ...sizeOf(node) }]),\n );\n\n // Lanes in the order the flow reaches them, external first, unassigned last\n const order = (lane: string) =>\n lane === EXTERNAL_LANE\n ? -Infinity\n : lane === UNASSIGNED_LANE\n ? Infinity\n : Math.min(\n ...topLevel\n .filter((node) => laneOf.get(node.id) === lane)\n .map((node) => boxes.get(node.id)!.x),\n );\n const lanes = [...new Set(laneOf.values())].sort(\n (a, b) => order(a) - order(b),\n );\n\n const minX = Math.min(...[...boxes.values()].map((box) => box.x));\n const maxX = Math.max(...[...boxes.values()].map((box) => box.x + box.width));\n const laneX = minX - LANE_HEADER_WIDTH - LANE_PADDING.x;\n const laneWidth = maxX - laneX + LANE_PADDING.x;\n\n const laneNodes: Node[] = [];\n const positions = new Map<string, { x: number; y: number }>();\n let laneY = 0;\n lanes.forEach((lane, index) => {\n // Top to bottom as laid out, each in the first row it doesn't overlap\n const members = topLevel\n .filter((node) => laneOf.get(node.id) === lane)\n .sort((a, b) => boxes.get(a.id)!.y - boxes.get(b.id)!.y);\n const rows: { boxes: Box[]; height: number }[] = [];\n const rowOf = new Map<string, number>();\n members.forEach((node) => {\n const box = boxes.get(node.id)!;\n const overlaps = (other: Box) =>\n box.x < other.x + other.width + COLUMN_GAP &&\n other.x < box.x + box.width + COLUMN_GAP;\n let row = rows.findIndex((r) => !r.boxes.some(overlaps));\n if (row === -1) {\n rows.push({ boxes: [], height: 0 });\n row = rows.length - 1;\n }\n rows[row].boxes.push(box);\n rows[row].height = Math.max(rows[row].height, box.height);\n rowOf.set(node.id, row);\n });\n\n const label =\n lane === EXTERNAL_LANE\n ? \"External\"\n : lane === UNASSIGNED_LANE\n ? `No ${SWIMLANE_GROUP_BY_LABELS[groupBy].toLowerCase()}`\n : (names.get(lane) ?? lane);\n\n // Tall enough for its rows and for its name, with the rows centred\n const rowsHeight =\n rows.reduce((total, row) => total + row.height, 0) +\n ROW_GAP * (rows.length - 1);\n const laneHeight = Math.max(\n rowsHeight + LANE_PADDING.y * 2,\n labelLength(label) + LANE_LABEL_PADDING * 2,\n );\n const rowTops: number[] = [];\n let rowY = (laneHeight - rowsHeight) / 2;\n rows.forEach((row) => {\n rowTops.push(rowY);\n rowY += row.height + ROW_GAP;\n });\n\n const laneId = `swimlane-${lane}`;\n members.forEach((node) => {\n const box = boxes.get(node.id)!;\n const row = rowOf.get(node.id)!;\n positions.set(node.id, {\n x: box.x - laneX,\n // Centred in its row\n y: rowTops[row] + (rows[row].height - box.height) / 2,\n });\n });\n\n laneNodes.push({\n id: laneId,\n type: SWIMLANE_NODE_TYPE,\n position: { x: laneX, y: laneY },\n origin: [0, 0],\n style: { width: laneWidth, height: laneHeight },\n draggable: false,\n selectable: false,\n focusable: false,\n data: {\n label,\n groupBy,\n level: groupBy,\n style: \"lanes\",\n kind:\n lane === EXTERNAL_LANE\n ? \"external\"\n : lane === UNASSIGNED_LANE\n ? \"unassigned\"\n : \"lane\",\n index,\n } satisfies SwimlaneNodeData,\n });\n laneY += laneHeight;\n });\n\n return {\n // Lanes come before their nodes, as React Flow needs parents first\n nodes: [\n ...laneNodes,\n ...nodes.map((node) => {\n const position = positions.get(node.id);\n if (!position) return node;\n return {\n ...node,\n parentId: `swimlane-${laneOf.get(node.id)}`,\n origin: [0, 0] as [number, number],\n position,\n };\n }),\n ],\n edges: edges.map((edge) => {\n if (!(edge.data as { route?: unknown } | undefined)?.route) return edge;\n const { route: _route, ...data } = edge.data as Record<string, unknown>;\n return { ...edge, data };\n }),\n };\n};\n\n/** How a graph is grouped, if it is */\nexport const getGrouping = (\n nodes: Node[],\n): { groupBy: FlowGroupBy; style: FlowGroupStyle } | null => {\n const data = nodes.find(isSwimlaneNode)?.data as SwimlaneNodeData | undefined;\n return data ? { groupBy: data.groupBy, style: data.style } : null;\n};\n\n/**\n * Takes a graph out of its groups (lanes or boxes): the groups go, and their\n * nodes stay where they are (their positions no longer relative to their\n * group). For laying a grouped graph out again, which lays it out without them.\n */\nexport const removeSwimlanes = ({\n nodes,\n edges,\n}: {\n nodes: Node[];\n edges: Edge[];\n}): { nodes: Node[]; edges: Edge[] } => {\n const groups = new Map(\n nodes.filter(isSwimlaneNode).map((group) => [group.id, group]),\n );\n if (groups.size === 0) return { nodes, edges };\n // Where a group is in the flow (groups can be in groups)\n const offset = (id: string | undefined): { x: number; y: number } => {\n const group = id ? groups.get(id) : undefined;\n if (!group) return { x: 0, y: 0 };\n const parent = offset(group.parentId);\n return { x: parent.x + group.position.x, y: parent.y + group.position.y };\n };\n return {\n nodes: nodes\n .filter((node) => !groups.has(node.id))\n .map((node) => {\n if (!node.parentId || !groups.has(node.parentId)) return node;\n const { parentId, ...rest } = node;\n const at = offset(parentId);\n return {\n ...rest,\n position: { x: at.x + node.position.x, y: at.y + node.position.y },\n };\n }),\n edges,\n };\n};\n\n// Room around the steps in a box, and on its left for its name\nconst BOX_PADDING = 40;\n\n/**\n * Groups a flow's steps into boxes, one per domain, system or team (for\n * domains with their systems, a box per system inside its domain's box), and\n * lays the flow out again with them, so edges are routed around the boxes.\n * Steps go in the same group as they would in lanes; steps in no group stay\n * outside the boxes. Boxes are at least as tall as their name.\n *\n * Only top level nodes are put in boxes (children of an expanded sub-flow stay\n * in it).\n */\nexport const groupIntoBoxes = async (\n { nodes, edges }: { nodes: Node[]; edges: Edge[] },\n groupBy: FlowGroupBy,\n { sizeOf = getNodeSize }: { sizeOf?: SizeOf } = {},\n): Promise<{ nodes: Node[]; edges: Edge[] }> => {\n const topLevel = nodes.filter((node) => !node.parentId);\n const levels: SwimlaneGroupBy[] =\n groupBy === \"domain-system\" ? [\"domain\", \"system\"] : [groupBy];\n const assignments = levels.map((level) =>\n assignLanes(topLevel, edges, level),\n );\n\n const groups = new Map<string, Node>();\n const parentOf = new Map<string, string>();\n topLevel.forEach((node) => {\n let parentId: string | undefined;\n assignments.forEach(({ laneOf, names }, depth) => {\n const lane = laneOf.get(node.id)!;\n // External steps have one box, whatever they're grouped by\n if (lane === UNASSIGNED_LANE || (depth > 0 && lane === EXTERNAL_LANE))\n return;\n const level = levels[depth];\n const id = `${parentId ?? \"swimlane\"}__${level}-${lane}`;\n if (!groups.has(id)) {\n const label =\n lane === EXTERNAL_LANE ? \"External\" : (names.get(lane) ?? lane);\n const minHeight = labelLength(label) + LANE_LABEL_PADDING * 2;\n groups.set(id, {\n id,\n type: SWIMLANE_NODE_TYPE,\n ...(parentId && { parentId }),\n position: { x: 0, y: 0 },\n selectable: false,\n focusable: false,\n data: {\n label,\n groupBy,\n level,\n style: \"boxes\",\n kind: lane === EXTERNAL_LANE ? \"external\" : \"lane\",\n index: groups.size,\n // Room for its name on the left, and at least as tall as it\n layoutOptions: {\n \"elk.padding\": `[top=${BOX_PADDING},left=${LANE_HEADER_WIDTH + BOX_PADDING},bottom=${BOX_PADDING},right=${BOX_PADDING}]`,\n \"elk.nodeSize.constraints\": \"MINIMUM_SIZE\",\n \"elk.nodeSize.minimum\": `(0, ${Math.ceil(minHeight)})`,\n },\n } satisfies SwimlaneNodeData & { layoutOptions: object },\n });\n }\n parentId = id;\n });\n if (parentId) parentOf.set(node.id, parentId);\n });\n\n return layoutWithElk(\n {\n nodes: [\n ...groups.values(),\n ...nodes.map((node) => {\n const parentId = parentOf.get(node.id);\n return parentId ? { ...node, parentId } : node;\n }),\n ],\n edges,\n },\n { sizeOf },\n );\n};\n","import type { Node, Edge } from \"@xyflow/react\";\nimport { isSwimlaneNode } from \"./swimlanes\";\n\n/**\n * Mermaid Export Utility\n * Converts React Flow nodes and edges to Mermaid flowchart syntax. Used by the\n * visualiser's Mermaid view and by EventCatalog (the `.mermaid` routes, AI Chat\n * and the MCP server), so every level of a diagram exports the same way.\n */\n\nexport interface MermaidExportOptions {\n /** Include class definitions for styling */\n includeStyles?: boolean;\n /** Direction of the flowchart: LR (left-right), TB (top-bottom), etc. */\n direction?: \"LR\" | \"TB\" | \"RL\" | \"BT\";\n}\n\n/**\n * Mapping of node types to their Mermaid shape syntax\n * Format: [prefix, suffix]\n */\nconst NODE_SHAPE_MAP: Record<string, [string, string]> = {\n agents: [\"[[\", \"]]\"],\n agent: [\"[[\", \"]]\"],\n agentTool: [\"[\", \"]\"],\n \"agent-tool\": [\"[\", \"]\"],\n services: [\"[[\", \"]]\"], // stadium shape\n service: [\"[[\", \"]]\"],\n events: [\">\", \"]\"], // flag/asymmetric shape (message-like)\n event: [\">\", \"]\"],\n commands: [\">\", \"]\"], // flag/asymmetric shape (message-like)\n command: [\">\", \"]\"],\n queries: [\"{{\", \"}}\"], // hexagon\n query: [\"{{\", \"}}\"],\n // Parallelogram: what messages pass through, not a cylinder, which reads as\n // a database (and is what data stores are drawn as)\n channels: [\"[/\", \"/]\"],\n channel: [\"[/\", \"/]\"],\n domains: [\"[\", \"]\"], // rectangle\n domain: [\"[\", \"]\"],\n \"context-domain\": [\"[\", \"]\"], // another domain, shown as a single card\n flows: [\"([\", \"])\"], // stadium (rounded)\n flow: [\"([\", \"])\"],\n flowExpanded: [\"([\", \"])\"],\n systems: [\"[[\", \"]]\"], // a system shown as a single node\n system: [\"[[\", \"]]\"],\n step: [\"[\", \"]\"], // rectangle\n user: [\"((\", \"))\"], // circle\n actor: [\"((\", \"))\"], // circle\n \"context-actor\": [\"((\", \"))\"], // circle\n externalSystem: [\"[[\", \"]]\"], // stadium\n \"external-system\": [\"[[\", \"]]\"],\n data: [\"[(\", \")]\"], // cylinder (database)\n container: [\"[(\", \")]\"],\n \"data-product\": [\"[[\", \"]]\"], // stadium\n \"data-products\": [\"[[\", \"]]\"],\n entities: [\"[\", \"]\"], // rectangle\n entity: [\"[\", \"]\"],\n custom: [\"[\", \"]\"], // rectangle\n view: [\"[\", \"]\"], // rectangle\n note: [\"[\", \"]\"], // rectangle\n field: [\"[(\", \")]\"], // cylinder\n fields: [\"[(\", \")]\"], // cylinder\n messageGroup: [\"[[\", \"]]\"],\n messageGroupExpanded: [\"[[\", \"]]\"],\n};\n\n/**\n * Mermaid class definitions for styling different node types\n */\nconst NODE_STYLE_CLASSES: Record<string, string> = {\n agents: \"fill:#0ea5e9,stroke:#0369a1,color:#fff\",\n agent: \"fill:#0ea5e9,stroke:#0369a1,color:#fff\",\n agentTool: \"fill:#8b5cf6,stroke:#6d28d9,color:#fff\",\n \"agent-tool\": \"fill:#8b5cf6,stroke:#6d28d9,color:#fff\",\n services: \"fill:#ec4899,stroke:#be185d,color:#fff\",\n service: \"fill:#ec4899,stroke:#be185d,color:#fff\",\n events: \"fill:#f97316,stroke:#c2410c,color:#fff\",\n event: \"fill:#f97316,stroke:#c2410c,color:#fff\",\n commands: \"fill:#3b82f6,stroke:#1d4ed8,color:#fff\",\n command: \"fill:#3b82f6,stroke:#1d4ed8,color:#fff\",\n queries: \"fill:#22c55e,stroke:#15803d,color:#fff\",\n query: \"fill:#22c55e,stroke:#15803d,color:#fff\",\n channels: \"fill:#6b7280,stroke:#374151,color:#fff\",\n channel: \"fill:#6b7280,stroke:#374151,color:#fff\",\n domains: \"fill:#eab308,stroke:#a16207,color:#000\",\n domain: \"fill:#eab308,stroke:#a16207,color:#000\",\n \"context-domain\": \"fill:#eab308,stroke:#a16207,color:#000\",\n flows: \"fill:#14b8a6,stroke:#0f766e,color:#fff\",\n flow: \"fill:#14b8a6,stroke:#0f766e,color:#fff\",\n flowExpanded: \"fill:#14b8a6,stroke:#0f766e,color:#fff\",\n systems: \"fill:#8b5cf6,stroke:#6d28d9,color:#fff\",\n system: \"fill:#8b5cf6,stroke:#6d28d9,color:#fff\",\n step: \"fill:#374151,stroke:#1f2937,color:#fff\",\n user: \"fill:#8b5cf6,stroke:#6d28d9,color:#fff\",\n actor: \"fill:#eab308,stroke:#a16207,color:#000\",\n \"context-actor\": \"fill:#eab308,stroke:#a16207,color:#000\", // yellow, as actors are on the diagram\n externalSystem: \"fill:#ec4899,stroke:#be185d,color:#fff\",\n \"external-system\": \"fill:#ec4899,stroke:#be185d,color:#fff\",\n data: \"fill:#3b82f6,stroke:#1d4ed8,color:#fff\",\n container: \"fill:#3b82f6,stroke:#1d4ed8,color:#fff\",\n \"data-product\": \"fill:#6366f1,stroke:#4338ca,color:#fff\",\n \"data-products\": \"fill:#6366f1,stroke:#4338ca,color:#fff\",\n entities: \"fill:#6b7280,stroke:#374151,color:#fff\",\n entity: \"fill:#6b7280,stroke:#374151,color:#fff\",\n custom: \"fill:#9ca3af,stroke:#6b7280,color:#000\",\n view: \"fill:#9ca3af,stroke:#6b7280,color:#000\",\n note: \"fill:#fef3c7,stroke:#d97706,color:#000\",\n field: \"fill:#06b6d4,stroke:#0891b2,color:#fff\",\n fields: \"fill:#06b6d4,stroke:#0891b2,color:#fff\",\n messageGroup: \"fill:#7c3aed,stroke:#5b21b6,color:#fff\",\n messageGroupExpanded: \"fill:#7c3aed,stroke:#5b21b6,color:#fff\",\n};\n\n/** Node types that are boundaries around other nodes, rendered as subgraphs */\nconst GROUP_NODE_TYPES = new Set([\"system-group\", \"domain-group\", \"group\"]);\n\n/**\n * Classes for the boundaries (subgraphs): the domain viewed, its subdomains,\n * other domains, systems and the lanes a flow's steps are grouped into\n */\nconst GROUP_STYLE_CLASSES = {\n domainGroup:\n \"fill:#eab30814,stroke:#ca8a04,stroke-width:2px,stroke-dasharray:6 4,color:#a16207\",\n subdomainGroup:\n \"fill:#eab3080d,stroke:#ca8a04,stroke-dasharray:4 4,color:#a16207\",\n otherDomainGroup:\n \"fill:#9ca3af14,stroke:#9ca3af,stroke-dasharray:4 4,color:#6b7280\",\n systemGroup: \"fill:#8b5cf614,stroke:#8b5cf6,stroke-width:2px,color:#7c3aed\",\n laneGroup: \"fill:#9ca3af0d,stroke:#9ca3af,stroke-dasharray:4 4,color:#6b7280\",\n} as const;\n\ntype GroupClass = keyof typeof GROUP_STYLE_CLASSES;\n\n/** The resource the diagram is about */\nconst FOCUSED_STYLE = \"stroke-width:4px\";\n\n/** Edges to or from another domain, highlighted as the visualiser does */\nconst CROSS_DOMAIN_LINK_STYLE = \"stroke:#4f46e5,stroke-width:3px\";\n\n/** Words Mermaid reads as keywords, so they can't be node ids */\nconst RESERVED_IDS = new Set([\n \"end\",\n \"subgraph\",\n \"graph\",\n \"flowchart\",\n \"style\",\n \"class\",\n \"classdef\",\n \"click\",\n \"linkstyle\",\n \"call\",\n \"href\",\n]);\n\n/**\n * Sanitize a string to be a valid Mermaid node ID\n * Mermaid IDs should be alphanumeric with underscores\n */\nexport function sanitizeMermaidId(id: string): string {\n // Replace any non-alphanumeric characters (except underscores) with underscores\n // Also handle dots and hyphens which are common in version strings\n const sanitized = id.replace(/[^a-zA-Z0-9_]/g, \"_\");\n return RESERVED_IDS.has(sanitized.toLowerCase())\n ? `node_${sanitized}`\n : sanitized;\n}\n\n/**\n * Collapses whitespace in each line of a label (e.g. \"reads from \\n (postgres)\"\n * from the graph builders), dropping empty lines\n */\nfunction normaliseLines(label: string): string[] {\n return label\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+/g, \" \").trim())\n .filter(Boolean);\n}\n\n/**\n * Escape special characters in Mermaid labels\n */\nfunction escapeMermaidLabel(label: string): string {\n // `#` first, as the escapes themselves start with it\n return label.replace(/#/g, \"#35;\").replace(/\"/g, \"#quot;\");\n}\n\n/**\n * Get the Mermaid shape syntax for a node type\n */\nexport function getMermaidNodeShape(type: string): [string, string] {\n return NODE_SHAPE_MAP[type] || [\"[\", \"]\"];\n}\n\n/**\n * Helper to format label with version\n */\nfunction formatLabelWithVersion(name: string, version?: string): string {\n if (version) {\n return `${name} (${version})`;\n }\n return name;\n}\n\n/** Name and version of a resource, whether flat or nested in `.data` (a collection entry) */\nfunction resourceLabel(resource: any, fallback: string): string {\n const name =\n resource?.name ||\n resource?.data?.name ||\n resource?.id ||\n resource?.data?.id;\n const version = resource?.version || resource?.data?.version;\n return formatLabelWithVersion(name || fallback, version);\n}\n\n/**\n * Extract the display label from a node based on its type and data\n * Includes version information when available\n */\nexport function getNodeLabel(node: Node): string {\n const { type } = node;\n const data = node.data as any;\n\n if (!data) return node.id;\n\n // Handle different node types and their data structures\n if (type === \"agents\" || type === \"agent\") {\n return resourceLabel(data.agent, node.id);\n }\n\n if (type === \"agentTool\" || type === \"agent-tool\") {\n const agentTool = data.agentTool;\n return agentTool?.name || agentTool?.id || node.id;\n }\n\n if (type === \"services\" || type === \"service\") {\n return resourceLabel(data.service, node.id);\n }\n\n if (\n type === \"events\" ||\n type === \"event\" ||\n type === \"commands\" ||\n type === \"command\" ||\n type === \"queries\" ||\n type === \"query\"\n ) {\n return resourceLabel(data.message, node.id);\n }\n\n if (type === \"channels\" || type === \"channel\") {\n return resourceLabel(data.channel, node.id);\n }\n\n // A domain: as a node, a card (e.g. another domain) or a boundary around its systems\n if (\n type === \"domains\" ||\n type === \"domain\" ||\n type === \"context-domain\" ||\n type === \"domain-group\" ||\n type === \"group\"\n ) {\n return resourceLabel(data.domain, node.id);\n }\n\n // A system: as a node, or a boundary around what's in it\n if (type === \"systems\" || type === \"system\" || type === \"system-group\") {\n return resourceLabel(data.system, node.id);\n }\n\n if (type === \"flows\" || type === \"flow\") {\n return resourceLabel(data.flow, node.id);\n }\n\n if (type === \"flowExpanded\") {\n return formatLabelWithVersion(data.flowName || node.id, data.version);\n }\n\n if (type === \"step\") {\n const step = data.step;\n return step?.title || step?.name || step?.id || node.id;\n }\n\n if (type === \"custom\") {\n return data.custom?.title || data.step?.title || data.label || node.id;\n }\n\n if (type === \"externalSystem\" || type === \"external-system\") {\n const system = data.externalSystem || data;\n return system?.name || system?.id || node.id;\n }\n\n if (type === \"data\" || type === \"container\") {\n const dataNode = data.data;\n return dataNode?.name || dataNode?.id || node.id;\n }\n\n if (type === \"data-product\" || type === \"data-products\") {\n return resourceLabel(data.dataProduct, node.id);\n }\n\n if (type === \"entities\" || type === \"entity\") {\n const entity = data.entity;\n return entity?.name || entity?.id || node.id;\n }\n\n if (type === \"view\") {\n return data.view?.name || data.view?.id || node.id;\n }\n\n if (type === \"note\") {\n return data.text || data.label || \"Note\";\n }\n\n if (type === \"field\" || type === \"fields\") {\n const name = data.name || node.id;\n return data.type ? `${name} (${data.type})` : name;\n }\n\n if (type === \"messageGroup\" || type === \"messageGroupExpanded\") {\n const groupName = data.groupName || node.id;\n const count = data.messageCount || 0;\n return `${groupName} (${count} messages)`;\n }\n\n // Fallback: try common data patterns (e.g. users, actors, swimlanes)\n return data.name || data.label || data.title || data.id || node.id;\n}\n\n/**\n * Extract label from an edge\n */\nfunction getEdgeLabel(edge: Edge): string | undefined {\n if (edge.label && typeof edge.label === \"string\") {\n return edge.label;\n }\n if (edge.data?.label && typeof edge.data.label === \"string\") {\n return edge.data.label;\n }\n return undefined;\n}\n\n/** Mermaid ids for the nodes, kept unique when different ids sanitize the same way */\nfunction getMermaidIds(nodes: Node[]): Map<string, string> {\n const mermaidIds = new Map<string, string>();\n const usedIds = new Set<string>();\n nodes.forEach((node) => {\n const base = sanitizeMermaidId(node.id);\n let id = base;\n for (let n = 2; usedIds.has(id); n++) id = `${base}_${n}`;\n usedIds.add(id);\n mermaidIds.set(node.id, id);\n });\n return mermaidIds;\n}\n\nconst isEntityNode = (node: Node) =>\n node.type === \"entities\" || node.type === \"entity\";\n\n/** An entity's property, as in its frontmatter */\ntype EntityProperty = {\n name: string;\n type: string;\n required?: boolean;\n references?: string;\n relationType?: string;\n items?: { type: string };\n};\n\n/** Relationships named only for their cardinality: labelled with the property instead */\nconst CARDINALITY_RELATIONS = new Set([\n \"hasOne\",\n \"hasMany\",\n \"belongsTo\",\n \"references\",\n]);\n\n/** External entities (from other domains) and aggregate roots, styled as on the diagram */\nconst ENTITY_STYLE_CLASSES = {\n externalEntity: \"fill:#9ca3af1a,stroke:#9ca3af,stroke-dasharray:4 4\",\n aggregateRoot: \"stroke-width:3px\",\n} as const;\n\n/**\n * An attribute's type or name, as Mermaid allows them: letters, digits,\n * hyphens, underscores, brackets and dots, starting with a letter\n */\nfunction sanitizeErToken(value: string, fallback: string): string {\n const token = value.trim().replace(/[^A-Za-z0-9_\\-[\\]().]/g, \"_\");\n return /^[A-Za-z_]/.test(token) ? token : `${fallback}_${token}`;\n}\n\n/**\n * Crow's foot notation for a relationship, from the entity holding the\n * property: it has many (an array, or `hasMany`), it has one, or (for\n * `belongsTo`, `references` and other relations) many of it point at one\n */\nfunction getErCardinality(property: EntityProperty): string {\n const required = !!property.required;\n if (property.relationType === \"hasMany\" || property.type === \"array\") {\n return required ? \"||--|{\" : \"||--o{\";\n }\n if (property.relationType === \"hasOne\") {\n return required ? \"||--||\" : \"||--o|\";\n }\n return required ? \"}o--||\" : \"}o--o|\";\n}\n\n/**\n * An entity map as a Mermaid entity relationship diagram: each entity with its\n * properties (the identifier as PK, references as FK), and a relationship for\n * each edge drawn between them, with its cardinality\n */\nexport function convertEntitiesToMermaid(\n nodes: Node[],\n edges: Edge[],\n { includeStyles = true }: Pick<MermaidExportOptions, \"includeStyles\"> = {},\n): string {\n const mermaidIds = getMermaidIds(nodes);\n const lines = [\"erDiagram\"];\n const classes = new Map<keyof typeof ENTITY_STYLE_CLASSES, string[]>();\n const addClass = (name: keyof typeof ENTITY_STYLE_CLASSES, id: string) =>\n classes.set(name, [...(classes.get(name) ?? []), id]);\n\n nodes.forEach((node) => {\n const data = node.data as any;\n const entity = data?.entity?.data ?? {};\n const id = mermaidIds.get(node.id)!;\n const name = entity.name || data?.label || node.id;\n // Entities from other domains say which domain they're in\n const label =\n data?.externalToDomain && data?.domainName\n ? `${name} (${data.domainName})`\n : name;\n const references = new Set<string>(data?.referencePropertyNames ?? []);\n const properties: EntityProperty[] = entity.properties ?? [];\n\n const attributes = properties.map((property) => {\n const type =\n property.type === \"array\" && property.items?.type\n ? `${property.items.type}[]`\n : property.type || \"unknown\";\n const keys = [\n property.name === entity.identifier && \"PK\",\n (property.references || references.has(property.name)) && \"FK\",\n ].filter(Boolean);\n return ` ${sanitizeErToken(type, \"t\")} ${sanitizeErToken(property.name, \"p\")}${keys.length ? ` ${keys.join(\", \")}` : \"\"}`;\n });\n\n const definition = ` ${id}[\"${escapeMermaidLabel(label)}\"]`;\n lines.push(\n ...(attributes.length\n ? [`${definition} {`, ...attributes, \" }\"]\n : [definition]),\n );\n if (data?.externalToDomain) addClass(\"externalEntity\", id);\n if (entity.aggregateRoot) addClass(\"aggregateRoot\", id);\n });\n\n const nodesById = new Map(nodes.map((node) => [node.id, node]));\n const relationships = edges\n .filter(\n (edge) =>\n !edge.hidden &&\n mermaidIds.has(edge.source) &&\n mermaidIds.has(edge.target),\n )\n .map((edge) => {\n // The property the relationship is drawn from (its handle is `{name}-source`)\n const propertyName = edge.sourceHandle?.replace(/-source$/, \"\");\n const properties: EntityProperty[] =\n (nodesById.get(edge.source)?.data as any)?.entity?.data?.properties ??\n [];\n const property = properties.find((p) => p.name === propertyName);\n const relation = property?.relationType?.trim();\n const label =\n relation && !CARDINALITY_RELATIONS.has(relation)\n ? relation\n : propertyName || getEdgeLabel(edge) || \"references\";\n const cardinality = property ? getErCardinality(property) : \"}o--o|\";\n return ` ${mermaidIds.get(edge.source)} ${cardinality} ${mermaidIds.get(edge.target)} : \"${escapeMermaidLabel(label)}\"`;\n });\n\n if (relationships.length > 0) lines.push(\"\", ...relationships);\n\n if (includeStyles && classes.size > 0) {\n lines.push(\"\");\n classes.forEach((ids, name) => {\n lines.push(` classDef ${name} ${ENTITY_STYLE_CLASSES[name]}`);\n lines.push(` class ${ids.join(\",\")} ${name}`);\n });\n }\n\n return lines.join(\"\\n\");\n}\n\n/** The class of a boundary: what kind of domain it is, a system, or a flow's lane */\nfunction getGroupClass(node: Node): GroupClass {\n const data = node.data as any;\n if (isSwimlaneNode(node)) return \"laneGroup\";\n if (node.type === \"system-group\") return \"systemGroup\";\n if (data?.otherDomain) return \"otherDomainGroup\";\n if (data?.subdomain) return \"subdomainGroup\";\n return \"domainGroup\";\n}\n\nconst isCrossDomain = (edge: Edge) =>\n !!(edge.data as { crossDomain?: boolean } | undefined)?.crossDomain;\n\n/**\n * Convert React Flow nodes and edges to Mermaid flowchart syntax. Domains,\n * systems and a flow's lanes are subgraphs around what's in them (nested, as\n * on the diagram), so every level of a domain or system diagram keeps its\n * boundaries. Edges between domains are highlighted.\n */\nexport function convertToMermaid(\n graphNodes: Node[],\n graphEdges: Edge[],\n options: MermaidExportOptions = {},\n): string {\n const { includeStyles = true, direction = \"LR\" } = options;\n const nodes = graphNodes.filter((node) => !node.hidden);\n\n // An entity map is a data model, so it's an entity relationship diagram\n if (nodes.length > 0 && nodes.every(isEntityNode)) {\n return convertEntitiesToMermaid(nodes, graphEdges, { includeStyles });\n }\n\n const mermaidIds = getMermaidIds(nodes);\n\n const lines: string[] = [];\n\n // Add flowchart header\n lines.push(`flowchart ${direction}`);\n\n // Nodes inside a group (a domain, system or lane) are nested in a subgraph\n // for that group, so containment is kept\n const childrenOf = new Map<string, Node[]>();\n const topLevelNodes: Node[] = [];\n nodes.forEach((node) => {\n if (node.parentId && mermaidIds.has(node.parentId)) {\n childrenOf.set(node.parentId, [\n ...(childrenOf.get(node.parentId) ?? []),\n node,\n ]);\n } else {\n topLevelNodes.push(node);\n }\n });\n const isGroup = (node: Node) =>\n childrenOf.has(node.id) ||\n GROUP_NODE_TYPES.has(node.type ?? \"\") ||\n isSwimlaneNode(node);\n\n // Collect used node types and boundary classes for class definitions\n const usedTypes = new Set<string>();\n const groupsByClass = new Map<GroupClass, string[]>();\n const focusedIds: string[] = [];\n nodes.forEach((node) => {\n const id = mermaidIds.get(node.id)!;\n if (isGroup(node)) {\n const groupClass = getGroupClass(node);\n groupsByClass.set(groupClass, [\n ...(groupsByClass.get(groupClass) ?? []),\n id,\n ]);\n return;\n }\n if (node.type && NODE_STYLE_CLASSES[node.type]) usedTypes.add(node.type);\n if ((node.data as any)?.isFocused) focusedIds.push(id);\n });\n\n // Add class definitions if styles are enabled\n if (includeStyles) {\n lines.push(\"\");\n lines.push(\" %% Style definitions\");\n usedTypes.forEach((type) => {\n lines.push(\n ` classDef ${sanitizeMermaidId(type)} ${NODE_STYLE_CLASSES[type]}`,\n );\n });\n groupsByClass.forEach((_, groupClass) => {\n lines.push(\n ` classDef ${groupClass} ${GROUP_STYLE_CLASSES[groupClass]}`,\n );\n });\n if (focusedIds.length > 0) {\n lines.push(` classDef focused ${FOCUSED_STYLE}`);\n }\n }\n\n // Add node definitions\n lines.push(\"\");\n lines.push(\" %% Nodes\");\n\n const addNode = (node: Node, depth: number) => {\n const indent = \" \".repeat(depth);\n const id = mermaidIds.get(node.id)!;\n const label = escapeMermaidLabel(\n normaliseLines(getNodeLabel(node)).join(\"<br/>\"),\n );\n\n if (isGroup(node)) {\n lines.push(`${indent}subgraph ${id}[\"${label}\"]`);\n childrenOf.get(node.id)?.forEach((child) => addNode(child, depth + 1));\n lines.push(`${indent}end`);\n return;\n }\n\n const [prefix, suffix] = getMermaidNodeShape(node.type || \"custom\");\n let nodeLine = `${indent}${id}${prefix}\"${label}\"${suffix}`;\n\n // Add class reference if styles are enabled\n if (includeStyles && node.type && usedTypes.has(node.type)) {\n nodeLine += `:::${sanitizeMermaidId(node.type)}`;\n }\n\n lines.push(nodeLine);\n };\n\n topLevelNodes.forEach((node) => addNode(node, 1));\n\n // Add edge definitions, leaving out edges to nodes that aren't shown\n lines.push(\"\");\n lines.push(\" %% Edges\");\n\n const crossDomainLinks: number[] = [];\n graphEdges\n .filter(\n (edge) =>\n !edge.hidden &&\n mermaidIds.has(edge.source) &&\n mermaidIds.has(edge.target),\n )\n .forEach((edge, index) => {\n const sourceId = mermaidIds.get(edge.source)!;\n const targetId = mermaidIds.get(edge.target)!;\n const label = normaliseLines(getEdgeLabel(edge) ?? \"\").join(\" \");\n\n lines.push(\n label\n ? ` ${sourceId} -->|\"${escapeMermaidLabel(label)}\"| ${targetId}`\n : ` ${sourceId} --> ${targetId}`,\n );\n if (isCrossDomain(edge)) crossDomainLinks.push(index);\n });\n\n // Boundaries, the resource viewed and edges between domains\n if (includeStyles) {\n const styleLines: string[] = [];\n groupsByClass.forEach((ids, groupClass) => {\n styleLines.push(` class ${ids.join(\",\")} ${groupClass}`);\n });\n if (focusedIds.length > 0) {\n styleLines.push(` class ${focusedIds.join(\",\")} focused`);\n }\n if (crossDomainLinks.length > 0) {\n styleLines.push(\n ` linkStyle ${crossDomainLinks.join(\",\")} ${CROSS_DOMAIN_LINK_STYLE}`,\n );\n }\n if (styleLines.length > 0) {\n lines.push(\"\");\n lines.push(\" %% Boundaries and highlights\");\n lines.push(...styleLines);\n }\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["import_react","message","isCrossDomain"]}
package/dist/layout.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import "./chunk-Q3OM5AL5.mjs";
1
+ import "./chunk-STJQWGKW.mjs";
2
2
  import {
3
+ convertToMermaid,
3
4
  getMessagesLabel,
4
5
  getNodeSize,
5
6
  hideMessageNodes,
@@ -7,8 +8,9 @@ import {
7
8
  isCarrier,
8
9
  isMessageNode,
9
10
  layoutWithElk
10
- } from "./chunk-Z3G354GF.mjs";
11
+ } from "./chunk-P2DH6CFV.mjs";
11
12
  export {
13
+ convertToMermaid,
12
14
  getMessagesLabel,
13
15
  getNodeSize,
14
16
  hideMessageNodes,
package/dist/model.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as Model, S as Scene } from './schema-DKA0EQ_H.mjs';
2
- export { A as ArchitecturePayload, C as CONTAINER_TYPES, l as CommentAuthor, m as CommentThread, e as ContainerType, E as EntityProperty, H as HOTSPOT_SEVERITIES, o as Hotspot, n as HotspotSeverity, i as LEVELS, k as Layout, L as Level, c as MESSAGE_TYPES, N as NOTE_COLOR_NAMES, j as Note, f as RELATION_KINDS, a as RESOURCE_TYPES, h as Relation, g as RelationKind, R as Resource, b as ResourceType, d as SCHEMA_FORMATS, T as Tour } from './schema-DKA0EQ_H.mjs';
1
+ import { M as Model, S as Scene } from './schema-CKdQ-Ak2.mjs';
2
+ export { A as ArchitecturePayload, C as CONTAINER_TYPES, l as CommentAuthor, m as CommentThread, e as ContainerType, E as EntityProperty, H as HOTSPOT_SEVERITIES, o as Hotspot, n as HotspotSeverity, i as LEVELS, k as Layout, L as Level, c as MESSAGE_TYPES, N as NOTE_COLOR_NAMES, j as Note, f as RELATION_KINDS, a as RESOURCE_TYPES, h as Relation, g as RelationKind, R as Resource, b as ResourceType, d as SCHEMA_FORMATS, T as Tour } from './schema-CKdQ-Ak2.mjs';
3
3
  import 'zod';
4
4
 
5
5
  /**
@@ -18,12 +18,12 @@ declare const ResourceType: z.ZodEnum<{
18
18
  channel: "channel";
19
19
  view: "view";
20
20
  team: "team";
21
- entity: "entity";
22
- container: "container";
23
21
  "external-system": "external-system";
22
+ container: "container";
23
+ entity: "entity";
24
24
  }>;
25
25
  type ResourceType = z.infer<typeof ResourceType>;
26
- declare const MESSAGE_TYPES: Set<"system" | "actor" | "domain" | "service" | "agent" | "event" | "query" | "command" | "channel" | "view" | "team" | "entity" | "container" | "external-system">;
26
+ declare const MESSAGE_TYPES: Set<"system" | "actor" | "domain" | "service" | "agent" | "event" | "query" | "command" | "channel" | "view" | "team" | "external-system" | "container" | "entity">;
27
27
  declare const SCHEMA_FORMATS: readonly ["json-schema", "avro", "protobuf", "json", "yaml", "xml", "graphql", "other"];
28
28
  /** A field of an entity (as EventCatalog's entities have them), e.g. { name: "orderId", type: "string" } */
29
29
  declare const EntityProperty: z.ZodObject<{
@@ -50,9 +50,9 @@ declare const Resource: z.ZodObject<{
50
50
  channel: "channel";
51
51
  view: "view";
52
52
  team: "team";
53
- entity: "entity";
54
- container: "container";
55
53
  "external-system": "external-system";
54
+ container: "container";
55
+ entity: "entity";
56
56
  }>;
57
57
  name: z.ZodString;
58
58
  summary: z.ZodOptional<z.ZodString>;
@@ -264,9 +264,9 @@ declare const Model: z.ZodObject<{
264
264
  channel: "channel";
265
265
  view: "view";
266
266
  team: "team";
267
- entity: "entity";
268
- container: "container";
269
267
  "external-system": "external-system";
268
+ container: "container";
269
+ entity: "entity";
270
270
  }>;
271
271
  name: z.ZodString;
272
272
  summary: z.ZodOptional<z.ZodString>;
package/dist/studio.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as Y from 'yjs';
2
2
  import { XYPosition, Node, Edge, Connection, EdgeMarker, NodeTypes, FitViewOptions, NodeProps } from '@xyflow/react';
3
- import { E as EdgeRoute, L as LayoutGraph, a as LayoutSize } from './elk-layout-BVUJjv6i.mjs';
3
+ import { E as EdgeRoute, C as CopiedEdgeRoute, L as LayoutGraph, a as LayoutSize } from './elk-layout-CpbZ6hB9.mjs';
4
4
  import * as react from 'react';
5
5
  import { RefObject, ReactNode, DragEvent, SVGProps } from 'react';
6
- import { R as Resource, M as Model, L as Level$1, A as ArchitecturePayload } from './schema-DKA0EQ_H.mjs';
6
+ import { R as Resource, M as Model, L as Level$1, A as ArchitecturePayload } from './schema-CKdQ-Ak2.mjs';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
  import { LucideIcon } from 'lucide-react';
9
9
  import 'zod';
@@ -218,7 +218,7 @@ type CanvasContent = {
218
218
  from: string;
219
219
  to: string;
220
220
  label?: string;
221
- route?: Pick<EdgeRoute, "points" | "label">;
221
+ route?: CopiedEdgeRoute;
222
222
  }[];
223
223
  levels?: Pick<DiagramLevels, 1 | 2 | "unavailable">;
224
224
  };
@@ -759,7 +759,7 @@ declare function CanvasPortal({ children }: {
759
759
  * notes are sticky note and text nodes, on their level; where things are goes in the model's layout.
760
760
  */
761
761
 
762
- declare const resourceTypeOf: (nodeType?: string) => "system" | "actor" | "domain" | "service" | "agent" | "event" | "query" | "command" | "channel" | "view" | "team" | "entity" | "container" | "external-system";
762
+ declare const resourceTypeOf: (nodeType?: string) => "system" | "actor" | "domain" | "service" | "agent" | "event" | "query" | "command" | "channel" | "view" | "team" | "external-system" | "container" | "entity";
763
763
  type ModelCanvasContent = {
764
764
  nodes: Node[];
765
765
  edges: Edge[];
@@ -1298,6 +1298,16 @@ declare function Field({ label, children, className, }: {
1298
1298
  className?: string;
1299
1299
  }): react_jsx_runtime.JSX.Element;
1300
1300
 
1301
+ /**
1302
+ * Measures a node's handles again once it has finished animating in or resizing. React Flow measures them when the
1303
+ * node appears, part way through its pop in (`studio-node-in`, which scales it), and doesn't again while its size
1304
+ * stays the same: edges would join a node short of its sides (far short of a container's). Same for a container
1305
+ * growing to fit what's in it (its size eases in, `is-container-resize`). One listener for the whole canvas, and one
1306
+ * update a frame for everything that finished in it (a container's width and height transitions end separately).
1307
+ * Render it inside `<ReactFlow>`.
1308
+ */
1309
+ declare const RemeasureHandles: react.NamedExoticComponent<object>;
1310
+
1301
1311
  /**
1302
1312
  * Someone's picture from their sign-in provider (SSO), loaded from there. By default it covers their avatar's
1303
1313
  * initials, which show again if it doesn't load (blocked, expired, or no longer there).
@@ -1309,4 +1319,4 @@ declare function Picture({ src, className, }: {
1309
1319
 
1310
1320
  declare const TeamNode: react.NamedExoticComponent<NodeProps>;
1311
1321
 
1312
- export { AgentMark, type Author, CANVAS_STATUSES, CANVAS_STATUS_LOOK, CATALOG_COLLECTIONS, CATALOG_GROUPS, CLIPBOARD_TYPE, COMPONENT_DRAG_TYPE, COMPONENT_TYPES, COUNTER_SCALE, type CanvasContent, _default$1 as CanvasControls, type CanvasMeta, CanvasPortal, type CanvasStatus, CardPreview, type CatalogCollection, ClaudeCodeLogo, ClaudeLogo, type ClipboardContent, type CommentAnchor, CommentLayer, CommentList, type ContainerChoice, ContainerChooser, ContextMenu, type ContextMenuState, CursorLogo, DEFAULT_NODE_SIZE, DIAGRAM_FILE_EXTENSION, DROP_GAP, type DiagramLevel, type DiagramLevels, type DragPreview, DropTargetContext, EDGE_COLOR, EDGE_MARKER, ENTITY_SOURCE_HANDLE, EdgeLabelPortal, EdgeRoute, _default as EditButton, EditStatus, EditableText, type Editing, EditingContext, EmptyCanvas, FIT_VIEW_OPTIONS, Field, GROUP_PADDING, GROUP_TYPES, type Hotspot, type HotspotDraft, type HotspotFields, HotspotLayer, HotspotList, type HotspotPlace, type HotspotSeverity, type HotspotSpot, KEEP_IN_CONTAINER_MARGIN, LEVELS, LOCAL_EDGE_KEYS, LOCAL_NODE_KEYS, type LayoutResult, type Level, type LevelPlace, type Link, type LiveMoves, type LivePeer, LockedContext, MESSAGE_COLLECTIONS, type MenuQuestion, type Message, type ModelCanvasContent, type Moves, type Moving, NODE_CATEGORIES, NODE_ICONS, NodeCard, type NodeCategory, type NodeDefinition, type NodeIcon, type NodePreview, OpenAILogo, Palette, PaletteItem, PaletteSection, type PaletteTab, Picture, type PresenceSource, type RenderedSizes, SEVERITIES, SHORTCUTS, STATUS, type SavedFileHandle, ShortcutsList, type Smoother, type StartDrag, StartWritingContext, type StatusChange, TEXT_FONT_SIZE, TeamNode, type Thread, type UpdateNodeData, UpdateNodeDataContext, ViewControls, addCanvasContent, addHotspots, addNodes, addThreads, agentLogoFor, anchorAt, arrangeLevel, buildNode, bySeverity, canGoInContainer, canHold, canPickWhereToSave, canvasImage, canvasToModel, changesNodesOrEdges, cleanFileName, componentKey, connectNodes, containerFor, copyNodes, createEdge, createHotspot, createSmoother, createThread, deleteEdges, deleteHotspot, deleteNodes, deleteThread, deleteThreads, downloadFile, fileNameFor, findDropTarget, findDropTargets, findGroupAtPoint, fitContainersAround, fitGroup, fitGroupToChildren, getAbsolutePosition, getAbsoluteRect, getAnchorPosition, getCanvasMaps, getCanvasStatus, getDiagramLevel, getDiagramLevelUnavailable, getEdgeLabel, getEdgeRoute, getLayoutPositions, getLevelGraph, getLevelNodeSize, getLevelUnavailableReason, getLock, getNodeDefinition, getNodeLabel, getNodeName, getNodeSize, getNoteLevel, getResource, getStructureKey, isCatalogCollection, isDecided, isGroupType, isOnLevel, isShownOnLevel, isVersioned, isWrittenOnCanvas, keepDragged, keepUnchanged, layoutGraph, levelPlaces, modelToCanvas, moveNode, moveThread, moveThreads, nodeDefinitions, nodeTypes, pasteNodes, patchShared, pickDiagramFile, placeDropped, placeNode, placeNodes, previewContainerGrowth, readCanvas, readClipboard, readDiagramFile, readHotspots, readLevelLayout, readMeta, readThreads, reconnectEdge, reopenIfDecided, reopeningIfEdited, replyToThread, resizeNode, resourceNodeData, resourceTypeOf, saveToPickedFile, sendMove, setCanvasStatus, setEdgeLabel, setEdgeRoutes, setLevelPlaces, setLock, setMeta, setNodeParent, setNodeParents, setThreadResolved, shortId, sizeOf, sortByHierarchy, timeAgo, toDiagramFile, toRelativePosition, toSharedNode, topSeverity, updateHotspot, updateNodeData, updateResource, useCounterScale, useDragGhost, useNodeAnchor, useRemoteMoves, useSavedFlash, withDescendants, withDiagramRoutes, withGroupSizes, withLocalState, withParent, withPositions };
1322
+ export { AgentMark, type Author, CANVAS_STATUSES, CANVAS_STATUS_LOOK, CATALOG_COLLECTIONS, CATALOG_GROUPS, CLIPBOARD_TYPE, COMPONENT_DRAG_TYPE, COMPONENT_TYPES, COUNTER_SCALE, type CanvasContent, _default$1 as CanvasControls, type CanvasMeta, CanvasPortal, type CanvasStatus, CardPreview, type CatalogCollection, ClaudeCodeLogo, ClaudeLogo, type ClipboardContent, type CommentAnchor, CommentLayer, CommentList, type ContainerChoice, ContainerChooser, ContextMenu, type ContextMenuState, CursorLogo, DEFAULT_NODE_SIZE, DIAGRAM_FILE_EXTENSION, DROP_GAP, type DiagramLevel, type DiagramLevels, type DragPreview, DropTargetContext, EDGE_COLOR, EDGE_MARKER, ENTITY_SOURCE_HANDLE, EdgeLabelPortal, EdgeRoute, _default as EditButton, EditStatus, EditableText, type Editing, EditingContext, EmptyCanvas, FIT_VIEW_OPTIONS, Field, GROUP_PADDING, GROUP_TYPES, type Hotspot, type HotspotDraft, type HotspotFields, HotspotLayer, HotspotList, type HotspotPlace, type HotspotSeverity, type HotspotSpot, KEEP_IN_CONTAINER_MARGIN, LEVELS, LOCAL_EDGE_KEYS, LOCAL_NODE_KEYS, type LayoutResult, type Level, type LevelPlace, type Link, type LiveMoves, type LivePeer, LockedContext, MESSAGE_COLLECTIONS, type MenuQuestion, type Message, type ModelCanvasContent, type Moves, type Moving, NODE_CATEGORIES, NODE_ICONS, NodeCard, type NodeCategory, type NodeDefinition, type NodeIcon, type NodePreview, OpenAILogo, Palette, PaletteItem, PaletteSection, type PaletteTab, Picture, type PresenceSource, RemeasureHandles, type RenderedSizes, SEVERITIES, SHORTCUTS, STATUS, type SavedFileHandle, ShortcutsList, type Smoother, type StartDrag, StartWritingContext, type StatusChange, TEXT_FONT_SIZE, TeamNode, type Thread, type UpdateNodeData, UpdateNodeDataContext, ViewControls, addCanvasContent, addHotspots, addNodes, addThreads, agentLogoFor, anchorAt, arrangeLevel, buildNode, bySeverity, canGoInContainer, canHold, canPickWhereToSave, canvasImage, canvasToModel, changesNodesOrEdges, cleanFileName, componentKey, connectNodes, containerFor, copyNodes, createEdge, createHotspot, createSmoother, createThread, deleteEdges, deleteHotspot, deleteNodes, deleteThread, deleteThreads, downloadFile, fileNameFor, findDropTarget, findDropTargets, findGroupAtPoint, fitContainersAround, fitGroup, fitGroupToChildren, getAbsolutePosition, getAbsoluteRect, getAnchorPosition, getCanvasMaps, getCanvasStatus, getDiagramLevel, getDiagramLevelUnavailable, getEdgeLabel, getEdgeRoute, getLayoutPositions, getLevelGraph, getLevelNodeSize, getLevelUnavailableReason, getLock, getNodeDefinition, getNodeLabel, getNodeName, getNodeSize, getNoteLevel, getResource, getStructureKey, isCatalogCollection, isDecided, isGroupType, isOnLevel, isShownOnLevel, isVersioned, isWrittenOnCanvas, keepDragged, keepUnchanged, layoutGraph, levelPlaces, modelToCanvas, moveNode, moveThread, moveThreads, nodeDefinitions, nodeTypes, pasteNodes, patchShared, pickDiagramFile, placeDropped, placeNode, placeNodes, previewContainerGrowth, readCanvas, readClipboard, readDiagramFile, readHotspots, readLevelLayout, readMeta, readThreads, reconnectEdge, reopenIfDecided, reopeningIfEdited, replyToThread, resizeNode, resourceNodeData, resourceTypeOf, saveToPickedFile, sendMove, setCanvasStatus, setEdgeLabel, setEdgeRoutes, setLevelPlaces, setLock, setMeta, setNodeParent, setNodeParents, setThreadResolved, shortId, sizeOf, sortByHierarchy, timeAgo, toDiagramFile, toRelativePosition, toSharedNode, topSeverity, updateHotspot, updateNodeData, updateResource, useCounterScale, useDragGhost, useNodeAnchor, useRemoteMoves, useSavedFlash, withDescendants, withDiagramRoutes, withGroupSizes, withLocalState, withParent, withPositions };