{"version":3,"file":"index.cjs","names":["AvoidLib","AvoidLib"],"sources":["../src/libavoid-session.ts","../src/parser.ts","../src/route-result.ts","../src/write-back.ts","../src/route-edges.ts","../src/session.ts"],"sourcesContent":["import type { Avoid } from \"libavoid-js\";\nimport type { ParsedGraph, ResolvedNode, ResolvedPort } from \"./parser\";\nimport type { ElkPoint, LibavoidRouterOptions } from \"./types\";\n\n/** Extract WASM types from the Avoid interface (not directly exported by libavoid-js) */\ntype WasmRouter = Avoid[\"Router\"];\ntype WasmShapeRef = Avoid[\"ShapeRef\"];\ntype WasmConnRef = Avoid[\"ConnRef\"];\ntype WasmRoutingParam =\n\tAvoid[\"RoutingParameter\"][keyof Avoid[\"RoutingParameter\"]];\ntype WasmRoutingOpt = Avoid[\"RoutingOption\"][keyof Avoid[\"RoutingOption\"]];\n\n/**\n * Free an Emscripten-wrapped C++ object.\n * The `.delete()` method exists at runtime but is not in the libavoid-js type definitions.\n */\n// biome-ignore lint/suspicious/noExplicitAny: Emscripten .delete() is untyped\nexport function freeWasm(obj: any): void {\n\tobj.delete();\n}\n\n/** ConnDir flag bitmasks (libavoid C++ enum values) */\nconst ConnDirUp = 1;\nconst ConnDirDown = 2;\nconst ConnDirLeft = 4;\nconst ConnDirRight = 8;\nconst ConnDirAll = 15;\n\n/** Pin class ID reserved for auto-generated pins (must be > 0) */\nexport const AUTO_PIN_CLASS_ID = 1;\n\ninterface ShapeEntry {\n\tnode: ResolvedNode;\n\tshapeRef: WasmShapeRef;\n}\n\ninterface ConnectorEntry {\n\tconnRef: WasmConnRef;\n\tedge: { id: string };\n}\n\nexport interface LibavoidSession {\n\tavoid: Avoid;\n\tconnectors: Map<string, ConnectorEntry>;\n\tedgeEndpointNodes: Set<string>;\n\tnodes: Map<string, ResolvedNode>;\n\tportPinClassIds: Map<string, number>;\n\trouter: WasmRouter;\n\tshapes: Map<string, ShapeEntry>;\n}\n\nfunction portSideToConnDir(side: string): number {\n\tswitch (side) {\n\t\tcase \"NORTH\":\n\t\t\treturn ConnDirUp;\n\t\tcase \"SOUTH\":\n\t\t\treturn ConnDirDown;\n\t\tcase \"EAST\":\n\t\t\treturn ConnDirRight;\n\t\tcase \"WEST\":\n\t\t\treturn ConnDirLeft;\n\t\tdefault:\n\t\t\treturn ConnDirAll;\n\t}\n}\n\nfunction applyRoutingParameters(\n\trouter: WasmRouter,\n\tAvoid: Avoid,\n\toptions: LibavoidRouterOptions,\n): void {\n\tconst RP = Avoid.RoutingParameter;\n\tconst paramMap: [keyof LibavoidRouterOptions, WasmRoutingParam][] = [\n\t\t[\"segmentPenalty\", RP.segmentPenalty],\n\t\t[\"anglePenalty\", RP.anglePenalty],\n\t\t[\"crossingPenalty\", RP.crossingPenalty],\n\t\t[\"clusterCrossingPenalty\", RP.clusterCrossingPenalty],\n\t\t[\"fixedSharedPathPenalty\", RP.fixedSharedPathPenalty],\n\t\t[\"portDirectionPenalty\", RP.portDirectionPenalty],\n\t\t[\"shapeBufferDistance\", RP.shapeBufferDistance],\n\t\t[\"idealNudgingDistance\", RP.idealNudgingDistance],\n\t\t[\"reverseDirectionPenalty\", RP.reverseDirectionPenalty],\n\t];\n\n\tfor (const [key, param] of paramMap) {\n\t\tconst val = options[key];\n\t\tif (typeof val === \"number\") {\n\t\t\trouter.setRoutingParameter(param, val);\n\t\t}\n\t}\n\n\tconst RO = Avoid.RoutingOption;\n\tconst optionMap: [keyof LibavoidRouterOptions, WasmRoutingOpt][] = [\n\t\t[\n\t\t\t\"nudgeOrthogonalSegmentsConnectedToShapes\",\n\t\t\tRO.nudgeOrthogonalSegmentsConnectedToShapes,\n\t\t],\n\t\t[\n\t\t\t\"nudgeOrthogonalTouchingColinearSegments\",\n\t\t\tRO.nudgeOrthogonalTouchingColinearSegments,\n\t\t],\n\t\t[\n\t\t\t\"performUnifyingNudgingPreprocessingStep\",\n\t\t\tRO.performUnifyingNudgingPreprocessingStep,\n\t\t],\n\t\t[\n\t\t\t\"nudgeSharedPathsWithCommonEndPoint\",\n\t\t\tRO.nudgeSharedPathsWithCommonEndPoint,\n\t\t],\n\t];\n\n\tfor (const [key, opt] of optionMap) {\n\t\tconst val = options[key];\n\t\tif (typeof val === \"boolean\") {\n\t\t\trouter.setRoutingOption(opt, val);\n\t\t}\n\t}\n}\n\nfunction registerNodeShapes(\n\tparsed: ParsedGraph,\n\tAvoid: Avoid,\n\trouter: WasmRouter,\n\tnodesNeedingAutoPin: Set<string>,\n\tedgeEndpointNodes: Set<string>,\n): {\n\tportPinClassIds: Map<string, number>;\n\tshapes: Map<string, ShapeEntry>;\n} {\n\tconst shapes = new Map<string, ShapeEntry>();\n\tconst pinClassCounters = new Map<string, number>();\n\tconst portPinClassIds = new Map<string, number>();\n\n\tfor (const [nodeId, node] of parsed.nodes) {\n\t\tif (node.parentId === null) continue;\n\t\t// Container nodes (with children) that are not edge endpoints are skipped —\n\t\t// edges can route through them freely. Ideally these would be ClusterRef\n\t\t// (soft boundaries with clusterCrossingPenalty), but ClusterRef is not\n\t\t// exposed in the libavoid-js WASM bindings. Container nodes that ARE\n\t\t// edge endpoints still need a ShapeRef for connector attachment.\n\t\tif (node.hasChildren && !edgeEndpointNodes.has(nodeId)) continue;\n\n\t\tconst topLeft = new Avoid.Point(node.x, node.y);\n\t\tconst bottomRight = new Avoid.Point(\n\t\t\tnode.x + node.width,\n\t\t\tnode.y + node.height,\n\t\t);\n\t\tconst rect = new Avoid.Rectangle(topLeft, bottomRight);\n\t\tconst shapeRef = new Avoid.ShapeRef(router, rect);\n\t\tfreeWasm(topLeft);\n\t\tfreeWasm(bottomRight);\n\t\tfreeWasm(rect);\n\n\t\tshapes.set(nodeId, { node, shapeRef });\n\n\t\tif (nodesNeedingAutoPin.has(nodeId)) {\n\t\t\tregisterAutoPins(shapeRef, Avoid);\n\t\t}\n\n\t\tregisterPortPins(\n\t\t\tnode.ports,\n\t\t\tnode,\n\t\t\tshapeRef,\n\t\t\tAvoid,\n\t\t\tpinClassCounters,\n\t\t\tportPinClassIds,\n\t\t);\n\t}\n\n\treturn { portPinClassIds, shapes };\n}\n\n/** Proportional offsets for boundary pins along each side (4 per side). */\nconst BOUNDARY_OFFSETS = [0.2, 0.4, 0.6, 0.8];\n\n/**\n * Register auto-generated boundary pins on a shape (4 per side, 16 total).\n * Used for nodes without explicit port pins, so edges connect at the boundary\n * and can spread out across each side.\n */\nexport function registerAutoPins(shapeRef: WasmShapeRef, Avoid: Avoid): void {\n\tfor (const t of BOUNDARY_OFFSETS) {\n\t\tfor (const [px, py, dir] of [\n\t\t\t[0, t, ConnDirLeft], // west\n\t\t\t[1, t, ConnDirRight], // east\n\t\t\t[t, 0, ConnDirUp], // north\n\t\t\t[t, 1, ConnDirDown], // south\n\t\t] as const) {\n\t\t\tconst pin = new Avoid.ShapeConnectionPin(\n\t\t\t\tshapeRef,\n\t\t\t\tAUTO_PIN_CLASS_ID,\n\t\t\t\tpx,\n\t\t\t\tpy,\n\t\t\t\ttrue,\n\t\t\t\t0,\n\t\t\t\tdir,\n\t\t\t);\n\t\t\tpin.setExclusive(false);\n\t\t}\n\t}\n}\n\nfunction registerPortPins(\n\tports: ResolvedPort[],\n\tnode: ResolvedNode,\n\tshapeRef: WasmShapeRef,\n\tAvoid: Avoid,\n\tpinClassCounters: Map<string, number>,\n\tportPinClassIds: Map<string, number>,\n): void {\n\tfor (const port of ports) {\n\t\t// Start at 2 because class ID 1 is reserved for AUTO_PIN_CLASS_ID\n\t\tconst current = pinClassCounters.get(node.id) ?? 2;\n\t\tpinClassCounters.set(node.id, current + 1);\n\t\tportPinClassIds.set(port.id, current);\n\n\t\tconst proportionX = Math.max(\n\t\t\t0,\n\t\t\tMath.min(1, (port.x + port.width / 2 - node.x) / node.width),\n\t\t);\n\t\tconst proportionY = Math.max(\n\t\t\t0,\n\t\t\tMath.min(1, (port.y + port.height / 2 - node.y) / node.height),\n\t\t);\n\t\tconst visDirs = portSideToConnDir(port.side);\n\n\t\tconst pin = new Avoid.ShapeConnectionPin(\n\t\t\tshapeRef,\n\t\t\tcurrent,\n\t\t\tproportionX,\n\t\t\tproportionY,\n\t\t\ttrue,\n\t\t\t0,\n\t\t\tvisDirs,\n\t\t);\n\t\tpin.setExclusive(false);\n\t}\n}\n\nfunction registerEdgeConnectors(\n\tparsed: ParsedGraph,\n\tAvoid: Avoid,\n\trouter: WasmRouter,\n\tshapes: Map<string, ShapeEntry>,\n\tportPinClassIds: Map<string, number>,\n): Map<string, ConnectorEntry> {\n\tconst connectors = new Map<string, ConnectorEntry>();\n\n\tfor (const edge of parsed.edges) {\n\t\tconst srcShape = shapes.get(edge.sourceNodeId);\n\t\tconst tgtShape = shapes.get(edge.targetNodeId);\n\n\t\tif (!srcShape) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": source node \"${edge.sourceNodeId}\" has no corresponding shape`,\n\t\t\t);\n\t\t}\n\t\tif (!tgtShape) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": target node \"${edge.targetNodeId}\" has no corresponding shape`,\n\t\t\t);\n\t\t}\n\n\t\tconst srcPinClass = edge.sourcePortId\n\t\t\t? portPinClassIds.get(edge.sourcePortId)\n\t\t\t: AUTO_PIN_CLASS_ID;\n\t\tconst tgtPinClass = edge.targetPortId\n\t\t\t? portPinClassIds.get(edge.targetPortId)\n\t\t\t: AUTO_PIN_CLASS_ID;\n\n\t\tif (srcPinClass === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": source port \"${edge.sourcePortId}\" has no pin class`,\n\t\t\t);\n\t\t}\n\t\tif (tgtPinClass === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": target port \"${edge.targetPortId}\" has no pin class`,\n\t\t\t);\n\t\t}\n\n\t\tconst srcEnd = new Avoid.ConnEnd(srcShape.shapeRef, srcPinClass);\n\t\tconst tgtEnd = new Avoid.ConnEnd(tgtShape.shapeRef, tgtPinClass);\n\t\tconst connRef = new Avoid.ConnRef(router, srcEnd, tgtEnd);\n\t\tfreeWasm(srcEnd);\n\t\tfreeWasm(tgtEnd);\n\n\t\tconnectors.set(edge.id, { connRef, edge });\n\t}\n\n\treturn connectors;\n}\n\n/**\n * Create a libavoid routing session from a parsed graph.\n *\n * Registers all nodes as obstacles (ShapeRef), ports as connection pins\n * (ShapeConnectionPin), and edges as connectors (ConnRef).\n */\nexport function createLibavoidSession(\n\tparsed: ParsedGraph,\n\tAvoid: Avoid,\n\toptions: LibavoidRouterOptions = {},\n): LibavoidSession {\n\tconst routingType = options.routingType ?? \"orthogonal\";\n\tconst routerFlag =\n\t\troutingType === \"polyline\"\n\t\t\t? Avoid.RouterFlag.PolyLineRouting.value\n\t\t\t: Avoid.RouterFlag.OrthogonalRouting.value;\n\n\tconst router = new Avoid.Router(routerFlag);\n\n\ttry {\n\t\tapplyRoutingParameters(router, Avoid, options);\n\n\t\tconst nodesNeedingAutoPin = new Set<string>();\n\t\tconst edgeEndpointNodes = new Set<string>();\n\t\tfor (const edge of parsed.edges) {\n\t\t\tedgeEndpointNodes.add(edge.sourceNodeId);\n\t\t\tedgeEndpointNodes.add(edge.targetNodeId);\n\t\t\tif (!edge.sourcePortId) nodesNeedingAutoPin.add(edge.sourceNodeId);\n\t\t\tif (!edge.targetPortId) nodesNeedingAutoPin.add(edge.targetNodeId);\n\t\t}\n\n\t\tconst { shapes, portPinClassIds } = registerNodeShapes(\n\t\t\tparsed,\n\t\t\tAvoid,\n\t\t\trouter,\n\t\t\tnodesNeedingAutoPin,\n\t\t\tedgeEndpointNodes,\n\t\t);\n\n\t\tconst connectors = registerEdgeConnectors(\n\t\t\tparsed,\n\t\t\tAvoid,\n\t\t\trouter,\n\t\t\tshapes,\n\t\t\tportPinClassIds,\n\t\t);\n\n\t\treturn {\n\t\t\tavoid: Avoid,\n\t\t\tconnectors,\n\t\t\tedgeEndpointNodes,\n\t\t\tnodes: parsed.nodes,\n\t\t\tportPinClassIds,\n\t\t\trouter,\n\t\t\tshapes,\n\t\t};\n\t} catch (err) {\n\t\trouter.delete();\n\t\tthrow err;\n\t}\n}\n\n/**\n * Extract computed routes from a processed libavoid session.\n * Returns absolute-coordinate point arrays keyed by edge ID.\n */\nexport function extractRoutes(\n\tsession: LibavoidSession,\n): Map<string, ElkPoint[]> {\n\tconst routes = new Map<string, ElkPoint[]>();\n\n\tfor (const [edgeId, { connRef }] of session.connectors) {\n\t\tconst displayRoute = connRef.displayRoute();\n\t\ttry {\n\t\t\tconst points: ElkPoint[] = [];\n\n\t\t\tfor (let i = 0; i < displayRoute.size(); i++) {\n\t\t\t\tconst point = displayRoute.at(i);\n\t\t\t\ttry {\n\t\t\t\t\tpoints.push({ x: point.x, y: point.y });\n\t\t\t\t} finally {\n\t\t\t\t\tfreeWasm(point);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\troutes.set(edgeId, points);\n\t\t} finally {\n\t\t\tfreeWasm(displayRoute);\n\t\t}\n\t}\n\n\treturn routes;\n}\n\n/**\n * Destroy a libavoid session, cleaning up WASM memory.\n * The router destructor handles cleanup of all associated shapes/connectors.\n */\nexport function destroySession(session: LibavoidSession): void {\n\tsession.router.delete();\n\tsession.shapes.clear();\n\tsession.connectors.clear();\n}\n","import type { ElkEdge, ElkGraph, ElkNode, ElkPort } from \"./types\";\n\n/** Internal representation after parsing */\nexport interface ParsedGraph {\n\tnodes: Map<string, ResolvedNode>;\n\tports: Map<string, ResolvedPort>;\n\tedges: ResolvedEdge[];\n}\n\nexport interface ResolvedNode {\n\tid: string;\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tports: ResolvedPort[];\n\tparentId: string | null;\n\t/** Whether this node has children (is a container/group node) */\n\thasChildren: boolean;\n\tpadding: { top: number; right: number; bottom: number; left: number };\n}\n\nexport interface ResolvedPort {\n\tid: string;\n\tnodeId: string;\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tside: \"NORTH\" | \"SOUTH\" | \"EAST\" | \"WEST\";\n}\n\nexport interface ResolvedEdge {\n\tid: string;\n\tsourceNodeId: string;\n\tsourcePortId: string | undefined;\n\ttargetNodeId: string;\n\ttargetPortId: string | undefined;\n\t/** Reference to the owning parent node ID (whose edges array contains this edge) */\n\townerNodeId: string;\n\t/** Reference back to the original ELK edge object for write-back */\n\telkEdgeRef: ElkEdge;\n}\n\nexport type PortSide = \"NORTH\" | \"SOUTH\" | \"EAST\" | \"WEST\";\n\n/** Mapping from ELK port.side property values to internal PortSide */\nconst ELK_PORT_SIDE_MAP: Record<string, PortSide> = {\n\tEAST: \"EAST\",\n\tNORTH: \"NORTH\",\n\tSOUTH: \"SOUTH\",\n\tWEST: \"WEST\",\n};\n\n/**\n * Determine which side of a node a port is on.\n *\n * If the port has an explicit `port.side` property (via `properties[\"port.side\"]`\n * or `properties[\"elk.port.side\"]`), that value is used. Otherwise, the side is\n * inferred from the port's position relative to the node bounds.\n *\n * Tie-breaking priority when inferring: EAST > WEST > NORTH > SOUTH.\n */\nfunction inferPortSide(\n\tport: ElkPort,\n\tnodeWidth: number,\n\tnodeHeight: number,\n): PortSide {\n\t// Check for explicit ELK port.side property\n\tconst explicitSide =\n\t\tport.properties?.[\"port.side\"] ?? port.properties?.[\"elk.port.side\"];\n\tif (typeof explicitSide === \"string\") {\n\t\tconst mapped = ELK_PORT_SIDE_MAP[explicitSide.toUpperCase()];\n\t\tif (mapped) return mapped;\n\t}\n\n\tconst px = port.x ?? 0;\n\tconst py = port.y ?? 0;\n\tconst pw = port.width ?? 0;\n\tconst ph = port.height ?? 0;\n\tconst centerX = px + pw / 2;\n\tconst centerY = py + ph / 2;\n\n\tconst distLeft = centerX;\n\tconst distRight = nodeWidth - centerX;\n\tconst distTop = centerY;\n\tconst distBottom = nodeHeight - centerY;\n\n\tconst minDist = Math.min(distLeft, distRight, distTop, distBottom);\n\n\tif (minDist === distRight) return \"EAST\";\n\tif (minDist === distLeft) return \"WEST\";\n\tif (minDist === distTop) return \"NORTH\";\n\treturn \"SOUTH\";\n}\n\n/**\n * Resolve an edge's source/target references to node and port IDs.\n *\n * Supports both:\n * - Simple format: edge.source / edge.target (node IDs), edge.sourcePort / edge.targetPort\n * - Extended format: edge.sources / edge.targets (can be node or port IDs)\n */\nfunction resolveEdgeEndpoint(\n\trefs: string[] | undefined,\n\tnodeId: string | undefined,\n\tportId: string | undefined,\n\tnodes: Map<string, ResolvedNode>,\n\tports: Map<string, ResolvedPort>,\n\tedgeId: string,\n\tendpoint: \"source\" | \"target\",\n): { nodeId: string; portId: string | undefined } {\n\tif (refs && refs.length > 0) {\n\t\tif (refs.length > 1) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edgeId}\": multiple ${endpoint}s are not supported (got ${refs.length})`,\n\t\t\t);\n\t\t}\n\t\tconst ref = refs[0];\n\t\tconst port = ports.get(ref);\n\t\tif (port) {\n\t\t\treturn { nodeId: port.nodeId, portId: ref };\n\t\t}\n\t\tif (nodes.has(ref)) {\n\t\t\treturn { nodeId: ref, portId: undefined };\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Edge \"${edgeId}\": ${endpoint} reference \"${ref}\" not found as node or port`,\n\t\t);\n\t}\n\n\tif (nodeId) {\n\t\tif (!nodes.has(nodeId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edgeId}\": ${endpoint} node \"${nodeId}\" not found`,\n\t\t\t);\n\t\t}\n\t\tif (portId && !ports.has(portId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edgeId}\": ${endpoint} port \"${portId}\" not found`,\n\t\t\t);\n\t\t}\n\t\treturn { nodeId, portId };\n\t}\n\n\tthrow new Error(\n\t\t`Edge \"${edgeId}\": no ${endpoint} specified (need source/target or sources/targets)`,\n\t);\n}\n\nfunction validateNode(\n\tnode: ElkNode,\n\tparentId: string | null,\n): { width: number; height: number } {\n\tconst { width, height } = node;\n\tif (parentId === null) {\n\t\t// Root node — dimensions not required (not used as obstacle)\n\t\treturn { height: height ?? 0, width: width ?? 0 };\n\t}\n\tif (width === undefined || height === undefined) {\n\t\tthrow new Error(\n\t\t\t`Node \"${node.id}\" is missing width or height. All nodes must have dimensions.`,\n\t\t);\n\t}\n\tif (width <= 0 || height <= 0) {\n\t\tthrow new Error(\n\t\t\t`Node \"${node.id}\" has invalid dimensions (${width}x${height}). Width and height must be positive.`,\n\t\t);\n\t}\n\tif (node.x === undefined || node.y === undefined) {\n\t\tthrow new Error(\n\t\t\t`Node \"${node.id}\" is missing x or y position. All non-root nodes must be positioned.`,\n\t\t);\n\t}\n\treturn { height, width };\n}\n\nfunction resolvePorts(\n\tnode: ElkNode,\n\tabsX: number,\n\tabsY: number,\n\twidth: number,\n\theight: number,\n\tports: Map<string, ResolvedPort>,\n): ResolvedPort[] {\n\tconst resolvedPorts: ResolvedPort[] = [];\n\tif (!node.ports) return resolvedPorts;\n\n\tfor (const port of node.ports) {\n\t\tconst portX = port.x ?? 0;\n\t\tconst portY = port.y ?? 0;\n\t\tconst portW = port.width ?? 0;\n\t\tconst portH = port.height ?? 0;\n\t\tconst side = inferPortSide(port, width, height);\n\n\t\tconst resolved: ResolvedPort = {\n\t\t\theight: portH,\n\t\t\tid: port.id,\n\t\t\tnodeId: node.id,\n\t\t\tside,\n\t\t\twidth: portW,\n\t\t\tx: absX + portX,\n\t\t\ty: absY + portY,\n\t\t};\n\n\t\tresolvedPorts.push(resolved);\n\t\tif (ports.has(port.id)) {\n\t\t\tthrow new Error(`Duplicate port ID \"${port.id}\" found in graph`);\n\t\t}\n\t\tports.set(port.id, resolved);\n\t}\n\treturn resolvedPorts;\n}\n\nfunction resolvePadding(node: ElkNode): {\n\ttop: number;\n\tright: number;\n\tbottom: number;\n\tleft: number;\n} {\n\treturn {\n\t\tbottom: node.padding?.bottom ?? 0,\n\t\tleft: node.padding?.left ?? 0,\n\t\tright: node.padding?.right ?? 0,\n\t\ttop: node.padding?.top ?? 0,\n\t};\n}\n\nfunction resolveEdges(\n\tnode: ElkNode,\n\tnodes: Map<string, ResolvedNode>,\n\tports: Map<string, ResolvedPort>,\n\tedges: ResolvedEdge[],\n\tedgeIds: Set<string>,\n): void {\n\tif (!node.edges) return;\n\n\tfor (const edge of node.edges) {\n\t\tif (edgeIds.has(edge.id)) {\n\t\t\tthrow new Error(`Duplicate edge ID \"${edge.id}\" found in graph`);\n\t\t}\n\t\tedgeIds.add(edge.id);\n\t\tconst src = resolveEdgeEndpoint(\n\t\t\tedge.sources,\n\t\t\tedge.source,\n\t\t\tedge.sourcePort,\n\t\t\tnodes,\n\t\t\tports,\n\t\t\tedge.id,\n\t\t\t\"source\",\n\t\t);\n\t\tconst tgt = resolveEdgeEndpoint(\n\t\t\tedge.targets,\n\t\t\tedge.target,\n\t\t\tedge.targetPort,\n\t\t\tnodes,\n\t\t\tports,\n\t\t\tedge.id,\n\t\t\t\"target\",\n\t\t);\n\n\t\tconst srcNode = nodes.get(src.nodeId);\n\t\tif (srcNode?.parentId === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": source node \"${src.nodeId}\" is the root node and cannot be an edge endpoint`,\n\t\t\t);\n\t\t}\n\t\tconst tgtNode = nodes.get(tgt.nodeId);\n\t\tif (tgtNode?.parentId === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": target node \"${tgt.nodeId}\" is the root node and cannot be an edge endpoint`,\n\t\t\t);\n\t\t}\n\n\t\tedges.push({\n\t\t\telkEdgeRef: edge,\n\t\t\tid: edge.id,\n\t\t\townerNodeId: node.id,\n\t\t\tsourceNodeId: src.nodeId,\n\t\t\tsourcePortId: src.portId,\n\t\t\ttargetNodeId: tgt.nodeId,\n\t\t\ttargetPortId: tgt.portId,\n\t\t});\n\t}\n}\n\n/**\n * Parse an ELK JSON graph into a flat, indexed representation.\n *\n * Recursively traverses the graph hierarchy, computing absolute positions\n * for all nodes and ports.\n */\nexport function parseElkGraph(graph: ElkGraph): ParsedGraph {\n\tconst nodes = new Map<string, ResolvedNode>();\n\tconst ports = new Map<string, ResolvedPort>();\n\tconst edges: ResolvedEdge[] = [];\n\tconst edgeIds = new Set<string>();\n\n\tfunction visitNode(\n\t\tnode: ElkNode,\n\t\tparentAbsX: number,\n\t\tparentAbsY: number,\n\t\tparentId: string | null,\n\t): void {\n\t\tconst absX = parentAbsX + (node.x ?? 0);\n\t\tconst absY = parentAbsY + (node.y ?? 0);\n\t\tconst { width, height } = validateNode(node, parentId);\n\n\t\tconst resolvedPorts = resolvePorts(node, absX, absY, width, height, ports);\n\n\t\tconst padding = resolvePadding(node);\n\n\t\tif (nodes.has(node.id)) {\n\t\t\tthrow new Error(`Duplicate node ID \"${node.id}\" found in graph`);\n\t\t}\n\t\tnodes.set(node.id, {\n\t\t\thasChildren: (node.children?.length ?? 0) > 0,\n\t\t\theight,\n\t\t\tid: node.id,\n\t\t\tpadding,\n\t\t\tparentId,\n\t\t\tports: resolvedPorts,\n\t\t\twidth,\n\t\t\tx: absX,\n\t\t\ty: absY,\n\t\t});\n\n\t\tif (node.children) {\n\t\t\tconst childOffsetX = absX + (node.padding?.left ?? 0);\n\t\t\tconst childOffsetY = absY + (node.padding?.top ?? 0);\n\t\t\tfor (const child of node.children) {\n\t\t\t\tvisitNode(child, childOffsetX, childOffsetY, node.id);\n\t\t\t}\n\t\t}\n\n\t\tresolveEdges(node, nodes, ports, edges, edgeIds);\n\t}\n\n\tvisitNode(graph, 0, 0, null);\n\n\treturn { edges, nodes, ports };\n}\n","import type { ConnectionSide, ElkPoint, RouteResult } from \"./types\";\n\n/**\n * Infer the connection side from the direction of the first or last edge segment.\n */\nfunction inferSide(\n\tfrom: ElkPoint,\n\tto: ElkPoint,\n\tisTarget: boolean,\n): ConnectionSide {\n\tconst dx = to.x - from.x;\n\tconst dy = to.y - from.y;\n\n\t// Zero-length segment: default to east/west\n\tif (dx === 0 && dy === 0) return isTarget ? \"west\" : \"east\";\n\n\tconst horizontal = Math.abs(dx) >= Math.abs(dy);\n\tif (horizontal) return dx > 0 === !isTarget ? \"east\" : \"west\";\n\treturn dy > 0 === !isTarget ? \"south\" : \"north\";\n}\n\n/**\n * Convert raw point arrays from extractRoutes into RouteResult objects\n * with source/target side metadata.\n */\nexport function buildRouteResults(\n\trawRoutes: Map<string, ElkPoint[]>,\n): Map<string, RouteResult> {\n\tconst results = new Map<string, RouteResult>();\n\tfor (const [edgeId, points] of rawRoutes) {\n\t\tif (points.length < 2) continue;\n\n\t\tconst sourceSide = inferSide(points[0], points[1], false);\n\t\tconst targetSide = inferSide(\n\t\t\tpoints[points.length - 2],\n\t\t\tpoints[points.length - 1],\n\t\t\ttrue,\n\t\t);\n\n\t\tresults.set(edgeId, {\n\t\t\tbendPoints: points.length > 2 ? points.slice(1, -1) : [],\n\t\t\tsourcePoint: points[0],\n\t\t\tsourceSide,\n\t\t\ttargetPoint: points[points.length - 1],\n\t\t\ttargetSide,\n\t\t});\n\t}\n\treturn results;\n}\n","import type { ParsedGraph } from \"./parser\";\nimport type { ElkEdge, ElkPoint } from \"./types\";\n\n/**\n * Detect whether the edge uses extended format (sources/targets with sections)\n * or simple format (source/target with sourcePoint/targetPoint/bendPoints).\n */\nfunction isExtendedFormat(edge: ElkEdge): boolean {\n\treturn (\n\t\t(edge.sources !== undefined && edge.sources.length > 0) ||\n\t\t(edge.targets !== undefined && edge.targets.length > 0)\n\t);\n}\n\nfunction writeSimpleFormat(elkEdge: ElkEdge, points: ElkPoint[]): void {\n\telkEdge.sourcePoint = points[0];\n\telkEdge.targetPoint = points[points.length - 1];\n\tif (points.length > 2) {\n\t\telkEdge.bendPoints = points.slice(1, -1);\n\t} else {\n\t\tdelete elkEdge.bendPoints;\n\t}\n\t// Clean up extended format fields if forcing simple\n\tdelete elkEdge.sections;\n}\n\nfunction writeExtendedFormat(\n\telkEdge: ElkEdge,\n\tpoints: ElkPoint[],\n\tedgeId: string,\n): void {\n\telkEdge.sections = [\n\t\t{\n\t\t\tbendPoints: points.length > 2 ? points.slice(1, -1) : undefined,\n\t\t\tendPoint: points[points.length - 1],\n\t\t\tid: `${edgeId}_s0`,\n\t\t\tstartPoint: points[0],\n\t\t},\n\t];\n\t// Clean up simple format fields if forcing extended\n\tdelete elkEdge.sourcePoint;\n\tdelete elkEdge.targetPoint;\n\tdelete elkEdge.bendPoints;\n}\n\n/**\n * Convert absolute route points to coordinates relative to the edge's owner node.\n *\n * In ELK JSON, edge coordinates are relative to the parent node that owns the edge.\n * Libavoid produces absolute coordinates, so we need to subtract the owner's\n * absolute position (including padding offset for children).\n */\nfunction toRelativeCoords(\n\tpoints: ElkPoint[],\n\townerNodeId: string,\n\tparsed: ParsedGraph,\n): ElkPoint[] {\n\tconst owner = parsed.nodes.get(ownerNodeId);\n\tif (!owner) {\n\t\tthrow new Error(\n\t\t\t`Owner node \"${ownerNodeId}\" not found for coordinate transformation`,\n\t\t);\n\t}\n\n\t// Edge coordinates in ELK are relative to the owner's content area (inside padding),\n\t// which is the same coordinate space as the owner's children.\n\tconst offsetX = owner.x + owner.padding.left;\n\tconst offsetY = owner.y + owner.padding.top;\n\n\treturn points.map((p) => ({\n\t\tx: p.x - offsetX,\n\t\ty: p.y - offsetY,\n\t}));\n}\n\n/**\n * Write computed routes back into the ELK graph's edge objects.\n *\n * Modifies the graph in place. Auto-detects whether each edge uses\n * extended format (sources/targets/sections) or simple format\n * (source/target/sourcePoint/targetPoint/bendPoints) and writes accordingly.\n */\nexport function writeRoutesToGraph(\n\troutes: Map<string, ElkPoint[]>,\n\tparsed: ParsedGraph,\n): void {\n\tfor (const edge of parsed.edges) {\n\t\tconst absolutePoints = routes.get(edge.id);\n\t\tif (!absolutePoints || absolutePoints.length < 2) continue;\n\n\t\tconst points = toRelativeCoords(absolutePoints, edge.ownerNodeId, parsed);\n\t\tconst elkEdge = edge.elkEdgeRef;\n\n\t\tif (isExtendedFormat(elkEdge)) {\n\t\t\twriteExtendedFormat(elkEdge, points, edge.id);\n\t\t} else {\n\t\t\twriteSimpleFormat(elkEdge, points);\n\t\t}\n\t}\n}\n","import { AvoidLib } from \"libavoid-js\";\nimport {\n\tcreateLibavoidSession,\n\tdestroySession,\n\textractRoutes,\n} from \"./libavoid-session\";\nimport type { ParsedGraph, ResolvedEdge } from \"./parser\";\nimport { parseElkGraph } from \"./parser\";\nimport { buildRouteResults } from \"./route-result\";\nimport type {\n\tConnectionSide,\n\tElkGraph,\n\tElkPoint,\n\tLibavoidRoutingOptions,\n\tRouteResult,\n} from \"./types\";\nimport { writeRoutesToGraph } from \"./write-back\";\n\nlet initPromise: Promise<void> | null = null;\nlet initialWasmPath: string | undefined;\n\n/**\n * Initialize the libavoid WASM module.\n *\n * Called automatically by {@link routeEdges}, {@link routeEdgesInPlace}, and\n * `createRoutingSession` on first use. You can call it explicitly to control\n * initialization timing or to provide a WASM URL in browser environments.\n *\n * Subsequent calls return the same promise (the `wasmPath` from the first call wins).\n * A warning is logged if a subsequent call provides a different `wasmPath`.\n *\n * In browser environments, you **must** call this with a URL to the libavoid.wasm\n * file (e.g. served from your app's public directory) before using the routing APIs.\n */\nexport async function init(wasmPath?: string): Promise<void> {\n\tif (!initPromise) {\n\t\tinitialWasmPath = wasmPath;\n\t\tinitPromise = AvoidLib.load(wasmPath).catch((err: unknown) => {\n\t\t\tinitPromise = null;\n\t\t\tinitialWasmPath = undefined;\n\t\t\tif (isBrowserEnvironment() && !wasmPath) {\n\t\t\t\tthrow wrapBrowserWasmError(err);\n\t\t\t}\n\t\t\tthrow err;\n\t\t});\n\t} else if (wasmPath !== undefined && wasmPath !== initialWasmPath) {\n\t\tconsole.warn(\n\t\t\t`elkjs-libavoid: init() already called with a different wasmPath (\"${initialWasmPath}\"). ` +\n\t\t\t\t`The new path (\"${wasmPath}\") will be ignored.`,\n\t\t);\n\t}\n\treturn initPromise;\n}\n\nfunction isBrowserEnvironment(): boolean {\n\treturn typeof globalThis !== \"undefined\" && \"document\" in globalThis;\n}\n\nfunction wrapBrowserWasmError(err: unknown): Error {\n\tconst msg = err instanceof Error ? err.message : String(err);\n\tif (\n\t\tmsg.includes(\"file://\") ||\n\t\tmsg.includes(\"locateFile\") ||\n\t\tmsg.includes(\"fetch\")\n\t) {\n\t\treturn new Error(\n\t\t\t\"In browser environments, you must call init('/path/to/libavoid.wasm') \" +\n\t\t\t\t\"with a URL that serves the WASM file. Copy libavoid.wasm from \" +\n\t\t\t\t\"node_modules/libavoid-js/dist/libavoid.wasm to your public directory.\",\n\t\t);\n\t}\n\treturn err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Generate a fallback self-loop route for edges where source === target.\n */\nfunction generateSelfLoopRoute(\n\tnodeX: number,\n\tnodeY: number,\n\tnodeWidth: number,\n\tnodeHeight: number,\n\tbufferDistance: number,\n): {\n\tpoints: ElkPoint[];\n\tsourceSide: ConnectionSide;\n\ttargetSide: ConnectionSide;\n} {\n\tconst exitX = nodeX + nodeWidth;\n\tconst exitY = nodeY + nodeHeight * 0.4;\n\tconst enterY = nodeY + nodeHeight * 0.6;\n\tconst loopX = exitX + bufferDistance * 3;\n\tconst loopTopY = nodeY - bufferDistance * 2;\n\n\treturn {\n\t\tpoints: [\n\t\t\t{ x: exitX, y: exitY },\n\t\t\t{ x: loopX, y: exitY },\n\t\t\t{ x: loopX, y: loopTopY },\n\t\t\t{ x: exitX, y: loopTopY },\n\t\t\t{ x: exitX, y: enterY },\n\t\t],\n\t\tsourceSide: \"east\",\n\t\ttargetSide: \"east\",\n\t};\n}\n\n/**\n * Partition edges into self-loops vs normal, optionally filtering by edgeIds.\n */\nfunction partitionEdges(\n\tedges: ResolvedEdge[],\n\tedgeIds?: string[],\n): { normalEdges: ResolvedEdge[]; selfLoopEdges: ResolvedEdge[] } {\n\tlet filtered: ResolvedEdge[];\n\tif (edgeIds) {\n\t\tconst idSet = new Set(edgeIds);\n\t\tfiltered = edges.filter((e) => idSet.has(e.id));\n\t} else {\n\t\tfiltered = edges;\n\t}\n\n\tconst normalEdges: ResolvedEdge[] = [];\n\tconst selfLoopEdges: ResolvedEdge[] = [];\n\n\tfor (const edge of filtered) {\n\t\tif (edge.sourceNodeId === edge.targetNodeId) {\n\t\t\tselfLoopEdges.push(edge);\n\t\t} else {\n\t\t\tnormalEdges.push(edge);\n\t\t}\n\t}\n\n\treturn { normalEdges, selfLoopEdges };\n}\n\n/**\n * Build self-loop RouteResults for \"fallback\" handling mode.\n */\nfunction buildSelfLoopResults(\n\tselfLoopEdges: ResolvedEdge[],\n\tparsed: ParsedGraph,\n\tbufferDistance: number,\n): Map<string, RouteResult> {\n\tconst results = new Map<string, RouteResult>();\n\tfor (const edge of selfLoopEdges) {\n\t\tconst node = parsed.nodes.get(edge.sourceNodeId);\n\t\tif (!node) continue;\n\n\t\tconst loop = generateSelfLoopRoute(\n\t\t\tnode.x,\n\t\t\tnode.y,\n\t\t\tnode.width,\n\t\t\tnode.height,\n\t\t\tbufferDistance,\n\t\t);\n\t\tresults.set(edge.id, {\n\t\t\tbendPoints: loop.points.slice(1, -1),\n\t\t\tsourcePoint: loop.points[0],\n\t\t\tsourceSide: loop.sourceSide,\n\t\t\ttargetPoint: loop.points[loop.points.length - 1],\n\t\t\ttargetSide: loop.targetSide,\n\t\t});\n\t}\n\treturn results;\n}\n\n/**\n * Build self-loop raw point routes (for writeRoutesToGraph in the in-place path).\n */\nfunction buildSelfLoopPointRoutes(\n\tselfLoopEdges: ResolvedEdge[],\n\tparsed: ParsedGraph,\n\tbufferDistance: number,\n): Map<string, ElkPoint[]> {\n\tconst routes = new Map<string, ElkPoint[]>();\n\tfor (const edge of selfLoopEdges) {\n\t\tconst node = parsed.nodes.get(edge.sourceNodeId);\n\t\tif (!node) continue;\n\n\t\tconst loop = generateSelfLoopRoute(\n\t\t\tnode.x,\n\t\t\tnode.y,\n\t\t\tnode.width,\n\t\t\tnode.height,\n\t\t\tbufferDistance,\n\t\t);\n\t\troutes.set(edge.id, loop.points);\n\t}\n\treturn routes;\n}\n\n/**\n * Shared routing pipeline: init WASM, parse graph, create session, route, extract.\n * The session creation is inside try/finally to prevent WASM leaks on partial failure.\n */\nexport function validateGraph(graph: unknown): asserts graph is ElkGraph {\n\tif (!graph || typeof graph !== \"object\") {\n\t\tthrow new Error(\n\t\t\t`Invalid graph: expected an ELK JSON graph object, got ${typeof graph}`,\n\t\t);\n\t}\n\tif (!(\"id\" in graph) || typeof (graph as ElkGraph).id !== \"string\") {\n\t\tthrow new Error(\n\t\t\t'Invalid graph: missing required \"id\" property of type string',\n\t\t);\n\t}\n}\n\nasync function executeRouting(\n\tgraph: ElkGraph,\n\toptions?: LibavoidRoutingOptions,\n): Promise<{\n\tparsed: ParsedGraph;\n\trawRoutes: Map<string, ElkPoint[]>;\n\tselfLoopEdges: ResolvedEdge[];\n}> {\n\tvalidateGraph(graph);\n\tawait init();\n\n\tconst Avoid = AvoidLib.getInstance();\n\tconst parsed = parseElkGraph(graph);\n\n\tif (parsed.edges.length === 0) {\n\t\treturn { parsed, rawRoutes: new Map(), selfLoopEdges: [] };\n\t}\n\n\tconst { normalEdges, selfLoopEdges } = partitionEdges(\n\t\tparsed.edges,\n\t\toptions?.edgeIds,\n\t);\n\n\tlet rawRoutes = new Map<string, ElkPoint[]>();\n\n\tif (normalEdges.length > 0) {\n\t\tconst session = createLibavoidSession(\n\t\t\t{ ...parsed, edges: normalEdges },\n\t\t\tAvoid,\n\t\t\toptions,\n\t\t);\n\t\ttry {\n\t\t\tsession.router.processTransaction();\n\t\t\trawRoutes = extractRoutes(session);\n\t\t} finally {\n\t\t\tdestroySession(session);\n\t\t}\n\t}\n\n\treturn { parsed, rawRoutes, selfLoopEdges };\n}\n\n/**\n * Route edges on an ELK JSON graph using libavoid.\n *\n * Returns a Map of edge ID → RouteResult. The input graph is NOT modified.\n * Automatically initializes the WASM module on first call if not already done\n * (Node.js only; in browsers, call {@link init} with a WASM URL first).\n *\n * **Coordinate system:** The returned {@link RouteResult} points use\n * **absolute** coordinates. If you need coordinates relative to the edge's\n * owner node (as in ELK JSON), use {@link routeEdgesInPlace} instead.\n */\nexport async function routeEdges(\n\tgraph: ElkGraph,\n\toptions?: LibavoidRoutingOptions,\n): Promise<Map<string, RouteResult>> {\n\tconst { parsed, rawRoutes, selfLoopEdges } = await executeRouting(\n\t\tgraph,\n\t\toptions,\n\t);\n\n\tconst results = buildRouteResults(rawRoutes);\n\n\tif ((options?.selfLoopHandling ?? \"skip\") === \"fallback\") {\n\t\tconst loopResults = buildSelfLoopResults(\n\t\t\tselfLoopEdges,\n\t\t\tparsed,\n\t\t\toptions?.shapeBufferDistance ?? 4,\n\t\t);\n\t\tfor (const [id, result] of loopResults) {\n\t\t\tresults.set(id, result);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Route edges on an ELK JSON graph, mutating the graph in place.\n *\n * This is the backward-compatible API that writes routes directly\n * into the graph's edge objects.\n *\n * **Coordinate system:** Edge routes are written as coordinates **relative**\n * to the edge's owner node's content area (inside padding), matching the\n * ELK JSON convention. This differs from {@link routeEdges}, which returns\n * absolute coordinates.\n */\nexport async function routeEdgesInPlace(\n\tgraph: ElkGraph,\n\toptions?: LibavoidRoutingOptions,\n): Promise<ElkGraph> {\n\tconst { parsed, rawRoutes, selfLoopEdges } = await executeRouting(\n\t\tgraph,\n\t\toptions,\n\t);\n\n\twriteRoutesToGraph(rawRoutes, parsed);\n\n\tif ((options?.selfLoopHandling ?? \"skip\") === \"fallback\") {\n\t\tconst loopRoutes = buildSelfLoopPointRoutes(\n\t\t\tselfLoopEdges,\n\t\t\tparsed,\n\t\t\toptions?.shapeBufferDistance ?? 4,\n\t\t);\n\t\twriteRoutesToGraph(loopRoutes, parsed);\n\t}\n\n\treturn graph;\n}\n","import type { Avoid } from \"libavoid-js\";\nimport { AvoidLib } from \"libavoid-js\";\nimport type { LibavoidSession } from \"./libavoid-session\";\nimport {\n\tAUTO_PIN_CLASS_ID,\n\tcreateLibavoidSession,\n\tdestroySession,\n\textractRoutes,\n\tfreeWasm,\n\tregisterAutoPins,\n} from \"./libavoid-session\";\nimport { parseElkGraph } from \"./parser\";\nimport { init, validateGraph } from \"./route-edges\";\nimport { buildRouteResults } from \"./route-result\";\nimport type { ElkGraph, LibavoidRouterOptions, RouteResult } from \"./types\";\n\n/**\n * A long-lived routing session that supports incremental updates.\n *\n * libavoid natively supports moving shapes and re-routing only affected connectors.\n * This avoids the overhead of creating/destroying the full WASM state on every frame.\n */\nexport class RoutingSession {\n\tprivate session: LibavoidSession;\n\tprivate avoid: Avoid;\n\tprivate destroyed = false;\n\n\t/** @internal Use createRoutingSession() instead. */\n\tconstructor(session: LibavoidSession, avoid: Avoid) {\n\t\tthis.session = session;\n\t\tthis.avoid = avoid;\n\t}\n\n\t/**\n\t * Move a node to a new position. The shape obstacle is updated in the router.\n\t * Call processTransaction() after all moves to re-route affected edges.\n\t *\n\t * Moving a container node also moves all descendant shapes by the same delta.\n\t */\n\tmoveNode(nodeId: string, position: { x: number; y: number }): void {\n\t\tthis.assertNotDestroyed();\n\t\tconst node = this.session.nodes.get(nodeId);\n\t\tif (!node) {\n\t\t\tthrow new Error(`Node \"${nodeId}\" not found in session.`);\n\t\t}\n\t\tconst shapeEntry = this.session.shapes.get(nodeId);\n\t\tif (!shapeEntry && !node.hasChildren) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${nodeId}\" not found in session. Only leaf (non-root, non-container) nodes can be moved.`,\n\t\t\t);\n\t\t}\n\n\t\tconst dx = position.x - node.x;\n\t\tconst dy = position.y - node.y;\n\t\tif (dx === 0 && dy === 0) return;\n\n\t\tthis.moveShapeAndTrack(nodeId, node, dx, dy);\n\n\t\t// Move all descendant shapes by the same delta\n\t\tif (node.hasChildren) {\n\t\t\tfor (const [childId, childNode] of this.session.nodes) {\n\t\t\t\tif (this.isDescendantOf(childId, nodeId)) {\n\t\t\t\t\tthis.moveShapeAndTrack(childId, childNode, dx, dy);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate moveShapeAndTrack(\n\t\tnodeId: string,\n\t\tnode: { x: number; y: number; ports: { x: number; y: number }[] },\n\t\tdx: number,\n\t\tdy: number,\n\t): void {\n\t\tconst shapeEntry = this.session.shapes.get(nodeId);\n\t\tif (shapeEntry) {\n\t\t\tthis.session.router.moveShape_delta(shapeEntry.shapeRef, dx, dy);\n\t\t}\n\t\tnode.x += dx;\n\t\tnode.y += dy;\n\t\tfor (const port of node.ports) {\n\t\t\tport.x += dx;\n\t\t\tport.y += dy;\n\t\t}\n\t}\n\n\tprivate isDescendantOf(nodeId: string, ancestorId: string): boolean {\n\t\tlet current = this.session.nodes.get(nodeId);\n\t\twhile (current) {\n\t\t\tif (current.parentId === ancestorId) return true;\n\t\t\tif (current.parentId === null) return false;\n\t\t\tcurrent = this.session.nodes.get(current.parentId);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Add a new edge connector to the session.\n\t * The source and target nodes must already exist in the session.\n\t */\n\taddEdge(edge: {\n\t\tid: string;\n\t\tsource: string;\n\t\ttarget: string;\n\t\tsourcePort?: string;\n\t\ttargetPort?: string;\n\t}): void {\n\t\tthis.assertNotDestroyed();\n\t\tif (this.session.connectors.has(edge.id)) {\n\t\t\tthrow new Error(`Edge \"${edge.id}\" already exists in session.`);\n\t\t}\n\t\tif (edge.source === edge.target) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": self-loop edges (source === target) are not supported by libavoid.`,\n\t\t\t);\n\t\t}\n\n\t\tconst srcShape = this.session.shapes.get(edge.source);\n\t\tconst tgtShape = this.session.shapes.get(edge.target);\n\t\tif (!srcShape) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": source node \"${edge.source}\" not found in session.`,\n\t\t\t);\n\t\t}\n\t\tif (!tgtShape) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": target node \"${edge.target}\" not found in session.`,\n\t\t\t);\n\t\t}\n\n\t\tconst srcPinClass = edge.sourcePort\n\t\t\t? this.session.portPinClassIds.get(edge.sourcePort)\n\t\t\t: AUTO_PIN_CLASS_ID;\n\t\tconst tgtPinClass = edge.targetPort\n\t\t\t? this.session.portPinClassIds.get(edge.targetPort)\n\t\t\t: AUTO_PIN_CLASS_ID;\n\n\t\tif (srcPinClass === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": source port \"${edge.sourcePort}\" has no pin class`,\n\t\t\t);\n\t\t}\n\t\tif (tgtPinClass === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Edge \"${edge.id}\": target port \"${edge.targetPort}\" has no pin class`,\n\t\t\t);\n\t\t}\n\n\t\tconst srcEnd = new this.avoid.ConnEnd(srcShape.shapeRef, srcPinClass);\n\t\tconst tgtEnd = new this.avoid.ConnEnd(tgtShape.shapeRef, tgtPinClass);\n\t\tconst connRef = new this.avoid.ConnRef(this.session.router, srcEnd, tgtEnd);\n\t\tfreeWasm(srcEnd);\n\t\tfreeWasm(tgtEnd);\n\n\t\tthis.session.edgeEndpointNodes.add(edge.source);\n\t\tthis.session.edgeEndpointNodes.add(edge.target);\n\t\tthis.session.connectors.set(edge.id, {\n\t\t\tconnRef,\n\t\t\tedge: { id: edge.id },\n\t\t});\n\t}\n\n\t/**\n\t * Remove an edge from the session.\n\t */\n\tremoveEdge(edgeId: string): void {\n\t\tthis.assertNotDestroyed();\n\t\tconst entry = this.session.connectors.get(edgeId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Edge \"${edgeId}\" not found in session.`);\n\t\t}\n\n\t\tthis.session.router.deleteConnector(entry.connRef);\n\t\tthis.session.connectors.delete(edgeId);\n\t}\n\n\t/**\n\t * Process pending changes and return updated routes.\n\t * Only edges affected by shape moves or additions are re-routed.\n\t *\n\t * **Coordinate system:** The returned {@link RouteResult} points use\n\t * **absolute** coordinates (matching the coordinate space used during\n\t * session creation). To convert to ELK-relative coordinates, subtract\n\t * the owner node's content area origin.\n\t */\n\tprocessTransaction(): Map<string, RouteResult> {\n\t\tthis.assertNotDestroyed();\n\t\tthis.session.router.processTransaction();\n\t\tconst rawRoutes = extractRoutes(this.session);\n\t\treturn buildRouteResults(rawRoutes);\n\t}\n\n\t/**\n\t * Destroy the session and free WASM memory.\n\t * The session cannot be used after this call.\n\t */\n\tdestroy(): void {\n\t\tif (!this.destroyed) {\n\t\t\tdestroySession(this.session);\n\t\t\tthis.destroyed = true;\n\t\t}\n\t}\n\n\t/**\n\t * Support for the TC39 Explicit Resource Management proposal.\n\t * Allows usage with `using session = await createRoutingSession(graph)`.\n\t */\n\t[Symbol.dispose](): void {\n\t\tthis.destroy();\n\t}\n\n\tprivate assertNotDestroyed(): void {\n\t\tif (this.destroyed) {\n\t\t\tthrow new Error(\"RoutingSession has been destroyed.\");\n\t\t}\n\t}\n}\n\n/**\n * Create a long-lived routing session for incremental updates.\n *\n * Use this instead of routeEdges() when you need to update node positions\n * frequently (e.g., during drag operations) without re-creating the\n * entire router on every frame.\n *\n * @example\n * ```ts\n * const session = await createRoutingSession(graph, options);\n *\n * // On node drag:\n * session.moveNode(\"n1\", { x: newX, y: newY });\n * const routes = session.processTransaction();\n *\n * // Cleanup:\n * session.destroy();\n * ```\n */\nexport async function createRoutingSession(\n\tgraph: ElkGraph,\n\toptions?: LibavoidRouterOptions,\n): Promise<RoutingSession> {\n\tvalidateGraph(graph);\n\tawait init();\n\n\tconst Avoid = AvoidLib.getInstance();\n\tconst parsed = parseElkGraph(graph);\n\n\t// Filter self-loops — they can't be routed by libavoid\n\tconst normalEdges = parsed.edges.filter(\n\t\t(e) => e.sourceNodeId !== e.targetNodeId,\n\t);\n\tconst sessionParsed = { ...parsed, edges: normalEdges };\n\n\tconst session = createLibavoidSession(sessionParsed, Avoid, options);\n\n\t// Register auto pins for ALL nodes so addEdge() can connect to any node.\n\t// createLibavoidSession only registers auto pins for nodes referenced by existing edges.\n\tconst nodesWithAutoPin = new Set<string>();\n\tfor (const edge of normalEdges) {\n\t\tif (!edge.sourcePortId) nodesWithAutoPin.add(edge.sourceNodeId);\n\t\tif (!edge.targetPortId) nodesWithAutoPin.add(edge.targetNodeId);\n\t}\n\tfor (const [nodeId, shapeEntry] of session.shapes) {\n\t\tif (!nodesWithAutoPin.has(nodeId)) {\n\t\t\tregisterAutoPins(shapeEntry.shapeRef, Avoid);\n\t\t}\n\t}\n\n\t// Run initial transaction\n\tsession.router.processTransaction();\n\n\treturn new RoutingSession(session, Avoid);\n}\n"],"mappings":"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"}