@knowvah/dot-engine 1.2.7 → 1.4.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.
package/dist/api.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/model/graphInfo.ts", "../src/model/graph.ts", "../src/model/nodeInfo.ts", "../src/model/node.ts", "../src/model/cgraph-ops.ts", "../src/model/edgeInfo.ts", "../src/model/edge.ts", "../src/api/edge-ops.ts", "../src/errors.ts", "../src/common/html-string.ts", "../src/api/builder.ts", "../src/api/geometry.ts"],
4
- "sourcesContent": ["// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agraphinfo_t from lib/common/types.h.\n * All GD_* macros in the C source are replaced by typed fields on GraphInfo.\n *\n * Supporting types (LayoutParams, RatioKind, FontnameKind, RankEntry,\n * RankTable) live in layoutParams.ts and rankEntry.ts to keep file size\n * within limits.\n *\n * @see lib/common/types.h:Agraphinfo_t\n */\n\nimport type { Box, Point } from './geom.js';\nimport type { Node } from './node.js';\nimport type { Graph } from './graph.js';\nimport type { LayoutParams, FontnameKind } from './layoutParams.js';\nimport type { RankTable } from './rankEntry.js';\nimport type { GvcContext } from '../gvc/context.js';\n\n// Re-export supporting types so consumers can import from this module alone.\nexport type { RatioKind, LayoutParams } from './layoutParams.js';\n// FontnameKind is both a const object (value) and a type \u2014 a plain export\n// covers both; no separate `export type` needed for the same name.\nexport { FontnameKind } from './layoutParams.js';\nexport type { AdjMatrix, RankEntry, RankTable } from './rankEntry.js';\n\n// ---------------------------------------------------------------------------\n// GVC_t \u2014 the rendering context attached to a laid-out graph.\n// ---------------------------------------------------------------------------\n\n/**\n * The rendering context attached to a laid-out graph (C's GVC_t). Retained\n * under the historical name `GVContext` for the `common/types.ts` re-export;\n * it now resolves to the concrete {@link GvcContext} rather than `unknown`, so\n * every `g.info.gvc` read is typed instead of an ad-hoc `as { textMeasurer? }`\n * cast. Type-only import \u2014 erased at compile time, so no runtime cycle with\n * `gvc/context.ts`.\n *\n * @see lib/gvc/gvcext.h:GVC_t\n */\nexport type GVContext = GvcContext;\n\n// ---------------------------------------------------------------------------\n// Agraphinfo_t \u2014 @see lib/common/types.h:Agraphinfo_t\n// ---------------------------------------------------------------------------\n\n/**\n * Graph-level info record, equivalent to Agraphinfo_t in the C source.\n * All GD_* accessor macros in the C source become plain typed fields here.\n *\n * Required fields (non-pointer C fields always present after graph_init):\n * - bb, rankdir, flags, charset, gui_state, has_labels, has_images\n *\n * All C pointer fields become optional (`| undefined`) because they may\n * legitimately be null/unset until the relevant layout pass runs.\n *\n * @see lib/common/types.h:Agraphinfo_t\n */\nexport interface GraphInfo {\n // -----------------------------------------------------------------------\n // Core rendering fields \u2014 GD_* macros that are always meaningful\n // -----------------------------------------------------------------------\n\n /**\n * Bounding box of the graph after layout.\n * @see lib/common/types.h:GD_bb\n */\n bb: Box;\n\n /**\n * Rank direction + flip flags (raw int, encode via SET_RANKDIR macro).\n * GD_rankdir(g) = rankdir & 0x3; GD_realrankdir(g) = rankdir >> 2.\n * @see lib/common/types.h:GD_rankdir2\n */\n rankdir: number;\n\n /**\n * Edge type and layout engine flags bitmask.\n * @see lib/common/types.h:GD_flags\n */\n flags: number;\n\n /**\n * Input character set constant (CHAR_UTF8, CHAR_LATIN1, CHAR_BIG5).\n * @see lib/common/types.h:GD_charset\n */\n charset: number;\n\n /**\n * GUI state flags bitmask (GUI_STATE_ACTIVE, _SELECTED, _VISITED, _DELETED).\n * @see lib/common/types.h:GD_gui_state\n */\n gui_state: number;\n\n /**\n * Bitmask of which label types are present in the graph.\n * @see lib/common/types.h:GD_has_labels\n */\n has_labels: number;\n\n /**\n * True if any node uses an image.\n * @see lib/common/types.h:GD_has_images\n */\n has_images: boolean;\n\n /**\n * Library-API-only flag (no C analogue): set by GvcContext.layout once the\n * graph has been laid out. Not cleared by freeLayout \u2014 node coordinates and\n * splines persist on the model after engine scratch is freed, so the\n * \"render then getLayout\" workflow stays valid. The public `getLayout` reads\n * it to reject a never-laid-out graph instead of returning the calloc-zero\n * geometry defaults as if they were real coordinates.\n */\n laidOut?: boolean;\n\n // -----------------------------------------------------------------------\n // Optional pointer fields \u2014 all C pointers become `| undefined`\n // -----------------------------------------------------------------------\n\n /**\n * Resolved layout parameters (set by the layout engine init).\n * @see lib/common/types.h:GD_drawing\n */\n drawing?: LayoutParams;\n\n /**\n * Cluster or graph title label. Typed fully in Batch 5b (textlabel_t).\n * @see lib/common/types.h:GD_label\n */\n label?: unknown;\n\n /**\n * Margin sizes for graph labels; four Points corresponding to pointf[4].\n * Indices: [0]=left, [1]=right, [2]=top, [3]=bottom per C convention.\n * @see lib/common/types.h:GD_border\n */\n border?: [Point, Point, Point, Point];\n\n /**\n * GVC context for cross-graph globals.\n * @see lib/common/types.h:GD_gvc\n */\n gvc?: GVContext;\n\n /**\n * Layout-engine cleanup function; called by graph_cleanup to free\n * engine-specific data attached to this graph.\n * @see lib/common/types.h:GD_cleanup\n */\n cleanup?: (g: Graph) => void;\n\n /**\n * Height below extremal ranks (dot layout).\n * @see lib/common/types.h:GD_ht1\n */\n ht1?: number;\n\n /**\n * Height above extremal ranks (dot layout).\n * @see lib/common/types.h:GD_ht2\n */\n ht2?: number;\n\n /**\n * Engine-specific algorithm state.\n * @see lib/common/types.h:GD_alg\n */\n alg?: unknown;\n\n // -----------------------------------------------------------------------\n // neato/fdp/sfdp-specific fields (#ifndef DOT_ONLY in C)\n // -----------------------------------------------------------------------\n\n /**\n * Node list for neato layout.\n * @see lib/common/types.h:GD_neato_nlist\n */\n neato_nlist?: Node[];\n\n /**\n * Move count for iterative neato placement.\n * @see lib/common/types.h:GD_move\n */\n move?: number;\n\n /**\n * All-pairs distance matrix (neato).\n * @see lib/common/types.h:GD_dist\n */\n dist?: number[][];\n\n /**\n * Spring constant matrix (neato).\n * @see lib/common/types.h:GD_spring\n */\n spring?: number[][];\n\n /**\n * Spring force sum matrix (neato).\n * @see lib/common/types.h:GD_sum_t\n */\n sum_t?: number[][];\n\n /**\n * Position delta tensor (neato, ndim-dimensional).\n * @see lib/common/types.h:GD_t\n */\n t?: number[][][];\n\n /**\n * Number of layout dimensions.\n * @see lib/common/types.h:GD_ndim\n */\n ndim?: number;\n\n /**\n * Original number of layout dimensions.\n * @see lib/common/types.h:GD_odim\n */\n odim?: number;\n\n // -----------------------------------------------------------------------\n // dot-specific fields (#ifndef NEATO_ONLY in C)\n // -----------------------------------------------------------------------\n\n /**\n * Number of cluster subgraphs.\n * @see lib/common/types.h:GD_n_cluster\n */\n n_cluster?: number;\n\n /**\n * Count of cluster edges created by fdp's processClusterEdges (edges whose\n * endpoint names a cluster). Non-zero \u21D2 HAS_CLUST_EDGE(g).\n * @see lib/common/utils.c:processClusterEdges (cl_edge_t.n_cluster_edges)\n */\n n_cluster_edges?: number;\n\n /**\n * Cluster margin (in points). Corresponds to the `margin` attribute on a\n * cluster graph, read by `late_int(g, G_margin, CL_OFFSET, 0)` in C.\n * @see lib/dotgen/position.c:contain_nodes\n */\n clusterMargin?: number;\n\n /**\n * Cluster array.\n *\n * IMPORTANT: In C this is 1-indexed \u2014 clust[1..n_cluster]. This TypeScript\n * array is 0-indexed; callers must add 1 when converting between C indices\n * and this array's indices, or subtract 1 when coming from C.\n *\n * @see lib/common/types.h:GD_clust\n */\n clust?: Graph[];\n\n /**\n * The root graph for dot layout purposes.\n * @see lib/common/types.h:GD_dotroot\n */\n dotroot?: Graph;\n\n /**\n * Linked-list head of nodes for dot layout traversal.\n * @see lib/common/types.h:GD_nlist\n */\n nlist?: Node;\n\n /**\n * Rank table for dot layout (array indexed by rank level).\n * @see lib/common/types.h:GD_rank\n */\n rank?: RankTable;\n\n /**\n * Containing cluster (not parent subgraph).\n * @see lib/common/types.h:GD_parent\n */\n parent?: Graph;\n\n /**\n * Cluster nesting level (not node level).\n * @see lib/common/types.h:GD_level\n */\n level?: number;\n\n /**\n * Set leader for minimum rank.\n * @see lib/common/types.h:GD_minrep\n */\n minrep?: Node;\n\n /**\n * Set leader for maximum rank.\n * @see lib/common/types.h:GD_maxrep\n */\n maxrep?: Node;\n\n /**\n * Fast-graph node component list.\n * @see lib/common/types.h:GD_comp (nlist_t)\n */\n comp?: Node[];\n\n /**\n * Set leader for minimum connected component.\n * @see lib/common/types.h:GD_minset\n */\n minset?: Node;\n\n /**\n * Set leader for maximum connected component.\n * @see lib/common/types.h:GD_maxset\n */\n maxset?: Node;\n\n /**\n * Minimum rank value in this graph.\n * @see lib/common/types.h:GD_minrank\n */\n minrank?: number;\n\n /**\n * Maximum rank value in this graph.\n * @see lib/common/types.h:GD_maxrank\n */\n maxrank?: number;\n\n /**\n * True if the graph has any flat (same-rank) edges.\n * @see lib/common/types.h:GD_has_flat_edges\n */\n has_flat_edges?: boolean;\n\n /**\n * Parity correction for the rank-array renumbering done by `abomination`.\n * C inserts a flat-label rank at index -1 (so real nodes keep their even rank\n * index); this 0-based port instead shifts every `ND_rank` up by 1 (AD-2),\n * which inverts the rank-index parity that `make_LR_constraints` uses to pick\n * the LR separation (`sep[i & 1]`). Each shift increments this offset so\n * `lrSep` can recover C's parity via `(rankIdx + abomShift) & 1`. 0 / unset\n * means no abomination happened. @see lib/dotgen/flat.c:abomination\n */\n abomShift?: number;\n\n /**\n * Show subdivision boxes for debugging; 0 means off.\n * @see lib/common/types.h:GD_showboxes\n */\n showboxes?: number;\n\n /**\n * SVG font name mangling setting.\n * @see lib/common/types.h:GD_fontnames\n */\n fontnames?: FontnameKind;\n\n /**\n * Node separation in points.\n * @see lib/common/types.h:GD_nodesep\n */\n nodesep?: number;\n\n /**\n * Rank separation in points.\n * @see lib/common/types.h:GD_ranksep\n */\n ranksep?: number;\n\n /**\n * Left-boundary node of the bounding box.\n * @see lib/common/types.h:GD_ln\n */\n ln?: Node;\n\n /**\n * Right-boundary node of the bounding box.\n * @see lib/common/types.h:GD_rn\n */\n rn?: Node;\n\n /**\n * Cluster leader node.\n * @see lib/common/types.h:GD_leader\n */\n leader?: Node;\n\n /**\n * Per-rank leader node array.\n * @see lib/common/types.h:GD_rankleader\n */\n rankleader?: Node[];\n\n /**\n * True if the cluster has been expanded.\n * @see lib/common/types.h:GD_expanded\n */\n expanded?: boolean;\n\n /**\n * Installation state flag.\n * @see lib/common/types.h:GD_installed\n */\n installed?: number;\n\n /**\n * Set type for union-find operations.\n * @see lib/common/types.h:GD_set_type\n */\n set_type?: number;\n\n /**\n * Label position flag for cluster labels.\n * @see lib/common/types.h:GD_label_pos\n */\n label_pos?: number;\n\n /**\n * Whether exact rank separation is enforced.\n * @see lib/common/types.h:GD_exact_ranksep\n */\n exact_ranksep?: boolean;\n\n /**\n * Whether the rank direction is flipped (used in flat_reorder and build_ranks).\n * Set from GD_realrankdir: true when layout is LR or RL (horizontal).\n * @see lib/dotgen/mincross.c:GD_flip\n */\n flip?: boolean;\n\n /**\n * Whether edge concentration is enabled (merges parallel edges).\n * Corresponds to the `concentrate` graph attribute.\n * @see lib/dotgen/class2.c:Concentrate\n */\n concentrate?: boolean;\n\n /**\n * Whether the graph has compound edges (lhead/ltail attributes set on edges).\n * When true, dot_compoundEdges clips splines to cluster bounding boxes.\n * Corresponds to the `compound` graph attribute.\n * @see lib/dotgen/compound.c:dot_compoundEdges\n * @see lib/common/types.h:GD_flags (via compound flag)\n */\n compound?: boolean;\n\n /**\n * AD-8 phase marker: true between create_aux_edges and set_xcoords,\n * while ND_rank holds x-coordinates instead of rank indices.\n * Cleared by setXcoords after ND_coord.x is populated.\n * @see lib/dotgen/position.c:set_xcoords (AD-8)\n */\n rankIsXCoord?: boolean;\n\n /**\n * MT19937 seed used by the SGD layout engine.\n * Corresponds to the rk_seed() call in lib/neatogen/sgd.c.\n * Defaults to 0 when not set.\n *\n * @see lib/neatogen/sgd.c:sgd (rk_seed call)\n */\n seed?: number;\n\n /**\n * Per-layout flag: true when edge label positions have been computed by\n * the spline-routing pass (dotsplines.c:EdgeLabelsDone).\n *\n * C uses a process-global `EdgeLabelsDone` (lib/common/globals.h) reset in\n * lib/common/input.c:commonInitGraph and set at lib/dotgen/dotsplines.c:471.\n * This port lifts it onto GraphInfo so it is per-graph (no globals).\n *\n * @see lib/common/globals.h:EdgeLabelsDone\n * @see lib/dotgen/dotsplines.c:471\n */\n edgeLabelsDone?: boolean;\n\n /**\n * Neato start attribute: controls initial placement and RNG seed.\n * Values: \"random[N]\" | \"self\" | \"regular\" | numeric string.\n * Corresponds to agget(g, \"start\") in lib/neatogen/neatoinit.c:setSeed.\n *\n * @see lib/neatogen/neatoinit.c:setSeed\n */\n start?: string;\n\n /**\n * Neato layout mode string attribute.\n * Values: \"KK\" | \"major\" | \"hier\" | \"ipsep\" | \"sgd\".\n * Corresponds to agget(g, \"mode\") in lib/neatogen/neato.h:neatoMode.\n *\n * @see lib/neatogen/neato.h:neatoMode\n */\n mode?: string;\n\n /**\n * Neato overlap removal control.\n * \"false\" disables VPSC overlap removal; any other value enables it.\n * Corresponds to agget(g, \"overlap\") in lib/neatogen/neatoinit.c.\n *\n * @see lib/neatogen/neatoinit.c:neato_layout\n */\n overlap?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a zero-initialized GraphInfo with all required fields set to their\n * defaults. All optional fields are left undefined (absent from the object),\n * matching a freshly allocated Agraphinfo_t after graph_init().\n *\n * @see lib/common/utils.c:graph_init\n */\nexport function makeGraphInfo(): GraphInfo {\n return {\n bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },\n rankdir: 0,\n flags: 0,\n charset: 0,\n gui_state: 0,\n has_labels: 0,\n has_images: false,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agraph_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agraph_s\n * @see lib/cgraph/cgraph.h:Agdesc_s\n */\n\nimport type { Edge } from './edge.js';\nimport type { Node } from './node.js';\nimport { type GraphInfo, makeGraphInfo } from './graphInfo.js';\n\n/**\n * Describes the kind of graph, corresponding to the Agdesc_t descriptor\n * fields `directed` and `strict` in the C implementation.\n *\n * @see lib/cgraph/cgraph.h:Agdesc_s\n * @see lib/cgraph/graph.c:Agdirected\n * @see lib/cgraph/graph.c:Agstrictdirected\n * @see lib/cgraph/graph.c:Agundirected\n * @see lib/cgraph/graph.c:Agstrictundirected\n */\nexport type GraphKind =\n | 'directed'\n | 'undirected'\n | 'strict-directed'\n | 'strict-undirected';\n\n/**\n * Represents an Agraph_t \u2014 a graph or subgraph.\n *\n * In the C implementation Agraph_t holds n_seq/n_id (node dicts), e_seq/e_id\n * (edge dicts), g_seq/g_id (subgraph dicts), parent, root, and a shared\n * Agclos_t. Here we use native TypeScript collections in place of libcdt\n * Dict_t, and the `info` field replaces the agbindrec / GD_* mechanism (AD-1).\n *\n * Nodes are owned by the root graph. Subgraphs hold references to the same\n * Node instances (Subgraph Ownership Semantics, cgraph.md).\n *\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\nexport class Graph {\n /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */\n readonly name: string;\n\n /**\n * True when the graph was declared with NO name (`digraph {`), as opposed to\n * an explicit empty name (`digraph \"\" {`). Both store `name === ''`, but\n * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while\n * the empty-named one keeps `\"\"`. Only the imagemap `<map id/name>` reads this\n * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an\n * anonymous root round-trips as `digraph {`, not `digraph %1 {`.\n * @see lib/cgraph/id.c:idmap (anon \u2192 `%1`) ; lib/cgraph/agraph.c:agnameof\n */\n readonly anonymous: boolean;\n\n /**\n * Directed/strict classification; mirrors Agdesc_t.directed and\n * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s\n */\n readonly kind: GraphKind;\n\n /**\n * Node set in insertion order, keyed by name.\n * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n nodes: Map<string, Node>;\n\n /**\n * All edges owned by this graph (root graph only for ownership; subgraphs\n * reference a subset). Mirrors Agraph_t.e_seq.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n edges: Edge[];\n\n /**\n * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n subgraphs: Map<string, Graph>;\n\n /**\n * String attributes (agget/agset equivalents).\n * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /** Default attributes applied to all nodes in this graph scope. */\n nodeDefaults: Map<string, string>;\n\n /** Default attributes applied to all edges in this graph scope. */\n edgeDefaults: Map<string, string>;\n\n /**\n * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->\n * root, inner wins) captured when this subgraph was OPENED during parsing.\n * Graph attributes set on an ancestor AFTER this subgraph was created are not\n * included \u2014 mirroring cgraph's parse-time agsubg defval copy. Used for\n * order-correct label/font inheritance (e.g. a root `label=` declared after a\n * cluster must NOT apply to that cluster). Undefined on the root graph.\n * @see lib/cgraph/graph.c:agsubg (attr defval copy)\n */\n graphDefaultsSnapshot?: Map<string, string>;\n\n /**\n * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than\n * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_\n * KEYS). cgraph distinguishes these structurally \u2014 a seeded value is inherited\n * through the dict view and has no local symbol, while a local declaration\n * creates one even when its value equals the inherited value \u2014 so the\n * serializer needs the provenance to decide what write_dict would print.\n * Undefined on the root graph and on any scope that seeded nothing.\n * @see lib/cgraph/write.c:262 write_dict\n */\n seededAttrs?: Set<string>;\n\n /**\n * Graph-attribute keys whose declaration in THIS scope was the first anywhere\n * in the graph. cgraph's `setattr` splits on exactly this: a key with no\n * existing symbol takes the global-declaration branch, which ends up seeding\n * every direct subgraph that already exists with a local empty value (see\n * `eagerEmptyParts`); a key some earlier scope already declared takes the\n * \"new local definition\" branch and touches no subgraph at all. So an\n * identical `graph [fill=x]` statement makes a child print `fill=\"\"` or print\n * nothing purely according to whether a SIBLING declared `fill` first.\n * Undefined on any scope that declared nothing new.\n * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)\n */\n firstGraphDecl?: Set<string>;\n\n /**\n * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: GraphInfo;\n\n /**\n * Immediate parent graph. null for root graphs.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n parent: Graph | null;\n\n /**\n * Root (main) graph. Self-referential for root graphs;\n * points up for subgraphs.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n root: Graph;\n\n /**\n * Subgraph sequence number (AGSEQ). A global counter on the root, assigned\n * at creation in source order, counting anonymous subgraphs. The root keeps\n * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to\n * emit cluster ids `clust<seq>`.\n * @see lib/cgraph/cgraph.h:AGSEQ\n * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))\n */\n seq = 0;\n\n /**\n * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ\n * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`\n * pre-increments it. Other graphs leave it at 0.\n * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])\n */\n subgSeqCounter = 0;\n\n /**\n * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy \u2014\n * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the\n * ROOT only, at parse time, whenever a graph attribute is assigned on any\n * (sub)graph: setting `ordering=out` on a subgraph declares \"ordering\"\n * graph-wide with an empty default, so `agGraphAttr(root, \"ordering\")` returns\n * `\"\"` (not undefined) \u2014 mirroring `agfindgraphattr`/`late_string`. Read via\n * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.\n * @see lib/cgraph/attr.c:agattr (graph-wide declaration, \"\" default)\n */\n declaredGraphAttrs: Set<string> = new Set();\n\n /** @see lib/cgraph/graph.c:agopen */\n constructor(name: string, kind: GraphKind, anonymous = false) {\n this.name = name;\n this.anonymous = anonymous;\n this.kind = kind;\n this.nodes = new Map();\n this.edges = [];\n this.subgraphs = new Map();\n this.attrs = new Map();\n this.nodeDefaults = new Map();\n this.edgeDefaults = new Map();\n this.info = makeGraphInfo();\n this.parent = null;\n this.root = this;\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agnodeinfo_t from lib/common/types.h.\n *\n * All ND_* macros in the C source expand into this struct via AGDATA().\n * In TypeScript, ND_* accesses become direct property reads/writes on NodeInfo.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/common/types.h:ND_* macros\n */\n\nimport type { Point, Box } from './geom.js';\nimport type { Edge } from './edge.js';\nimport type { Graph } from './graph.js';\nimport type { Node } from './node.js';\nimport type { NodeAlgData } from './algData.js';\n\n// Per-engine ND_alg data types live in ./algData.js (split out to keep this\n// file under the size cap). Re-exported here so existing importers of\n// './nodeInfo.js' continue to resolve them unchanged.\nexport type {\n DotAlgData, NeatoAlgData, FdpAlgData, FdpDndata, CircoNData, CircoCData,\n TwopiAlgData, OsageAlgData, PatchworkAlgData, NodeAlgData,\n} from './algData.js';\nexport { THETA_UNSET } from './algData.js';\n\n// ---------------------------------------------------------------------------\n// Edge list helper type (mirrors elist in types.h)\n// ---------------------------------------------------------------------------\n\n/**\n * Dynamic edge list, ported from the C `elist` struct.\n *\n * @see lib/common/types.h: typedef struct elist { edge_t **list; size_t size; }\n */\nexport interface EdgeList {\n list: Edge[];\n size: number;\n}\n\n// ---------------------------------------------------------------------------\n// NodeInfo interface\n// ---------------------------------------------------------------------------\n\n/**\n * Node-level layout info record, ported from Agnodeinfo_t in lib/common/types.h.\n *\n * This interface replaces all ND_* macro accesses in the C source. Required\n * (always-present) fields mirror the non-pointer, non-conditional fields of\n * Agnodeinfo_t. Optional fields correspond to pointer fields and to fields\n * gated by #ifndef DOT_ONLY / #ifndef NEATO_ONLY.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/common/types.h:ND_* macros\n */\nexport interface NodeInfo {\n // -------------------------------------------------------------------------\n // Required fields \u2014 always present, non-pointer in C\n // -------------------------------------------------------------------------\n\n /** Final layout coordinate (node center). @see lib/common/types.h:ND_coord */\n coord: Point;\n\n /** Node width in inches. @see lib/common/types.h:ND_width */\n width: number;\n\n /** Node height in inches. @see lib/common/types.h:ND_height */\n height: number;\n\n /** Bounding box. @see lib/common/types.h:ND_bb */\n bb: Box;\n\n /** Total height in points. @see lib/common/types.h:ND_ht */\n ht: number;\n\n /** Left half-width in points. @see lib/common/types.h:ND_lw */\n lw: number;\n\n /** Right half-width in points. @see lib/common/types.h:ND_rw */\n rw: number;\n\n /**\n * Width in points with penwidth taken into account.\n * @see lib/common/types.h:ND_outline_width\n */\n outline_width: number;\n\n /**\n * Height in points with penwidth taken into account.\n * @see lib/common/types.h:ND_outline_height\n */\n outline_height: number;\n\n /**\n * Pre-periphery-growth node width in inches: the box the innermost\n * vertex ring is scaled to before peripheries step outward. Only set\n * by the measurer sizing path; consumed by multi-periphery vertex\n * generation. (C keeps the equivalent implicitly \u2014 poly_init builds\n * vertices and bb in one pass.)\n * @see lib/common/shapes.c:poly_init\n */\n base_width?: number;\n\n /** Pre-periphery-growth node height in inches. @see base_width */\n base_height?: number;\n\n /**\n * Layout state (char in C). @see lib/common/types.h:ND_state\n */\n state: number;\n\n /**\n * Node state for GUI ops (unsigned char in C).\n * Bitfield values: GUI_STATE_ACTIVE, GUI_STATE_SELECTED,\n * GUI_STATE_VISITED, GUI_STATE_DELETED.\n * @see lib/common/types.h:ND_gui_state\n */\n gui_state: number;\n\n /** True if node represents a cluster. @see lib/common/types.h:ND_clustnode */\n clustnode: boolean;\n\n // -------------------------------------------------------------------------\n // Optional pointer / engine-specific fields\n // -------------------------------------------------------------------------\n\n /**\n * Main node label (textlabel_t* in C).\n * Typed as unknown until textlabel_t is ported in Batch 5b.\n * @see lib/common/types.h:ND_label\n */\n label?: unknown;\n\n /**\n * External node label (textlabel_t* in C).\n * Typed as unknown until textlabel_t is ported in Batch 5b.\n * @see lib/common/types.h:ND_xlabel\n */\n xlabel?: unknown;\n\n /**\n * Node shape descriptor (shape_desc* in C).\n * Typed as unknown until shape_desc is ported.\n * @see lib/common/types.h:ND_shape\n */\n shape?: unknown;\n\n /**\n * Shape-specific data (polygon_t*, field_t*, or engine struct in C).\n * Typed as unknown until the concrete shape types are ported.\n * @see lib/common/types.h:ND_shape_info\n */\n shape_info?: unknown;\n\n // -------------------------------------------------------------------------\n // Fields from #ifndef NEATO_ONLY block\n // -------------------------------------------------------------------------\n\n /**\n * During the dot rank-assignment phase this field holds the assigned rank\n * (integer row in the layered graph).\n *\n * DUAL-USE WARNING: During the dot position phase (x-coordinate assignment),\n * `rank` is REPURPOSED as the x-coordinate by network simplex. Do not read\n * this field as a rank value while the position phase is active. The field\n * is restored to its rank value by `set_xcoords` at the end of the position\n * phase. See lib/dotgen/position.c and AD-8.\n *\n * @see lib/common/types.h:ND_rank\n */\n rank?: number;\n\n /**\n * Initial order for ordered edges; also used in crossing-minimization.\n * @see lib/common/types.h:ND_order\n */\n order?: number;\n\n /**\n * AD-8: True while the position phase is active and `rank` holds an\n * x-coordinate instead of a rank index. Set before rank(g,2,\u2026) is called;\n * cleared by setXcoords after ND_coord.x is populated.\n * @see lib/dotgen/position.c:set_xcoords\n */\n rankIsXCoord?: boolean;\n\n /** @see lib/common/types.h:ND_mval */\n mval?: number;\n\n /**\n * Fast-graph / spanning-tree node type (char in C).\n * @see lib/common/types.h:ND_node_type\n */\n node_type?: number;\n\n /**\n * Rank type classification (char in C).\n * @see lib/common/types.h:ND_ranktype\n */\n ranktype?: number;\n\n /**\n * Weight class (char in C).\n * @see lib/common/types.h:ND_weight_class\n */\n weight_class?: number;\n\n /**\n * Visited / traversal mark (size_t in C).\n * @see lib/common/types.h:ND_mark\n */\n mark?: number;\n\n /**\n * On-stack flag for DFS (char in C).\n * @see lib/common/types.h:ND_onstack\n */\n onstack?: number;\n\n /**\n * True if edge has port info. @see lib/common/types.h:ND_has_port\n */\n has_port?: boolean;\n\n /**\n * Showboxes flag (unsigned char in C).\n * @see lib/common/types.h:ND_showboxes\n */\n showboxes?: number;\n\n // Edge adjacency lists (elist fields)\n\n /** In-edges. @see lib/common/types.h:ND_in */\n in?: EdgeList;\n\n /** Out-edges. @see lib/common/types.h:ND_out */\n out?: EdgeList;\n\n /** Flat in-edges (same-rank). @see lib/common/types.h:ND_flat_in */\n flat_in?: EdgeList;\n\n /** Flat out-edges (same-rank). @see lib/common/types.h:ND_flat_out */\n flat_out?: EdgeList;\n\n /** Other (non-tree, non-flat) edges. @see lib/common/types.h:ND_other */\n other?: EdgeList;\n\n /** Saved in-edges (for virtual node removal). @see lib/common/types.h:ND_save_in */\n save_in?: EdgeList;\n\n /** Saved out-edges (for virtual node removal). @see lib/common/types.h:ND_save_out */\n save_out?: EdgeList;\n\n /** Network-simplex tree in-edges. @see lib/common/types.h:ND_tree_in */\n tree_in?: EdgeList;\n\n /** Network-simplex tree out-edges. @see lib/common/types.h:ND_tree_out */\n tree_out?: EdgeList;\n\n // Doubly-linked list through fast-graph\n\n /** Next node in doubly-linked fast-graph list. @see lib/common/types.h:ND_next */\n next?: Node;\n\n /** Previous node in doubly-linked fast-graph list. @see lib/common/types.h:ND_prev */\n prev?: Node;\n\n // Network-simplex fields\n\n /**\n * Network-simplex spanning-tree parent edge.\n * @see lib/common/types.h:ND_par\n */\n par?: Edge;\n\n /** Low limit for network simplex. @see lib/common/types.h:ND_low */\n low?: number;\n\n /** Limit for network simplex subtree. @see lib/common/types.h:ND_lim */\n lim?: number;\n\n /** Priority for network simplex edge selection. @see lib/common/types.h:ND_priority */\n priority?: number;\n\n // Union-find / collapsing\n\n /** Union-find tree size. @see lib/common/types.h:ND_UF_size */\n UF_size?: number;\n\n /** Union-find parent node. @see lib/common/types.h:ND_UF_parent */\n UF_parent?: Node;\n\n /** Representative node for a collapsed set. @see lib/common/types.h:ND_rep */\n rep?: Node;\n\n /** Set leader for min/max rank. @see lib/common/types.h:ND_set */\n set?: Node;\n\n /** Cluster subgraph that this node belongs to. @see lib/common/types.h:ND_clust */\n clust?: Graph;\n\n /**\n * Flat-edge label constraint edge stored in ND_alg during dot position phase.\n * Set by flat.c when a virtual node hosts a flat-edge label; read by\n * make_LR_constraints and dotsplines.c.\n * @see lib/dotgen/flat.c:ND_alg (flat edge label usage)\n */\n posAlg?: Edge;\n\n // -------------------------------------------------------------------------\n // Fields from #ifndef DOT_ONLY block (neato/fdp/sfdp)\n // -------------------------------------------------------------------------\n\n /**\n * True if the node's position is pinned (pos attribute with !).\n * @see lib/common/types.h:ND_pinned\n */\n pinned?: boolean;\n\n /**\n * True if the node has a user-supplied `pos=` (C's ND_pinned >= P_SET, i.e.\n * hasPos). Distinct from {@link pinned} (P_PIN, isFixed): a plain `pos=`\n * seeds the initial layout without fixing the node. neato uses it to start\n * majorization from the input positions (user_pos + hasPos).\n * @see lib/neatogen/neatoinit.c:user_pos; lib/common/macros.h:hasPos\n */\n posSet?: boolean;\n\n /**\n * Internal integer node ID used by neato/fdp.\n * @see lib/common/types.h:ND_id\n */\n id?: number;\n\n /**\n * Index into the priority queue heap (neato/fdp).\n * @see lib/common/types.h:ND_heapindex\n */\n heapindex?: number;\n\n /**\n * BFS hop count from the starting node (neato/fdp).\n * @see lib/common/types.h:ND_hops\n */\n hops?: number;\n\n /**\n * N-dimensional position vector (length = ndim).\n * @see lib/common/types.h:ND_pos\n */\n pos?: number[];\n\n /**\n * Distance value used by neato stress majorization.\n * @see lib/common/types.h:ND_dist\n */\n dist?: number;\n\n // -------------------------------------------------------------------------\n // Engine algorithm data (AD-7)\n // -------------------------------------------------------------------------\n\n /**\n * Per-engine algorithm data. Each layout engine stores a different struct\n * in ND_alg; the `kind` discriminant identifies which engine's data is\n * active without unsafe casting (AD-7).\n *\n * @see lib/common/types.h:ND_alg\n */\n alg?: NodeAlgData;\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Constructs a zero-valued NodeInfo with all required fields set to their\n * zero/false initial state and all optional fields absent (undefined).\n *\n * Mirrors the effect of calloc-initializing an Agnodeinfo_t in C.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/cgraph/node.c:agnode (initial record allocation via agbindrec)\n */\nexport function makeNodeInfo(): NodeInfo {\n // C agbindrec calloc-zeroes the WHOLE Agnodeinfo_t struct, so every ND_*\n // field exists from creation at a fixed offset. The port mirrors that:\n // pre-declaring EVERY optional field (value-preserving `undefined` \u2014 same\n // semantics as absent under the `?? 0`/truthy accessors) pins a single V8\n // hidden class for all NodeInfo objects. Without this, fields added lazily\n // during layout (par/low/lim/rank/tree_in/tree_out/...) drove NodeInfo into\n // dictionary (slow) mode \u2014 the dominant cost of the network-simplex DFS hot\n // path on large graphs (~26x per-op vs C). No code reads node.info via\n // `in`/spread/Object.keys, so adding the keys is behavior-free (verified\n // identical across the full dot corpus). Keep in sync with NodeInfo.\n return {\n coord: { x: 0, y: 0 },\n width: 0,\n height: 0,\n bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },\n ht: 0,\n lw: 0,\n rw: 0,\n outline_width: 0,\n outline_height: 0,\n state: 0,\n gui_state: 0,\n clustnode: false,\n // --- lazily-assigned fields, pre-declared to lock the hidden class ---\n base_width: undefined, base_height: undefined, label: undefined, xlabel: undefined,\n shape: undefined, shape_info: undefined, rank: undefined, order: undefined,\n rankIsXCoord: undefined, mval: undefined, node_type: undefined, ranktype: undefined,\n weight_class: undefined, mark: undefined, onstack: undefined, has_port: undefined,\n showboxes: undefined, in: undefined, out: undefined, flat_in: undefined,\n flat_out: undefined, other: undefined, save_in: undefined, save_out: undefined,\n tree_in: undefined, tree_out: undefined, next: undefined, prev: undefined,\n par: undefined, low: undefined, lim: undefined, priority: undefined,\n UF_size: undefined, UF_parent: undefined, rep: undefined, set: undefined,\n clust: undefined, posAlg: undefined, pinned: undefined, id: undefined,\n heapindex: undefined, hops: undefined, pos: undefined, dist: undefined, alg: undefined,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agnode_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\n\nimport type { Edge } from './edge.js';\nimport type { Graph } from './graph.js';\nimport { type NodeInfo, makeNodeInfo } from './nodeInfo.js';\n\n/**\n * Represents an Agnode_t \u2014 a graph node.\n *\n * In the C implementation, Agnode_t embeds Agobj_t (tag + data), holds a\n * pointer to its root graph, and contains an embedded Agsubnode_t for the\n * main graph. Per-graph membership is tracked via Agsubnode_t records in\n * libcdt dicts. Here node identity is object identity (pointer comparison\n * in C maps to reference equality in TypeScript).\n *\n * The `info` field replaces the agbindrec / ND_* mechanism (AD-1).\n *\n * @see lib/cgraph/cgraph.h:Agnode_s\n * @see lib/cgraph/cgraph.h:Agsubnode_s\n */\nexport class Node {\n /**\n * Unique integer ID per root graph; maps to AGID in the C implementation.\n * @see lib/cgraph/cgraph.h:AGID\n */\n readonly id: number;\n\n /** Node name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */\n readonly name: string;\n\n /**\n * String attributes (agget/agset equivalents).\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /**\n * Layout-engine info; replaces ND_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: NodeInfo;\n\n /**\n * Root graph that owns this node.\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\n readonly root: Graph;\n\n /**\n * Frozen snapshot of the node-attribute defaults (`node [...]`) in effect \u2014\n * from the declaring subgraph up through its ancestors \u2014 at the moment this\n * node was first created. Captured at creation (not read live at render) so\n * that a `node [...]` declared *after* this node in the same or an ancestor\n * scope does not retroactively apply, matching DOT semantics. Present on\n * parser-built nodes (even when empty); undefined on programmatically created\n * nodes, which fall back to live graph defaults in `nodeAttr`.\n */\n nodeDefaultsSnapshot?: Map<string, string>;\n\n /** @see lib/cgraph/node.c:agnode */\n constructor(id: number, name: string, root: Graph) {\n this.id = id;\n this.name = name;\n this.attrs = new Map();\n this.info = makeNodeInfo();\n this.root = root;\n }\n\n /**\n * Returns out-edges of this node in graph g (where this node is the tail).\n *\n * ORDER: cgraph's per-node out-edge dict sorts by the SEQ of the edge's\n * out-half node \u2014 the HEAD \u2014 then by edge seq (edge.c:agedgeseqcmpf,\n * Ag_mainedge_seq_disc). Iteration is therefore by (head creation order,\n * edge creation order), NOT by plain edge insertion order. This ordering\n * is load-bearing for emission order and for force-accumulation order in\n * the iterative engines.\n *\n * INVARIANT: In the C cgraph library, the adjacency list for a node begins\n * with the node's own self-loop (index 0), and neighbor traversal MUST start\n * at index 1. This convention originates from lib/cgraph/edge.c and\n * agfstedge/agnxtedge. In this TypeScript implementation, self-loops are\n * included in outEdges() but are excluded from inEdges() \u2014 matching the\n * agnxtedge behavior that skips self-loops as in-edges. Callers iterating\n * over neighbors must account for self-loop edges appearing in outEdges().\n *\n * @see lib/cgraph/edge.c:agfstout\n * @see lib/cgraph/edge.c:agnxtout\n * @see lib/cgraph/edge.c:agedgeseqcmpf\n */\n outEdges(g: Graph): Edge[] {\n return g.edges\n .filter((e) => e.tail === this)\n .sort((a, b) => (a.head.id - b.head.id) || (a.seq - b.seq));\n }\n\n /**\n * Returns in-edges of this node in graph g (where this node is the head).\n * Self-loops are excluded per C agnxtedge semantics. Ordered by the SEQ\n * of the in-half node \u2014 the TAIL \u2014 then edge seq (agedgeseqcmpf).\n *\n * @see lib/cgraph/edge.c:agfstin\n * @see lib/cgraph/edge.c:agnxtin\n * @see lib/cgraph/edge.c:agnxtedge (skips self-loops as in-edges)\n * @see lib/cgraph/edge.c:agedgeseqcmpf\n */\n inEdges(g: Graph): Edge[] {\n return g.edges\n .filter((e) => e.head === this && e.tail !== this)\n .sort((a, b) => (a.tail.id - b.tail.id) || (a.seq - b.seq));\n }\n}\n\n/**\n * Build a tail\u2192out-edges index for graph `g`. Each list is identical to\n * `node.outEdges(g)` (tail === node, self-loops included, sorted by head.id then\n * seq), but the whole index is O(E log E) instead of O(N\u00B7E log E) from calling\n * outEdges once per node. Use at sites that loop every node's out-edges and do\n * not mutate `g.edges` during the loop. @see Node.outEdges\n */\nexport function buildOutEdgeIndex(g: Graph): Map<Node, Edge[]> {\n const m = new Map<Node, Edge[]>();\n for (const e of g.edges) {\n const l = m.get(e.tail);\n if (l !== undefined) l.push(e);\n else m.set(e.tail, [e]);\n }\n for (const l of m.values()) {\n l.sort((a, b) => (a.head.id - b.head.id) || (a.seq - b.seq));\n }\n return m;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Faithful TypeScript port of the cgraph subgraph/node operations needed by\n * later layout passes (e.g. dot's newrank fill_ranks, which creates a\n * `_new_rank` subgraph and adds anonymous placeholder nodes).\n *\n * Nodes are owned by the root graph and keyed by name in each graph's\n * `nodes` Map; subgraph membership is presence in that subgraph's `nodes`\n * Map. A node in a subgraph is a member of every enclosing graph up to and\n * including root (Subgraph Ownership Semantics).\n *\n * @see lib/cgraph/node.c\n * @see lib/cgraph/subg.c\n */\n\nimport type { Graph } from './graph.js';\nimport { Graph as GraphClass } from './graph.js';\nimport { Node } from './node.js';\n\n/**\n * The C anonymous-name prefix LOCALNAMEPREFIX ('%'); an object with no\n * external name has `agnameof` synthesize \"%<id>\".\n * @see lib/cgraph/cghdr.h:LOCALNAMEPREFIX\n * @see lib/cgraph/id.c:aginternalmapprint\n */\nconst LOCALNAMEPREFIX = '%';\n\n/**\n * Allocate a fresh node id unique within `root.nodes`. Mirrors C's\n * agnextseq/agmapnametoid id reservation: at layout time the per-parse\n * NodeRegistry counter is unavailable, so derive (max existing root id) + 1.\n * @see lib/cgraph/node.c:agnextseq\n */\nfunction freshNodeId(root: Graph): number {\n let max = -1;\n for (const n of root.nodes.values()) {\n if (n.id > max) max = n.id;\n }\n return max + 1;\n}\n\n/**\n * Create-or-get a node by name on the root graph. When `name` is null/empty,\n * mint a fresh anonymous node with a unique id and unique \"%<id>\" name. When\n * `create` is false and a named node is absent, returns null.\n *\n * Unlike the C agnode (which keys by id), this port keys nodes by name in\n * `Graph.nodes`; node creation always installs into the root graph.\n *\n * @see lib/cgraph/node.c:agnode\n */\nexport function agnode(\n g: Graph,\n name: string | null,\n create: boolean,\n): Node | null {\n const root = g.root;\n if (name !== null && name !== '') {\n const existing = root.nodes.get(name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const node = new Node(freshNodeId(root), name, root);\n root.nodes.set(name, node);\n return node;\n }\n if (!create) return null;\n const id = freshNodeId(root);\n const anonName = LOCALNAMEPREFIX + String(id);\n const node = new Node(id, anonName, root);\n root.nodes.set(anonName, node);\n return node;\n}\n\n/**\n * Assign a subgraph its AGSEQ from the root-level counter, mirroring\n * `agnextseq(par, AGRAPH)` in agopen: pre-increment `clos->seq[AGRAPH]` (stored\n * on the root as `subgSeqCounter`) and record it on the subgraph. Call once,\n * only when the subgraph is first created.\n *\n * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))\n * @see lib/cgraph/graph.c:agnextseq\n */\nexport function assignSubgSeq(parent: Graph, sg: Graph): void {\n sg.seq = ++parent.root.subgSeqCounter;\n}\n\n/**\n * Create-or-get a named subgraph under `parent`. On create, sets the new\n * graph's `parent`/`root`, assigns its AGSEQ, and registers it in\n * `parent.subgraphs`. Returns null when `create` is false and the subgraph is\n * absent.\n *\n * @see lib/cgraph/subg.c:agsubg\n */\nexport function agsubg(\n parent: Graph,\n name: string,\n create: boolean,\n): Graph | null {\n const existing = parent.subgraphs.get(name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const subg = new GraphClass(name, parent.kind);\n subg.parent = parent;\n subg.root = parent.root;\n assignSubgSeq(parent, subg);\n parent.subgraphs.set(name, subg);\n return subg;\n}\n\n/**\n * Lookup or insert node `n` in subgraph `g`, recursively installing it into\n * every enclosing graph up to and including root. Returns null if `n` does\n * not belong to `g`'s root. Mirrors the parser's enclosing-graph membership\n * loop (builder.ts processNodeStmt).\n *\n * @see lib/cgraph/node.c:agsubnode\n */\nexport function agsubnode(g: Graph, n: Node, create: boolean): Node | null {\n if (g.root !== n.root) return null;\n const existing = g.nodes.get(n.name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const par = g.parent;\n if (par !== null) {\n agsubnode(par, n, create);\n }\n g.nodes.set(n.name, n);\n return n;\n}\n\n/**\n * Remove node `n` from graph `g` and every enclosing graph up to and\n * including root (its member graphs). Mirrors agdelnode's image removal\n * applied across the graph and its ancestors.\n *\n * @see lib/cgraph/node.c:agdelnode\n */\nexport function agdelnode(g: Graph, n: Node): void {\n for (let cur: Graph | null = g; cur !== null; cur = cur.parent) {\n cur.nodes.delete(n.name);\n }\n}\n\n/**\n * Remove subgraph `sg` from `parent.subgraphs`. Mirrors agdelsubg, which\n * only deletes the entry in the parent's subgraph dict.\n *\n * @see lib/cgraph/subg.c:agdelsubg\n */\nexport function agdelsubg(parent: Graph, sg: Graph): void {\n parent.subgraphs.delete(sg.name);\n}\n\n/**\n * Faithful `agxget` for a graph attribute, mirroring C's\n * `late_string(g, agfindgraphattr(g, name), NULL)`:\n * - returns `g`'s explicit value if set;\n * - else, if `name` was declared graph-wide (set on any (sub)graph at parse\n * time \u2192 present in `root.declaredGraphAttrs`), returns the empty-string\n * default `\"\"` (cgraph's agsafeset default);\n * - else returns `undefined` (the attribute symbol does not exist).\n *\n * The `undefined` vs `\"\"` distinction is load-bearing: e.g. `ordered_edges`\n * treats a non-NULL (incl. empty) value as \"graph ordering present, do not\n * recurse into subgraphs\". A subgraph-scoped `ordering=out` therefore yields\n * `\"\"` at the root, so the root no-ops it exactly as C does.\n * @see lib/cgraph/attr.c:agxget, lib/common/utils.c:late_string\n */\nexport function agGraphAttr(g: Graph, name: string): string | undefined {\n const own = g.attrs.get(name);\n if (own !== undefined) return own;\n return g.root.declaredGraphAttrs.has(name) ? '' : undefined;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agedgeinfo_t from lib/common/types.h.\n *\n * All ED_* accessor macros in the C source expand to typed reads/writes into\n * this struct. In TypeScript the macro indirection is unnecessary \u2014 consumers\n * access fields directly on the EdgeInfo object attached to each Edge.\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/types.h:ED_* macros\n */\n\nimport type { Port, Spline } from './geom.js';\nimport type { Edge } from './edge.js';\nimport type { TextlabelT } from '../common/types.js';\nimport type { ArrowDrawOp } from '../common/arrows-types.js';\n\n/**\n * Edge layout and rendering info record; replaces the ED_* macro family.\n *\n * Required fields are always initialized when an edge is created (matching C\n * zero-init of the port struct in common_init_edge). All other fields are\n * optional; they are populated by specific phases of the layout pipeline.\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/utils.c:common_init_edge\n */\nexport interface EdgeInfo {\n /**\n * Tail-end port specification. Always initialized on edge creation.\n * @see lib/common/types.h:Agedgeinfo_t.tail_port\n * @see lib/common/types.h:ED_tail_port\n */\n tail_port: Port;\n\n /**\n * Head-end port specification. Always initialized on edge creation.\n * @see lib/common/types.h:Agedgeinfo_t.head_port\n * @see lib/common/types.h:ED_head_port\n */\n head_port: Port;\n\n /**\n * Rendered spline geometry. Undefined before the spline routing phase.\n * Set by clip_and_install() in lib/common/splines.c. Do not read before\n * the layout engine's spline pass has completed.\n *\n * @see lib/common/types.h:Agedgeinfo_t.spl\n * @see lib/common/types.h:ED_spl\n * @see lib/common/splines.c:clip_and_install\n */\n spl?: Spline;\n\n // -------------------------------------------------------------------------\n // Labels \u2014 AD6: typed as TextlabelT (was unknown until TextLabel was ported).\n // -------------------------------------------------------------------------\n\n /**\n * Center edge label.\n * @see lib/common/types.h:Agedgeinfo_t.label\n * @see lib/common/types.h:ED_label\n */\n label?: TextlabelT;\n\n /**\n * Head-end label (arrowhead side).\n * @see lib/common/types.h:Agedgeinfo_t.head_label\n * @see lib/common/types.h:ED_head_label\n */\n head_label?: TextlabelT;\n\n /**\n * Tail-end label (arrowtail side).\n * @see lib/common/types.h:Agedgeinfo_t.tail_label\n * @see lib/common/types.h:ED_tail_label\n */\n tail_label?: TextlabelT;\n\n /**\n * External label (placed outside the edge spline).\n * @see lib/common/types.h:Agedgeinfo_t.xlabel\n * @see lib/common/types.h:ED_xlabel\n */\n xlabel?: TextlabelT;\n\n // -------------------------------------------------------------------------\n // Arrowhead draw-ops (ADR-1). The C port regenerates arrowheads at render\n // time from the rotated spline endpoint; this TS port precomputes the typed\n // primitive list during routing (per arrowhead/arrowtail type) and rotates\n // it in postproc. Replaces the loose `_arrowPts`/`_tailArrowPts` Point[].\n // -------------------------------------------------------------------------\n\n /** Head-end arrow primitives (arrowhead). @see lib/common/arrows.c:arrow_gen */\n headArrowOps?: ArrowDrawOp[];\n\n /** Tail-end arrow primitives (arrowtail). @see lib/common/arrows.c:arrow_gen */\n tailArrowOps?: ArrowDrawOp[];\n\n // -------------------------------------------------------------------------\n // Edge metadata\n // -------------------------------------------------------------------------\n\n /**\n * Whether this edge participates in rank constraints.\n * Corresponds to the `constraint` edge attribute (default: true).\n * When false, the edge does not constrain rank assignment.\n * @see lib/dotgen/class1.c:nonconstraint_edge\n */\n constraint?: boolean;\n\n /**\n * Edge type code: REGULAREDGE / FLATEDGE / SELFEDGE.\n * Stored as char in C; mapped to number here.\n * @see lib/common/types.h:Agedgeinfo_t.edge_type\n * @see lib/common/types.h:ED_edge_type\n */\n edge_type?: number;\n\n /**\n * True for compound edges (cross-cluster).\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.compound\n * @see lib/common/types.h:ED_compound\n */\n compound?: number;\n\n /**\n * True for flat edges whose tail and head are adjacent in rank.\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.adjacent\n * @see lib/common/types.h:ED_adjacent\n */\n adjacent?: number;\n\n /**\n * Whether the edge label is placed above (on top of) the spline.\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.label_ontop\n * @see lib/common/types.h:ED_label_ontop\n */\n label_ontop?: number;\n\n /**\n * GUI state flags (bitwise OR of GUI_STATE_* constants).\n * Stored as unsigned char in C.\n * @see lib/common/types.h:Agedgeinfo_t.gui_state\n * @see lib/common/types.h:ED_gui_state\n * @see lib/common/types.h:GUI_STATE_ACTIVE / GUI_STATE_SELECTED / GUI_STATE_VISITED / GUI_STATE_DELETED\n */\n gui_state?: number;\n\n // -------------------------------------------------------------------------\n // Virtual edge back-pointers (dot layout)\n // -------------------------------------------------------------------------\n\n /**\n * Back-pointer to the original (non-virtual) edge; set on virtual edges\n * created during dot layout for long-range connections.\n * @see lib/common/types.h:Agedgeinfo_t.to_orig\n * @see lib/common/types.h:ED_to_orig\n * @see lib/common/shapes.c\n */\n to_orig?: Edge;\n\n /**\n * Back-pointer to the virtual edge that represents this edge in the\n * rank-spanning chain; set on real edges when a virtual chain exists.\n * @see lib/common/types.h:Agedgeinfo_t.to_virt\n * @see lib/common/types.h:ED_to_virt\n */\n to_virt?: Edge;\n\n /**\n * Routing attempted and routesplines failed: the edge is LOST exactly as in\n * C (no spline installed, map_edge warns, emit skips). Port-only bookkeeping\n * so the routeDotEdges backstop does not re-attempt an edge C routes once.\n * @see lib/common/routespl.c:routesplines (NULL), lib/common/postproc.c:map_edge\n */\n lost?: boolean;\n\n // -------------------------------------------------------------------------\n // Engine scratch\n // -------------------------------------------------------------------------\n\n /**\n * Layout-engine algorithm scratch pointer. Engine-specific; not typed here.\n * @see lib/common/types.h:Agedgeinfo_t.alg\n * @see lib/common/types.h:ED_alg\n */\n alg?: unknown;\n\n // -------------------------------------------------------------------------\n // dot-specific fields (#ifndef NEATO_ONLY in C)\n // -------------------------------------------------------------------------\n\n /**\n * Edge weight for network simplex. Higher weight means the edge length is\n * penalized more, biasing ranks toward short edges.\n * int in C.\n * @see lib/common/types.h:Agedgeinfo_t.weight\n * @see lib/common/types.h:ED_weight\n */\n weight?: number;\n\n /**\n * Minimum rank separation required between tail and head nodes.\n * int in C.\n * @see lib/common/types.h:Agedgeinfo_t.minlen\n * @see lib/common/types.h:ED_minlen\n */\n minlen?: number;\n\n /**\n * Network-simplex cut value; used during feasible-tree construction.\n * @see lib/common/types.h:Agedgeinfo_t.cutvalue\n * @see lib/common/types.h:ED_cutvalue\n */\n cutvalue?: number;\n\n /**\n * Index into the network-simplex spanning tree edge list.\n * @see lib/common/types.h:Agedgeinfo_t.tree_index\n * @see lib/common/types.h:ED_tree_index\n */\n tree_index?: number;\n\n /**\n * Crossing penalty multiplier for this edge.\n * short in C.\n * @see lib/common/types.h:Agedgeinfo_t.xpenalty\n * @see lib/common/types.h:ED_xpenalty\n */\n xpenalty?: number;\n\n /**\n * Multiplicity count \u2014 number of parallel edges merged into this one\n * during concentration.\n * short in C.\n * @see lib/common/types.h:Agedgeinfo_t.count\n * @see lib/common/types.h:ED_count\n */\n count?: number;\n\n /**\n * Concentrated-opposite flag; set when merging parallel edges from\n * opposing directions.\n * @see lib/common/types.h:Agedgeinfo_t.conc_opp_flag\n * @see lib/common/types.h:ED_conc_opp_flag\n */\n conc_opp_flag?: boolean;\n\n /**\n * Debug: render intermediate routing boxes.\n * unsigned char in C.\n * @see lib/common/types.h:Agedgeinfo_t.showboxes\n * @see lib/common/types.h:ED_showboxes\n */\n showboxes?: number;\n\n /**\n * True for edges created inside the flat-adjacent auxiliary clone graph\n * (make_flat_adj_edges). Models C's setState window: cloneGraph re-declares\n * the aux attribute dictionary via agnxtattr (NAME-sorted), so aux ids are\n * alphabetical ranks while the global symbols E_arrowsz/E_penwidth \u2014 which\n * setState does NOT remap \u2014 keep main-graph declaration-order ids. Inside\n * the aux, arrow_length's agxget(e, E_arrowsz) therefore mis-indexes into\n * an unrelated attribute (e.g. #1949: id 1 = `color` \u2192 \"black\"/\"blue\"),\n * strtod fails, and late_double falls back to 1.0 for both arrowsize and\n * penwidth. The arrow POLYGON is unaffected (C regenerates it at emit time\n * from the original edge). Load-bearing C quirk; consumed by the arrow\n * clip-length computation only.\n * @see lib/dotgen/dotsplines.c:setState (E_arrowsz/E_penwidth not remapped)\n * @see lib/common/arrows.c:arrow_length (late_double(e, E_arrowsz, 1.0, 0.0))\n */\n stale_arrow_attrs?: boolean;\n\n // -------------------------------------------------------------------------\n // neato/fdp-specific fields (#ifndef DOT_ONLY in C)\n // -------------------------------------------------------------------------\n\n /**\n * Spring factor (ideal edge length multiplier) for spring-model layout.\n * double in C.\n * @see lib/common/types.h:Agedgeinfo_t.factor\n * @see lib/common/types.h:ED_factor\n */\n factor?: number;\n\n /**\n * Ideal edge length for neato/fdp stress-model layout.\n * double in C.\n * @see lib/common/types.h:Agedgeinfo_t.dist\n * @see lib/common/types.h:ED_dist\n */\n dist?: number;\n\n /**\n * Path used during spline routing (Ppolyline_t in C).\n * Typed in Batch 4 when pathplan types are ported.\n * @see lib/common/types.h:Agedgeinfo_t.path\n * @see lib/common/types.h:ED_path\n */\n path?: unknown; // ED_path \u2014 Ppolyline_t in C\n\n\n // -------------------------------------------------------------------------\n // samehead / sametail \u2014 group id for port merging (sameport.c)\n // -------------------------------------------------------------------------\n\n /**\n * Group id for samehead port merging. Edges sharing this value at the same\n * head node will be merged onto a single port by dot_sameports().\n * Corresponds to agxget(e, E_samehead) in the C source.\n * @see lib/dotgen/sameport.c:dot_sameports\n */\n samehead?: string;\n\n /**\n * Group id for sametail port merging. Edges sharing this value at the same\n * tail node will be merged onto a single port by dot_sameports().\n * Corresponds to agxget(e, E_sametail) in the C source.\n * @see lib/dotgen/sameport.c:dot_sameports\n */\n sametail?: string;\n\n // -------------------------------------------------------------------------\n // Compound edge cluster clipping (dot layout)\n // -------------------------------------------------------------------------\n\n /**\n * Name of the cluster subgraph to clip the head of this edge against.\n * When set, the spline endpoint at the head is clipped to the cluster's\n * bounding box by dot_compoundEdges(). Corresponds to agget(e, \"lhead\").\n * @see lib/dotgen/compound.c:makeCompoundEdge\n */\n lhead?: string;\n\n /**\n * Name of the cluster subgraph to clip the tail of this edge against.\n * When set, the spline start at the tail is clipped to the cluster's\n * bounding box by dot_compoundEdges(). Corresponds to agget(e, \"ltail\").\n * @see lib/dotgen/compound.c:makeCompoundEdge\n */\n ltail?: string;\n\n // -------------------------------------------------------------------------\n // dot layout tracking (TypeScript-only, no C equivalent field)\n // -------------------------------------------------------------------------\n\n /**\n * True if this edge was reversed by acyclic() during cycle-breaking.\n * Set by reverseEdge() in src/layout/dot/fastgr.ts. Used by dot_splines\n * to swap bezier control-point order back to tail\u2192head orientation.\n *\n * There is no corresponding C field; C detects reversal via rank comparison\n * in swap_ends_p() (lib/dotgen/dotsplines.c). The TypeScript port adds this\n * field for explicit tracking.\n *\n * @see lib/dotgen/dotsplines.c:swap_ends_p\n */\n reversed?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a zero-initialized Port matching C's zero-init of the port struct\n * in common_init_edge.\n *\n * @see lib/common/types.h:struct port\n * @see lib/common/utils.c:common_init_edge\n */\nexport function makePort(): Port {\n return {\n p: { x: 0, y: 0 },\n theta: 0,\n bp: null,\n defined: false,\n constrained: false,\n // C's default Center port has clip = true; edges clip to the node\n // boundary unless head/tailclip disables it.\n // @see lib/common/shapes.c:Center\n clip: true,\n dyna: false,\n order: 0,\n side: 0,\n name: null,\n };\n}\n\n/**\n * Constructs a minimal EdgeInfo with required port fields initialized.\n *\n * Both ports are required \u2014 tail_port and head_port are always initialized\n * when an edge is created (never null in the C model).\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/utils.c:common_init_edge\n */\nexport function makeEdgeInfo(tailPort: Port, headPort: Port): EdgeInfo {\n // C agbindrec calloc-zeroes the WHOLE Agedgeinfo_t struct, so every ED_*\n // field exists from creation at a fixed offset. Mirror that (same treatment\n // as makeNodeInfo): pre-declare EVERY optional field as value-preserving\n // `undefined` to pin a single V8 hidden class for all EdgeInfo objects.\n // Without this, fields added lazily during layout (weight/minlen/cutvalue/\n // tree_index/edge_type/to_virt/...) drove EdgeInfo into dictionary (slow)\n // mode \u2014 the dominant per-op cost of the network-simplex enter-edge scan\n // (dfsEnterInedge ~52% of 2371's profile), which reads tree_index, cutvalue\n // and minlen per candidate edge. No code reads edge.info via `in`/\n // Object.keys/for-in; the `{...e.info}` spreads copy identical key sets, so\n // adding the keys is behavior-free. Keep in sync with EdgeInfo.\n return {\n tail_port: tailPort,\n head_port: headPort,\n // --- lazily-assigned fields, pre-declared to lock the hidden class ---\n spl: undefined, label: undefined, head_label: undefined, tail_label: undefined,\n xlabel: undefined, headArrowOps: undefined, tailArrowOps: undefined,\n constraint: undefined, edge_type: undefined, compound: undefined,\n adjacent: undefined, label_ontop: undefined, gui_state: undefined,\n to_orig: undefined, to_virt: undefined, lost: undefined, alg: undefined,\n weight: undefined, minlen: undefined, cutvalue: undefined, tree_index: undefined,\n xpenalty: undefined, count: undefined, conc_opp_flag: undefined,\n showboxes: undefined, stale_arrow_attrs: undefined,\n factor: undefined, dist: undefined, path: undefined,\n samehead: undefined, sametail: undefined, lhead: undefined, ltail: undefined,\n reversed: undefined,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agedge_t / Agedgepair_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agedge_s\n * @see lib/cgraph/cgraph.h:Agedgepair_s\n */\n\nimport type { Node } from './node.js';\nimport { type EdgeInfo, makeEdgeInfo, makePort } from './edgeInfo.js';\n\n/**\n * Represents an abstract edge in the graph, corresponding to Agedgepair_t.\n *\n * IMPORTANT: In Agedgepair_t, the \"out\" half stores the HEAD node and the\n * \"in\" half stores the TAIL node. This is counterintuitive but matches C:\n * out.node == head, in.node == tail\n * See lib/cgraph/cgraph.h Agedgepair_t and the AGTAIL/AGHEAD macro definitions.\n *\n * From cgraph.md \"Edge Direction Semantics\":\n * - tail: source node (arrow comes FROM); AGTAIL(e) = AGMKIN(e)->node = in.node\n * - head: destination node (arrow goes TO); AGHEAD(e) = AGMKOUT(e)->node = out.node\n * - out-edge: stored in tail node's out-edge set; out.node == head\n * - in-edge: stored in head node's in-edge set; in.node == tail\n *\n * The `info` field replaces the agbindrec / ED_* mechanism (AD-1).\n *\n * @see lib/cgraph/cgraph.h:Agedgepair_s\n * @see lib/cgraph/cgraph.h:AGTAIL\n * @see lib/cgraph/cgraph.h:AGHEAD\n * @see lib/cgraph/cgraph.h:AGMKIN\n * @see lib/cgraph/cgraph.h:AGMKOUT\n */\nexport class Edge {\n // Code review: process-wide static seq counter, never reset between renders\n // (C's AGSEQ lives in a per-agopen Agclos_t). Safe today \u2014 every reader only\n // compares .seq values within one graph's own edges for relative ordering \u2014\n // and it evades the module-globals fitness scan (a private static field, not\n // a module-scope `let`). Revisit if any consumer serializes an absolute .seq\n // or relies on the counter resetting per parse/createGraph.\n /** @see lib/cgraph/cgraph.h:AGSEQ */\n private static _nextSeq = 0;\n\n /**\n * Monotone sequence number \u2014 equivalent to AGSEQ(e) in C.\n * Used by edgeidcmpf for stable sort ordering.\n *\n * Mutable because C's new_virtual_edge overwrites a virtual edge's AGSEQ\n * with AGSEQ(orig) after allocation; copyVirtualEdgeInfo mirrors that.\n * @see lib/cgraph/cgraph.h:AGSEQ, lib/dotgen/fastgr.c:new_virtual_edge\n */\n seq: number;\n\n /**\n * Per-graph creation-order index (1-based).\n * Set by the parser after root.edges.push(). Used as the SVG edge id,\n * matching C's AGSEQ-based edge id assignment.\n */\n graphSeq: number = 0;\n\n /**\n * Source node (arrow origin).\n * C: AGTAIL(e) = AGMKIN(e)->node = in.node\n * @see lib/cgraph/cgraph.h:AGTAIL\n */\n readonly tail: Node;\n\n /**\n * Destination node (arrow target).\n * C: AGHEAD(e) = AGMKOUT(e)->node = out.node\n * @see lib/cgraph/cgraph.h:AGHEAD\n */\n readonly head: Node;\n\n /**\n * Edge key; empty string for anonymous edges.\n * Corresponds to the `name` argument of agedge().\n * @see lib/cgraph/cgraph.h:agedge\n */\n readonly name: string;\n\n /**\n * String attributes (agget/agset equivalents).\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /**\n * Layout-engine info; replaces ED_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: EdgeInfo;\n\n /** @see lib/cgraph/edge.c:agedge */\n constructor(tail: Node, head: Node, name: string) {\n this.seq = Edge._nextSeq++;\n this.tail = tail;\n this.head = head;\n this.name = name;\n this.attrs = new Map();\n this.info = makeEdgeInfo(makePort(), makePort());\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Safe edge-creation helper \u2014 the `agedge` equivalent for programmatic graph\n * construction. Mirrors the parser's dual-list insertion and adds the\n * strict-graph deduplication mandated by cgraph.\n *\n * @see lib/cgraph/edge.c:agedge\n */\n\nimport type { Graph } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport { Edge } from '../model/edge.js';\n\n/** @see lib/cgraph/cgraph.h:agisundirected */\nfunction isUndirected(g: Graph): boolean {\n const k = g.root.kind;\n return k === 'undirected' || k === 'strict-undirected';\n}\n\n/** @see lib/cgraph/cgraph.h:agisstrict */\nfunction isStrict(g: Graph): boolean {\n const k = g.root.kind;\n return k === 'strict-directed' || k === 'strict-undirected';\n}\n\n/**\n * Probe root.edges for an existing (tail,head) pair.\n * For undirected graphs, (h,t) matches (t,h) \u2014 mirrors the C wildcard\n * key probe in ok_to_make_edge / agedge pre-creation check.\n * @see lib/cgraph/edge.c:ok_to_make_edge\n */\nfunction findExistingEdge(\n root: Graph,\n tail: Node,\n head: Node,\n undirected: boolean,\n): Edge | null {\n for (const e of root.edges) {\n if (e.tail === tail && e.head === head) return e;\n if (undirected && e.tail === head && e.head === tail) return e;\n }\n return null;\n}\n\n/**\n * Insert edge into root.edges and walk the subgraph chain toward root,\n * adding the edge (and its endpoint nodes) to every enclosing graph.\n * Mirrors processEdgePair's enclosing-graph loop and installedge.\n * @see lib/cgraph/edge.c:installedge\n * @see src/parser/builder.ts:238-246\n */\nfunction insertEdge(g: Graph, root: Graph, edge: Edge): void {\n root.edges.push(edge);\n edge.graphSeq = root.edges.length;\n for (let cur: Graph | null = g; cur !== null && cur !== root; cur = cur.parent) {\n cur.nodes.set(edge.tail.name, edge.tail);\n cur.nodes.set(edge.head.name, edge.head);\n cur.edges.push(edge);\n }\n}\n\n/**\n * Create and insert an edge from `tail` to `head` in graph `g`.\n *\n * In a strict graph, at most one edge between any (tail, head) pair exists\n * (symmetric for undirected). When a match is found the existing edge is\n * returned without modification. This matches C `agedge` with `cflag=1`.\n *\n * Most callers building a graph programmatically should prefer\n * `createGraph().addEdge(...)` (the `GvGraphBuilder` method), which accepts\n * node names or handles. Use this lower-level `addEdge` when you already\n * hold `Graph`/`Node` references \u2014 e.g. edges added onto a graph returned by\n * `parse()`.\n *\n * @param g - Owning graph or subgraph; root derived via `g.root`.\n * @param tail - Source node (AGTAIL). @see lib/cgraph/cgraph.h:AGTAIL\n * @param head - Destination node (AGHEAD). @see lib/cgraph/cgraph.h:AGHEAD\n * @param name - Edge key; defaults to empty string for anonymous edges.\n * Ignored for strict-graph dedup (wildcard match).\n * @returns The new (or existing, for strict graphs) edge.\n *\n * @example\n * ```ts\n * import { parse, addEdge } from '@knowvah/dot-engine';\n *\n * const g = parse('digraph { a; b; }');\n * const a = g.nodes.get('a')!;\n * const b = g.nodes.get('b')!;\n * const edge = addEdge(g, a, b, 'ab1');\n * // edge.tail === a, edge.head === b\n * ```\n *\n * @see lib/cgraph/edge.c:agedge\n */\nexport function addEdge(\n g: Graph,\n tail: Node,\n head: Node,\n name?: string,\n): Edge {\n const root = g.root;\n const undirected = isUndirected(g);\n\n if (isStrict(g)) {\n const existing = findExistingEdge(root, tail, head, undirected);\n if (existing !== null) return existing;\n }\n\n const edge = new Edge(tail, head, name ?? '');\n insertEdge(g, root, edge);\n return edge;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Public structured-error contract for @knowvah/dot-engine.\n *\n * Consumers branch on the stable `code` / `type` fields; the\n * `code -> friendlyMessage` map is the single seam a future i18n library\n * replaces. This module is a runtime leaf: the only project import is the\n * type-only `Expectation`, which is erased at compile time.\n *\n * @see plans/structured-errors/decisions.md\n */\n\nimport type { Expectation } from './parser/dot.js';\n\n/** Stable public alias of peggy's expectation union (SYNTAX_* errors only). */\nexport type GvExpectation = Expectation;\n\n/** Coarse classification of where an error originated. */\nexport type GvErrorType = 'syntax' | 'semantic' | 'render';\n\n/** Closed union of stable error codes \u2014 each is an i18n key. */\nexport type GvErrorCode =\n | 'SYNTAX_ERROR' // peggy parse failure (token found)\n | 'SYNTAX_UNEXPECTED_EOF' // peggy parse failure, found === null\n | 'EDGE_OP_DIRECTED_IN_UNDIRECTED' // '->' used in an undirected graph\n | 'EDGE_OP_UNDIRECTED_IN_DIRECTED' // '--' used in a digraph\n | 'HTML_PARSE_ERROR' // HTML-like label parse failure\n | 'RENDER_ERROR' // known layout/render-stage failure\n | 'GENERIC_ERROR'; // catch-all fallback\n\n/** Structured error contract shared by every error source. */\nexport interface GvError {\n type: GvErrorType;\n /** Stable i18n key. */\n code: GvErrorCode;\n /** Concise technical text we own (may diverge from C). */\n message: string;\n /** Approachable, non-localized English (delivery). */\n friendlyMessage: string;\n /** Real error position; the highest-value field. */\n location?: { line: number; column: number; offset?: number };\n /** Peggy's discriminated union, passed through unmapped; SYNTAX_* only. */\n expected?: GvExpectation[];\n}\n\n/** Result of a result-style render: `svg` XOR `errors` for v1. */\nexport interface RenderResult {\n /** Present on success. */\n svg?: string;\n /** Present on failure; length <= 1 (first failure only) for v1. */\n errors?: GvError[];\n}\n\n/**\n * Central `code -> friendlyMessage` map. Non-localized, approachable English.\n * This is the seam a future i18n library replaces.\n */\nexport const FRIENDLY_MESSAGES: Record<GvErrorCode, string> = {\n SYNTAX_ERROR: 'There is a syntax error in the DOT source.',\n SYNTAX_UNEXPECTED_EOF:\n 'The DOT source ended unexpectedly \u2014 a bracket or statement may be unclosed.',\n EDGE_OP_DIRECTED_IN_UNDIRECTED:\n \"A directed edge '->' was used in an undirected graph; use '--' instead.\",\n EDGE_OP_UNDIRECTED_IN_DIRECTED:\n \"An undirected edge '--' was used in a directed graph; use '->' instead.\",\n HTML_PARSE_ERROR: 'An HTML-like label could not be parsed.',\n RENDER_ERROR: 'The graph could not be laid out or rendered.',\n GENERIC_ERROR: 'An unexpected error occurred while rendering the graph.',\n};\n\n/**\n * Look up the approachable English message for a code. The single seam a\n * future i18n library replaces.\n */\nexport function friendlyMessageFor(code: GvErrorCode): string {\n return FRIENDLY_MESSAGES[code];\n}\n\n/**\n * Error thrown for known layout/render-stage failures. Only `RENDER_ERROR`\n * and `GENERIC_ERROR` are valid render-stage codes.\n */\nexport class RenderError extends Error implements GvError {\n readonly type = 'render';\n readonly code: GvErrorCode;\n readonly friendlyMessage: string;\n\n constructor(message: string, code: GvErrorCode = 'RENDER_ERROR') {\n super(message);\n this.name = 'RenderError';\n this.code = code;\n this.friendlyMessage = friendlyMessageFor(code);\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * HTML-string marking. cgraph tags strings created from `label=<...>` via\n * a flag on the refstr (aghtmlstr); this port marks the attribute value\n * with a leading control character that cannot appear in DOT input.\n *\n * @see lib/cgraph/refstr.c:aghtmlstr\n */\n\n/** Marker prefix identifying an attribute value parsed from `<...>`. */\nexport const HTML_STRING_MARK = '\\u0001';\n\n/** True when the attribute value came from an HTML `<...>` literal. */\nexport function isHtmlValue(s: string): boolean {\n return s.startsWith(HTML_STRING_MARK);\n}\n\n/** Strip the HTML marker, returning the markup content. */\nexport function htmlValueContent(s: string): string {\n return s.slice(HTML_STRING_MARK.length);\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Programmatic graph builder \u2014 constructs a Graph model without emitting DOT.\n *\n * Exposes lightweight typed handles (GvNode, GvEdge) that wrap internal\n * model references without leaking the mutable model classes (ADR-1, ADR-6,\n * ADR-8).\n *\n * @see lib/cgraph/graph.c:agopen\n * @see lib/cgraph/node.c:agnode\n * @see lib/cgraph/subg.c:agsubg\n */\n\nimport { Graph, type GraphKind } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport type { Edge } from '../model/edge.js';\nimport { agnode, agsubg, agsubnode } from '../model/cgraph-ops.js';\nimport { addEdge as cgraphAddEdge } from './edge-ops.js';\nimport { RenderError } from '../errors.js';\nimport { HTML_STRING_MARK } from '../common/html-string.js';\n\n// \u2500\u2500 Public interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Options for createGraph. */\nexport interface CreateGraphOptions {\n directed?: boolean;\n strict?: boolean;\n name?: string;\n}\n\n/**\n * Opaque handle for a graph node.\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\nexport interface GvNode {\n readonly name: string;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute to an HTML-string value (e.g. `label` to an HTML-table\n * label). Equivalent to `label=<...>` in DOT text: the value is tagged as\n * HTML so the layout engine measures it as markup, not as literal text.\n * @see lib/cgraph/refstr.c:aghtmlstr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n}\n\n/**\n * Opaque handle for a graph edge.\n * @see lib/cgraph/cgraph.h:Agedge_s\n */\nexport interface GvEdge {\n readonly tail: string;\n readonly head: string;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute to an HTML-string value (e.g. an HTML-table edge label).\n * @see GvNode.setHtmlAttr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n}\n\n/**\n * Builder returned by createGraph. Provides idiomatic programmatic\n * construction of a graph model.\n */\nexport interface GvGraphBuilder {\n addNode(name: string, attrs?: Record<string, string>): GvNode;\n addEdge(\n tail: GvNode | string,\n head: GvNode | string,\n attrs?: Record<string, string>,\n ): GvEdge;\n addSubgraph(name: string, attrs?: Record<string, string>): GvGraphBuilder;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute on this (sub)graph to an HTML-string value \u2014 the\n * supported way to give a cluster an HTML-table `label` (the header-size\n * reservation the jar drives) without hand-writing the internal marker.\n * @see GvNode.setHtmlAttr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n readonly graph: Graph;\n}\n\n// \u2500\u2500 Internal handle implementations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Internal node handle \u2014 wraps Node ref without leaking the class. */\nclass NodeHandle implements GvNode {\n /** @internal */ readonly _node: Node;\n\n constructor(node: Node) {\n this._node = node;\n }\n\n get name(): string {\n return this._node.name;\n }\n\n setAttr(k: string, v: string): void {\n this._node.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._node.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._node.attrs.get(k);\n }\n}\n\n/** Internal edge handle \u2014 wraps Edge ref without leaking the class. */\nclass EdgeHandle implements GvEdge {\n /** @internal */ readonly _edge: Edge;\n\n constructor(edge: Edge) {\n this._edge = edge;\n }\n\n get tail(): string {\n return this._edge.tail.name;\n }\n\n get head(): string {\n return this._edge.head.name;\n }\n\n setAttr(k: string, v: string): void {\n this._edge.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._edge.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._edge.attrs.get(k);\n }\n}\n\n// \u2500\u2500 Builder implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Resolve a GvNode handle or a name string to an internal Node. */\nfunction resolveNode(g: Graph, ref: GvNode | string): Node {\n if (typeof ref === 'string') {\n const node = agnode(g, ref, true);\n if (node === null) {\n throw new RenderError(`Failed to resolve node '${ref}' in graph '${g.name}'`, 'GENERIC_ERROR');\n }\n return node;\n }\n return (ref as NodeHandle)._node;\n}\n\n/** Apply an attrs Record to a Map. */\nfunction applyAttrs(\n map: Map<string, string>,\n attrs: Record<string, string> | undefined,\n): void {\n if (attrs === undefined) return;\n for (const [k, v] of Object.entries(attrs)) {\n map.set(k, v);\n }\n}\n\n/**\n * Builder backed by a Graph + optional subgraph context.\n * Root builder: _context === _graph (the root Graph).\n * Subgraph builder: _context is the subgraph; _graph is always the root.\n */\nclass GraphBuilder implements GvGraphBuilder {\n private readonly _graph: Graph;\n private readonly _context: Graph;\n\n constructor(graph: Graph, context: Graph) {\n this._graph = graph;\n this._context = context;\n }\n\n get graph(): Graph {\n return this._graph;\n }\n\n addNode(name: string, attrs?: Record<string, string>): GvNode {\n return addNodeToContext(this._graph, this._context, name, attrs);\n }\n\n addEdge(\n tail: GvNode | string,\n head: GvNode | string,\n attrs?: Record<string, string>,\n ): GvEdge {\n const tailNode = resolveNode(this._graph, tail);\n const headNode = resolveNode(this._graph, head);\n const edge = cgraphAddEdge(this._graph, tailNode, headNode);\n applyAttrs(edge.attrs, attrs);\n return new EdgeHandle(edge);\n }\n\n addSubgraph(name: string, attrs?: Record<string, string>): GvGraphBuilder {\n const sg = agsubg(this._context, name, true);\n if (sg === null) {\n throw new RenderError(`Failed to create subgraph '${name}'`, 'GENERIC_ERROR');\n }\n applyAttrs(sg.attrs, attrs);\n return new GraphBuilder(this._graph, sg);\n }\n\n setAttr(k: string, v: string): void {\n this._context.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._context.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._context.attrs.get(k);\n }\n}\n\n/**\n * Create a node in root and, when context is a subgraph, install it there too.\n * Extracted to keep GraphBuilder.addNode below the CCN/length threshold.\n */\nfunction addNodeToContext(\n root: Graph,\n context: Graph,\n name: string,\n attrs: Record<string, string> | undefined,\n): GvNode {\n const node = agnode(root, name, true);\n if (node === null) throw new RenderError(`Failed to create node '${name}'`, 'GENERIC_ERROR');\n applyAttrs(node.attrs, attrs);\n if (context !== root) agsubnode(context, node, true);\n return new NodeHandle(node);\n}\n\n// \u2500\u2500 GraphKind mapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Lookup table: [directed][strict] \u2192 GraphKind.\n * Per parser/builder.ts:272-274 (the canonical mapping in this codebase).\n * @see lib/cgraph/graph.c:Agdirected / Agstrictdirected / Agundirected / Agstrictundirected\n */\nconst KIND_TABLE: Record<string, GraphKind> = {\n 'true:true': 'strict-directed',\n 'true:false': 'directed',\n 'false:true': 'strict-undirected',\n 'false:false': 'undirected',\n};\n\n/** Derive GraphKind from CreateGraphOptions. */\nfunction resolveKind(opts: CreateGraphOptions | undefined): GraphKind {\n const d = String(opts?.directed ?? true);\n const s = String(opts?.strict ?? false);\n return KIND_TABLE[`${d}:${s}`] ?? 'directed';\n}\n\n// \u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Create a new programmatic graph builder.\n *\n * The builder's `.graph` is a fresh Graph ready for handoff to layout/render.\n * Defaults: directed=true, strict=false, name=''.\n *\n * @see lib/cgraph/graph.c:agopen\n */\nexport function createGraph(opts?: CreateGraphOptions): GvGraphBuilder {\n const g = new Graph(opts?.name ?? '', resolveKind(opts));\n return new GraphBuilder(g, g);\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Geometry snapshot API \u2014 reads computed layout geometry from an\n * internal Graph after ctx.layout() has run, and returns a plain,\n * JSON-serializable snapshot.\n *\n * Coordinate systems\n * ------------------\n * Native graphviz coordinates are y-up (origin at lower-left).\n * Most screen consumers want y-down (origin at upper-left).\n *\n * With the default `yAxis: 'down'`, every y coordinate is flipped:\n * y' = bbHeight - y\n * where bbHeight = graph bb.ur.y - graph bb.ll.y.\n * The `bounds` origin is normalised to (0, 0) at the top-left corner.\n *\n * With `yAxis: 'up'`, values are returned unchanged (native y-up frame).\n *\n * Units\n * -----\n * The internal model stores node `width` and `height` in inches\n * (matching C ND_width / ND_height in lib/common/types.h).\n * `NodeGeometry.width` and `NodeGeometry.height` are converted to\n * **points** (1 inch = 72 points) before being returned.\n *\n * All other coordinates (x, y, bbox dimensions, spline points,\n * label positions) are in the native graphviz point unit.\n *\n * @see lib/common/types.h\n */\n\nimport type { Graph } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport type { Edge } from '../model/edge.js';\nimport { RenderError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// Public coordinate types (canonical home \u2014 T5 imports GeometryOptions here)\n// ---------------------------------------------------------------------------\n\n/** Coordinate system for returned geometry. */\nexport type YAxis = 'up' | 'down';\n\n/**\n * Options for {@link getLayout}.\n *\n * @property yAxis - Coordinate direction. Default `'down'` (origin top-left,\n * y increases downward \u2014 screen convention). Use `'up'` to get native\n * graphviz coordinates (origin bottom-left, y increases upward).\n */\nexport type GeometryOptions = { yAxis?: YAxis };\n\n/**\n * Overall bounding box of the graph, in points.\n *\n * With `yAxis:'down'`, x and y are 0 (normalised to top-left origin)\n * and width/height are the natural dimensions.\n * With `yAxis:'up'`, x and y match the raw lower-left corner of the\n * graph bounding box.\n */\nexport interface BoundsGeometry {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Per-node geometry extracted after layout, in points.\n *\n * `x` and `y` are the node centre coordinates.\n * `width` and `height` are in **points** (converted from the inches\n * stored on the model: `NodeInfo.width * 72`, `NodeInfo.height * 72`).\n *\n * @see lib/common/types.h:ND_coord, ND_width, ND_height\n */\nexport interface NodeGeometry {\n name: string;\n x: number;\n y: number;\n /**\n * Node width in **points** (model stores inches; multiplied by 72 here).\n * @see lib/common/types.h:ND_width\n */\n width: number;\n /**\n * Node height in **points** (model stores inches; multiplied by 72 here).\n * @see lib/common/types.h:ND_height\n */\n height: number;\n}\n\n/**\n * Per-edge geometry extracted after spline routing, in points.\n *\n * `points` concatenates all bezier control points from the edge spline,\n * in order. An edge with no routed spline produces an empty `points` array.\n * `label` is present only when the edge carries a centre label.\n *\n * @see lib/common/types.h:ED_spl, ED_label\n */\nexport interface EdgeGeometry {\n tail: string;\n head: string;\n /** Bezier control points for the edge spline, in points. */\n points: { x: number; y: number }[];\n /** Centre edge label position, if present. @see lib/common/types.h:ED_label */\n label?: { x: number; y: number };\n}\n\n/**\n * Per-cluster geometry extracted after layout, in points.\n *\n * `name` is the cluster subgraph's name (e.g. `cluster6`); for nested\n * clusters the name encodes the hierarchy, so no explicit parent link is\n * exposed. `x`/`y`/`width`/`height` describe the cluster's bounding box,\n * following the same frame convention as {@link BoundsGeometry}: with\n * `yAxis:'down'` (x, y) is the top-left corner; with `yAxis:'up'` (x, y) is\n * the lower-left corner (native graphviz frame). These are the raw box\n * corners graphviz computed \u2014 the same values `render()` rounds to emit the\n * `class=\"cluster\"` polygon, so a consumer quantizing to SVG precision gets\n * byte-conformant geometry.\n *\n * @see lib/common/types.h:GD_bb (of a cluster subgraph)\n */\nexport interface ClusterGeometry {\n /** Cluster subgraph name (e.g. `cluster6`); encodes nesting. */\n name: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Plain, JSON-serializable snapshot of the graph's computed geometry.\n *\n * `clusters` lists every cluster subgraph (recursively, nested clusters each\n * get their own entry) with a computed bounding box; it is empty for graphs\n * without clusters.\n *\n * @see lib/common/types.h:GD_bb, ND_coord, ED_spl, GD_clust\n */\nexport interface LayoutSnapshot {\n bounds: BoundsGeometry;\n nodes: NodeGeometry[];\n edges: EdgeGeometry[];\n clusters: ClusterGeometry[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants\n// ---------------------------------------------------------------------------\n\n/** Points per inch \u2014 matches graphviz's DPI constant. @see lib/common/geom.h */\nconst INCHES_TO_POINTS = 72;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Build a y-flip function bound to a specific graph bounding-box height. */\nfunction makeFlipY(bbHeight: number, yAxis: YAxis): (y: number) => number {\n if (yAxis === 'up') return (y) => y;\n return (y) => bbHeight - y;\n}\n\n/**\n * Snapshot one node's geometry.\n * @see lib/common/types.h:ND_coord, ND_width, ND_height\n */\nfunction snapshotNode(node: Node, flipY: (y: number) => number): NodeGeometry {\n const coord = node.info.coord;\n return {\n name: node.name,\n x: coord.x,\n y: flipY(coord.y),\n width: node.info.width * INCHES_TO_POINTS,\n height: node.info.height * INCHES_TO_POINTS,\n };\n}\n\n/**\n * Collect bezier control points from an edge's spline.\n * Uses `.size` (not `.list.length`) because C over-allocates `list`\n * and `size` holds the actual count after clip_and_install.\n * @see lib/common/splines.c:clip_and_install\n */\nfunction collectEdgePoints(\n edge: Edge,\n flipY: (y: number) => number,\n): { x: number; y: number }[] {\n const spl = edge.info.spl;\n if (spl === undefined) return [];\n const pts: { x: number; y: number }[] = [];\n for (const bz of spl.list) {\n for (let k = 0; k < bz.size; k++) {\n const pt = bz.list[k];\n pts.push({ x: pt.x, y: flipY(pt.y) });\n }\n }\n return pts;\n}\n\n/**\n * Snapshot one edge's geometry.\n * @see lib/common/types.h:ED_spl, ED_label (textlabel_t.pos)\n */\nfunction snapshotEdge(edge: Edge, flipY: (y: number) => number): EdgeGeometry {\n const geom: EdgeGeometry = {\n tail: edge.tail.name,\n head: edge.head.name,\n points: collectEdgePoints(edge, flipY),\n };\n const lbl = edge.info.label;\n if (lbl !== undefined) {\n geom.label = { x: lbl.pos.x, y: flipY(lbl.pos.y) };\n }\n return geom;\n}\n\n/**\n * Snapshot one cluster's bounding box, in the requested frame.\n *\n * Mirrors the {@link BoundsGeometry} convention: `yAxis:'up'` returns the\n * native lower-left corner (ll); `yAxis:'down'` returns the top-left corner\n * (ll.x, flipped ur.y). `width`/`height` are frame-independent (ur - ll).\n *\n * @see lib/common/types.h:GD_bb (cluster subgraph)\n */\nfunction snapshotCluster(\n sg: Graph, yAxis: YAxis, flipY: (y: number) => number,\n): ClusterGeometry {\n const bb = sg.info.bb;\n return {\n name: sg.name,\n x: bb.ll.x,\n // 'up' keeps the lower-left y; 'down' flips the upper-right y to the\n // top-left of the box (flipY is monotonic-decreasing there).\n y: yAxis === 'up' ? bb.ll.y : flipY(bb.ur.y),\n width: bb.ur.x - bb.ll.x,\n height: bb.ur.y - bb.ll.y,\n };\n}\n\n/**\n * Collect every cluster subgraph (depth-first, nested clusters included).\n * C stores clusters 1-indexed in GD_clust; the TS model exposes a 0-indexed\n * `info.clust` array on each (sub)graph. A cluster without a computed bb\n * (never laid out) is skipped.\n * @see lib/common/types.h:GD_clust, GD_n_cluster\n */\nfunction collectClusters(\n sg: Graph, yAxis: YAxis, flipY: (y: number) => number,\n out: ClusterGeometry[],\n): void {\n for (const c of sg.info.clust ?? []) {\n if (c.info.bb !== undefined) out.push(snapshotCluster(c, yAxis, flipY));\n collectClusters(c, yAxis, flipY, out);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a plain, JSON-serializable snapshot of the computed geometry for\n * all nodes and edges in graph `g`.\n *\n * Must be called **after** `ctx.layout(g, engine)` (or `render`) has run.\n * Before layout the geometry fields hold calloc-zero defaults (every node at\n * the origin, an empty bounding box), so a not-yet-laid-out graph is rejected\n * with a `RenderError` rather than returning that all-zero snapshot as if it\n * were real geometry.\n *\n * @param g - Laid-out graph (internal model; not mutated by this function).\n * @param opts - Coordinate options; defaults to `{ yAxis: 'down' }`.\n * @throws RenderError if `g` has not been laid out.\n *\n * @see lib/common/types.h:GD_bb, ND_coord, ED_spl\n */\nexport function getLayout(g: Graph, opts?: GeometryOptions): LayoutSnapshot {\n if (g.info?.laidOut !== true) {\n throw new RenderError(\n 'getLayout requires a laid-out graph; run ctx.layout(g, engine) or render() first',\n 'GENERIC_ERROR',\n );\n }\n const yAxis: YAxis = opts?.yAxis ?? 'down';\n const bb = g.info.bb;\n const bbWidth = bb.ur.x - bb.ll.x;\n const bbHeight = bb.ur.y - bb.ll.y;\n const flipY = makeFlipY(bbHeight, yAxis);\n\n const bounds: BoundsGeometry = yAxis === 'down'\n ? { x: 0, y: 0, width: bbWidth, height: bbHeight }\n : { x: bb.ll.x, y: bb.ll.y, width: bbWidth, height: bbHeight };\n\n const nodes = Array.from(g.nodes.values()).map((n) => snapshotNode(n, flipY));\n const edges = g.edges.map((e) => snapshotEdge(e, flipY));\n const clusters: ClusterGeometry[] = [];\n collectClusters(g, yAxis, flipY, clusters);\n\n return { bounds, nodes, edges, clusters };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agraphinfo_t from lib/common/types.h.\n * All GD_* macros in the C source are replaced by typed fields on GraphInfo.\n *\n * Supporting types (LayoutParams, RatioKind, FontnameKind, RankEntry,\n * RankTable) live in layoutParams.ts and rankEntry.ts to keep file size\n * within limits.\n *\n * @see lib/common/types.h:Agraphinfo_t\n */\n\nimport type { Box, Point } from './geom.js';\nimport type { Node } from './node.js';\nimport type { Graph } from './graph.js';\nimport type { LayoutParams, FontnameKind } from './layoutParams.js';\nimport type { RankTable } from './rankEntry.js';\nimport type { GvcContext } from '../gvc/context.js';\n\n// Re-export supporting types so consumers can import from this module alone.\nexport type { RatioKind, LayoutParams } from './layoutParams.js';\n// FontnameKind is both a const object (value) and a type \u2014 a plain export\n// covers both; no separate `export type` needed for the same name.\nexport { FontnameKind } from './layoutParams.js';\nexport type { AdjMatrix, RankEntry, RankTable } from './rankEntry.js';\n\n// ---------------------------------------------------------------------------\n// GVC_t \u2014 the rendering context attached to a laid-out graph.\n// ---------------------------------------------------------------------------\n\n/**\n * The rendering context attached to a laid-out graph (C's GVC_t). Retained\n * under the historical name `GVContext` for the `common/types.ts` re-export;\n * it now resolves to the concrete {@link GvcContext} rather than `unknown`, so\n * every `g.info.gvc` read is typed instead of an ad-hoc `as { textMeasurer? }`\n * cast. Type-only import \u2014 erased at compile time, so no runtime cycle with\n * `gvc/context.ts`.\n *\n * @see lib/gvc/gvcext.h:GVC_t\n */\nexport type GVContext = GvcContext;\n\n// ---------------------------------------------------------------------------\n// Agraphinfo_t \u2014 @see lib/common/types.h:Agraphinfo_t\n// ---------------------------------------------------------------------------\n\n/**\n * Graph-level info record, equivalent to Agraphinfo_t in the C source.\n * All GD_* accessor macros in the C source become plain typed fields here.\n *\n * Required fields (non-pointer C fields always present after graph_init):\n * - bb, rankdir, flags, charset, gui_state, has_labels, has_images\n *\n * All C pointer fields become optional (`| undefined`) because they may\n * legitimately be null/unset until the relevant layout pass runs.\n *\n * @see lib/common/types.h:Agraphinfo_t\n */\nexport interface GraphInfo {\n // -----------------------------------------------------------------------\n // Core rendering fields \u2014 GD_* macros that are always meaningful\n // -----------------------------------------------------------------------\n\n /**\n * Bounding box of the graph after layout.\n * @see lib/common/types.h:GD_bb\n */\n bb: Box;\n\n /**\n * Rank direction + flip flags (raw int, encode via SET_RANKDIR macro).\n * GD_rankdir(g) = rankdir & 0x3; GD_realrankdir(g) = rankdir >> 2.\n * @see lib/common/types.h:GD_rankdir2\n */\n rankdir: number;\n\n /**\n * Edge type and layout engine flags bitmask.\n * @see lib/common/types.h:GD_flags\n */\n flags: number;\n\n /**\n * Input character set constant (CHAR_UTF8, CHAR_LATIN1, CHAR_BIG5).\n * @see lib/common/types.h:GD_charset\n */\n charset: number;\n\n /**\n * GUI state flags bitmask (GUI_STATE_ACTIVE, _SELECTED, _VISITED, _DELETED).\n * @see lib/common/types.h:GD_gui_state\n */\n gui_state: number;\n\n /**\n * Bitmask of which label types are present in the graph.\n * @see lib/common/types.h:GD_has_labels\n */\n has_labels: number;\n\n /**\n * True if any node uses an image.\n * @see lib/common/types.h:GD_has_images\n */\n has_images: boolean;\n\n /**\n * Library-API-only flag (no C analogue): set by GvcContext.layout once the\n * graph has been laid out. Not cleared by freeLayout \u2014 node coordinates and\n * splines persist on the model after engine scratch is freed, so the\n * \"render then getLayout\" workflow stays valid. The public `getLayout` reads\n * it to reject a never-laid-out graph instead of returning the calloc-zero\n * geometry defaults as if they were real coordinates.\n */\n laidOut?: boolean;\n\n // -----------------------------------------------------------------------\n // Optional pointer fields \u2014 all C pointers become `| undefined`\n // -----------------------------------------------------------------------\n\n /**\n * Resolved layout parameters (set by the layout engine init).\n * @see lib/common/types.h:GD_drawing\n */\n drawing?: LayoutParams;\n\n /**\n * Cluster or graph title label. Typed fully in Batch 5b (textlabel_t).\n * @see lib/common/types.h:GD_label\n */\n label?: unknown;\n\n /**\n * Margin sizes for graph labels; four Points corresponding to pointf[4].\n * Indices: [0]=left, [1]=right, [2]=top, [3]=bottom per C convention.\n * @see lib/common/types.h:GD_border\n */\n border?: [Point, Point, Point, Point];\n\n /**\n * GVC context for cross-graph globals.\n * @see lib/common/types.h:GD_gvc\n */\n gvc?: GVContext;\n\n /**\n * Layout-engine cleanup function; called by graph_cleanup to free\n * engine-specific data attached to this graph.\n * @see lib/common/types.h:GD_cleanup\n */\n cleanup?: (g: Graph) => void;\n\n /**\n * Height below extremal ranks (dot layout).\n * @see lib/common/types.h:GD_ht1\n */\n ht1?: number;\n\n /**\n * Height above extremal ranks (dot layout).\n * @see lib/common/types.h:GD_ht2\n */\n ht2?: number;\n\n /**\n * Engine-specific algorithm state.\n * @see lib/common/types.h:GD_alg\n */\n alg?: unknown;\n\n // -----------------------------------------------------------------------\n // neato/fdp/sfdp-specific fields (#ifndef DOT_ONLY in C)\n // -----------------------------------------------------------------------\n\n /**\n * Node list for neato layout.\n * @see lib/common/types.h:GD_neato_nlist\n */\n neato_nlist?: Node[];\n\n /**\n * Move count for iterative neato placement.\n * @see lib/common/types.h:GD_move\n */\n move?: number;\n\n /**\n * All-pairs distance matrix (neato).\n * @see lib/common/types.h:GD_dist\n */\n dist?: number[][];\n\n /**\n * Spring constant matrix (neato).\n * @see lib/common/types.h:GD_spring\n */\n spring?: number[][];\n\n /**\n * Spring force sum matrix (neato).\n * @see lib/common/types.h:GD_sum_t\n */\n sum_t?: number[][];\n\n /**\n * Position delta tensor (neato, ndim-dimensional).\n * @see lib/common/types.h:GD_t\n */\n t?: number[][][];\n\n /**\n * Number of layout dimensions.\n * @see lib/common/types.h:GD_ndim\n */\n ndim?: number;\n\n /**\n * Original number of layout dimensions.\n * @see lib/common/types.h:GD_odim\n */\n odim?: number;\n\n // -----------------------------------------------------------------------\n // dot-specific fields (#ifndef NEATO_ONLY in C)\n // -----------------------------------------------------------------------\n\n /**\n * Number of cluster subgraphs.\n * @see lib/common/types.h:GD_n_cluster\n */\n n_cluster?: number;\n\n /**\n * Count of cluster edges created by fdp's processClusterEdges (edges whose\n * endpoint names a cluster). Non-zero \u21D2 HAS_CLUST_EDGE(g).\n * @see lib/common/utils.c:processClusterEdges (cl_edge_t.n_cluster_edges)\n */\n n_cluster_edges?: number;\n\n /**\n * Cluster margin (in points). Corresponds to the `margin` attribute on a\n * cluster graph, read by `late_int(g, G_margin, CL_OFFSET, 0)` in C.\n * @see lib/dotgen/position.c:contain_nodes\n */\n clusterMargin?: number;\n\n /**\n * Cluster array.\n *\n * IMPORTANT: In C this is 1-indexed \u2014 clust[1..n_cluster]. This TypeScript\n * array is 0-indexed; callers must add 1 when converting between C indices\n * and this array's indices, or subtract 1 when coming from C.\n *\n * @see lib/common/types.h:GD_clust\n */\n clust?: Graph[];\n\n /**\n * The root graph for dot layout purposes.\n * @see lib/common/types.h:GD_dotroot\n */\n dotroot?: Graph;\n\n /**\n * Linked-list head of nodes for dot layout traversal.\n * @see lib/common/types.h:GD_nlist\n */\n nlist?: Node;\n\n /**\n * Rank table for dot layout (array indexed by rank level).\n * @see lib/common/types.h:GD_rank\n */\n rank?: RankTable;\n\n /**\n * Containing cluster (not parent subgraph).\n * @see lib/common/types.h:GD_parent\n */\n parent?: Graph;\n\n /**\n * Cluster nesting level (not node level).\n * @see lib/common/types.h:GD_level\n */\n level?: number;\n\n /**\n * Set leader for minimum rank.\n * @see lib/common/types.h:GD_minrep\n */\n minrep?: Node;\n\n /**\n * Set leader for maximum rank.\n * @see lib/common/types.h:GD_maxrep\n */\n maxrep?: Node;\n\n /**\n * Fast-graph node component list.\n * @see lib/common/types.h:GD_comp (nlist_t)\n */\n comp?: Node[];\n\n /**\n * Set leader for minimum connected component.\n * @see lib/common/types.h:GD_minset\n */\n minset?: Node;\n\n /**\n * Set leader for maximum connected component.\n * @see lib/common/types.h:GD_maxset\n */\n maxset?: Node;\n\n /**\n * Minimum rank value in this graph.\n * @see lib/common/types.h:GD_minrank\n */\n minrank?: number;\n\n /**\n * Maximum rank value in this graph.\n * @see lib/common/types.h:GD_maxrank\n */\n maxrank?: number;\n\n /**\n * True if the graph has any flat (same-rank) edges.\n * @see lib/common/types.h:GD_has_flat_edges\n */\n has_flat_edges?: boolean;\n\n /**\n * Parity correction for the rank-array renumbering done by `abomination`.\n * C inserts a flat-label rank at index -1 (so real nodes keep their even rank\n * index); this 0-based port instead shifts every `ND_rank` up by 1 (AD-2),\n * which inverts the rank-index parity that `make_LR_constraints` uses to pick\n * the LR separation (`sep[i & 1]`). Each shift increments this offset so\n * `lrSep` can recover C's parity via `(rankIdx + abomShift) & 1`. 0 / unset\n * means no abomination happened. @see lib/dotgen/flat.c:abomination\n */\n abomShift?: number;\n\n /**\n * Show subdivision boxes for debugging; 0 means off.\n * @see lib/common/types.h:GD_showboxes\n */\n showboxes?: number;\n\n /**\n * SVG font name mangling setting.\n * @see lib/common/types.h:GD_fontnames\n */\n fontnames?: FontnameKind;\n\n /**\n * Node separation in points.\n * @see lib/common/types.h:GD_nodesep\n */\n nodesep?: number;\n\n /**\n * Rank separation in points.\n * @see lib/common/types.h:GD_ranksep\n */\n ranksep?: number;\n\n /**\n * Left-boundary node of the bounding box.\n * @see lib/common/types.h:GD_ln\n */\n ln?: Node;\n\n /**\n * Right-boundary node of the bounding box.\n * @see lib/common/types.h:GD_rn\n */\n rn?: Node;\n\n /**\n * Cluster leader node.\n * @see lib/common/types.h:GD_leader\n */\n leader?: Node;\n\n /**\n * Per-rank leader node array.\n * @see lib/common/types.h:GD_rankleader\n */\n rankleader?: Node[];\n\n /**\n * True if the cluster has been expanded.\n * @see lib/common/types.h:GD_expanded\n */\n expanded?: boolean;\n\n /**\n * Installation state flag.\n * @see lib/common/types.h:GD_installed\n */\n installed?: number;\n\n /**\n * Set type for union-find operations.\n * @see lib/common/types.h:GD_set_type\n */\n set_type?: number;\n\n /**\n * Label position flag for cluster labels.\n * @see lib/common/types.h:GD_label_pos\n */\n label_pos?: number;\n\n /**\n * Whether exact rank separation is enforced.\n * @see lib/common/types.h:GD_exact_ranksep\n */\n exact_ranksep?: boolean;\n\n /**\n * Whether the rank direction is flipped (used in flat_reorder and build_ranks).\n * Set from GD_realrankdir: true when layout is LR or RL (horizontal).\n * @see lib/dotgen/mincross.c:GD_flip\n */\n flip?: boolean;\n\n /**\n * Whether edge concentration is enabled (merges parallel edges).\n * Corresponds to the `concentrate` graph attribute.\n * @see lib/dotgen/class2.c:Concentrate\n */\n concentrate?: boolean;\n\n /**\n * Whether the graph has compound edges (lhead/ltail attributes set on edges).\n * When true, dot_compoundEdges clips splines to cluster bounding boxes.\n * Corresponds to the `compound` graph attribute.\n * @see lib/dotgen/compound.c:dot_compoundEdges\n * @see lib/common/types.h:GD_flags (via compound flag)\n */\n compound?: boolean;\n\n /**\n * AD-8 phase marker: true between create_aux_edges and set_xcoords,\n * while ND_rank holds x-coordinates instead of rank indices.\n * Cleared by setXcoords after ND_coord.x is populated.\n * @see lib/dotgen/position.c:set_xcoords (AD-8)\n */\n rankIsXCoord?: boolean;\n\n /**\n * MT19937 seed used by the SGD layout engine.\n * Corresponds to the rk_seed() call in lib/neatogen/sgd.c.\n * Defaults to 0 when not set.\n *\n * @see lib/neatogen/sgd.c:sgd (rk_seed call)\n */\n seed?: number;\n\n /**\n * Per-layout flag: true when edge label positions have been computed by\n * the spline-routing pass (dotsplines.c:EdgeLabelsDone).\n *\n * C uses a process-global `EdgeLabelsDone` (lib/common/globals.h) reset in\n * lib/common/input.c:commonInitGraph and set at lib/dotgen/dotsplines.c:471.\n * This port lifts it onto GraphInfo so it is per-graph (no globals).\n *\n * @see lib/common/globals.h:EdgeLabelsDone\n * @see lib/dotgen/dotsplines.c:471\n */\n edgeLabelsDone?: boolean;\n\n /**\n * Neato start attribute: controls initial placement and RNG seed.\n * Values: \"random[N]\" | \"self\" | \"regular\" | numeric string.\n * Corresponds to agget(g, \"start\") in lib/neatogen/neatoinit.c:setSeed.\n *\n * @see lib/neatogen/neatoinit.c:setSeed\n */\n start?: string;\n\n /**\n * Neato layout mode string attribute.\n * Values: \"KK\" | \"major\" | \"hier\" | \"ipsep\" | \"sgd\".\n * Corresponds to agget(g, \"mode\") in lib/neatogen/neato.h:neatoMode.\n *\n * @see lib/neatogen/neato.h:neatoMode\n */\n mode?: string;\n\n /**\n * Neato overlap removal control.\n * \"false\" disables VPSC overlap removal; any other value enables it.\n * Corresponds to agget(g, \"overlap\") in lib/neatogen/neatoinit.c.\n *\n * @see lib/neatogen/neatoinit.c:neato_layout\n */\n overlap?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a zero-initialized GraphInfo with all required fields set to their\n * defaults. All optional fields are left undefined (absent from the object),\n * matching a freshly allocated Agraphinfo_t after graph_init().\n *\n * @see lib/common/utils.c:graph_init\n */\nexport function makeGraphInfo(): GraphInfo {\n return {\n bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },\n rankdir: 0,\n flags: 0,\n charset: 0,\n gui_state: 0,\n has_labels: 0,\n has_images: false,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agraph_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agraph_s\n * @see lib/cgraph/cgraph.h:Agdesc_s\n */\n\nimport type { Edge } from './edge.js';\nimport type { Node } from './node.js';\nimport { type GraphInfo, makeGraphInfo } from './graphInfo.js';\n\n/**\n * Describes the kind of graph, corresponding to the Agdesc_t descriptor\n * fields `directed` and `strict` in the C implementation.\n *\n * @see lib/cgraph/cgraph.h:Agdesc_s\n * @see lib/cgraph/graph.c:Agdirected\n * @see lib/cgraph/graph.c:Agstrictdirected\n * @see lib/cgraph/graph.c:Agundirected\n * @see lib/cgraph/graph.c:Agstrictundirected\n */\nexport type GraphKind =\n | 'directed'\n | 'undirected'\n | 'strict-directed'\n | 'strict-undirected';\n\n/**\n * Represents an Agraph_t \u2014 a graph or subgraph.\n *\n * In the C implementation Agraph_t holds n_seq/n_id (node dicts), e_seq/e_id\n * (edge dicts), g_seq/g_id (subgraph dicts), parent, root, and a shared\n * Agclos_t. Here we use native TypeScript collections in place of libcdt\n * Dict_t, and the `info` field replaces the agbindrec / GD_* mechanism (AD-1).\n *\n * Nodes are owned by the root graph. Subgraphs hold references to the same\n * Node instances (Subgraph Ownership Semantics, cgraph.md).\n *\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\nexport class Graph {\n /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */\n readonly name: string;\n\n /**\n * True when the graph was declared with NO name (`digraph {`), as opposed to\n * an explicit empty name (`digraph \"\" {`). Both store `name === ''`, but\n * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while\n * the empty-named one keeps `\"\"`. Only the imagemap `<map id/name>` reads this\n * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an\n * anonymous root round-trips as `digraph {`, not `digraph %1 {`.\n * @see lib/cgraph/id.c:idmap (anon \u2192 `%1`) ; lib/cgraph/agraph.c:agnameof\n */\n readonly anonymous: boolean;\n\n /**\n * Directed/strict classification; mirrors Agdesc_t.directed and\n * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s\n */\n readonly kind: GraphKind;\n\n /**\n * Node set in insertion order, keyed by name.\n * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n nodes: Map<string, Node>;\n\n /**\n * All edges owned by this graph (root graph only for ownership; subgraphs\n * reference a subset). Mirrors Agraph_t.e_seq.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n edges: Edge[];\n\n /**\n * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n subgraphs: Map<string, Graph>;\n\n /**\n * String attributes (agget/agset equivalents).\n * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /** Default attributes applied to all nodes in this graph scope. */\n nodeDefaults: Map<string, string>;\n\n /** Default attributes applied to all edges in this graph scope. */\n edgeDefaults: Map<string, string>;\n\n /**\n * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->\n * root, inner wins) captured when this subgraph was OPENED during parsing.\n * Graph attributes set on an ancestor AFTER this subgraph was created are not\n * included \u2014 mirroring cgraph's parse-time agsubg defval copy. Used for\n * order-correct label/font inheritance (e.g. a root `label=` declared after a\n * cluster must NOT apply to that cluster). Undefined on the root graph.\n * @see lib/cgraph/graph.c:agsubg (attr defval copy)\n */\n graphDefaultsSnapshot?: Map<string, string>;\n\n /**\n * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than\n * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_\n * KEYS). cgraph distinguishes these structurally \u2014 a seeded value is inherited\n * through the dict view and has no local symbol, while a local declaration\n * creates one even when its value equals the inherited value \u2014 so the\n * serializer needs the provenance to decide what write_dict would print.\n * Undefined on the root graph and on any scope that seeded nothing.\n * @see lib/cgraph/write.c:262 write_dict\n */\n seededAttrs?: Set<string>;\n\n /**\n * Graph-attribute keys whose declaration in THIS scope was the first anywhere\n * in the graph. cgraph's `setattr` splits on exactly this: a key with no\n * existing symbol takes the global-declaration branch, which ends up seeding\n * every direct subgraph that already exists with a local empty value (see\n * `eagerEmptyParts`); a key some earlier scope already declared takes the\n * \"new local definition\" branch and touches no subgraph at all. So an\n * identical `graph [fill=x]` statement makes a child print `fill=\"\"` or print\n * nothing purely according to whether a SIBLING declared `fill` first.\n * Undefined on any scope that declared nothing new.\n * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)\n */\n firstGraphDecl?: Set<string>;\n\n /**\n * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: GraphInfo;\n\n /**\n * Immediate parent graph. null for root graphs.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n parent: Graph | null;\n\n /**\n * Root (main) graph. Self-referential for root graphs;\n * points up for subgraphs.\n * @see lib/cgraph/cgraph.h:Agraph_s\n */\n root: Graph;\n\n /**\n * Subgraph sequence number (AGSEQ). A global counter on the root, assigned\n * at creation in source order, counting anonymous subgraphs. The root keeps\n * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to\n * emit cluster ids `clust<seq>`.\n * @see lib/cgraph/cgraph.h:AGSEQ\n * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))\n */\n seq = 0;\n\n /**\n * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ\n * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`\n * pre-increments it. Other graphs leave it at 0.\n * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])\n */\n subgSeqCounter = 0;\n\n /**\n * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy \u2014\n * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the\n * ROOT only, at parse time, whenever a graph attribute is assigned on any\n * (sub)graph: setting `ordering=out` on a subgraph declares \"ordering\"\n * graph-wide with an empty default, so `agGraphAttr(root, \"ordering\")` returns\n * `\"\"` (not undefined) \u2014 mirroring `agfindgraphattr`/`late_string`. Read via\n * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.\n * @see lib/cgraph/attr.c:agattr (graph-wide declaration, \"\" default)\n */\n declaredGraphAttrs: Set<string> = new Set();\n\n /** @see lib/cgraph/graph.c:agopen */\n constructor(name: string, kind: GraphKind, anonymous = false) {\n this.name = name;\n this.anonymous = anonymous;\n this.kind = kind;\n this.nodes = new Map();\n this.edges = [];\n this.subgraphs = new Map();\n this.attrs = new Map();\n this.nodeDefaults = new Map();\n this.edgeDefaults = new Map();\n this.info = makeGraphInfo();\n this.parent = null;\n this.root = this;\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agnodeinfo_t from lib/common/types.h.\n *\n * All ND_* macros in the C source expand into this struct via AGDATA().\n * In TypeScript, ND_* accesses become direct property reads/writes on NodeInfo.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/common/types.h:ND_* macros\n */\n\nimport type { Point, Box } from './geom.js';\nimport type { Edge } from './edge.js';\nimport type { Graph } from './graph.js';\nimport type { Node } from './node.js';\nimport type { NodeAlgData } from './algData.js';\n\n// Per-engine ND_alg data types live in ./algData.js (split out to keep this\n// file under the size cap). Re-exported here so existing importers of\n// './nodeInfo.js' continue to resolve them unchanged.\nexport type {\n DotAlgData, NeatoAlgData, FdpAlgData, FdpDndata, CircoNData, CircoCData,\n TwopiAlgData, OsageAlgData, PatchworkAlgData, NodeAlgData,\n} from './algData.js';\nexport { THETA_UNSET } from './algData.js';\n\n// ---------------------------------------------------------------------------\n// Edge list helper type (mirrors elist in types.h)\n// ---------------------------------------------------------------------------\n\n/**\n * Dynamic edge list, ported from the C `elist` struct.\n *\n * @see lib/common/types.h: typedef struct elist { edge_t **list; size_t size; }\n */\nexport interface EdgeList {\n list: Edge[];\n size: number;\n}\n\n// ---------------------------------------------------------------------------\n// NodeInfo interface\n// ---------------------------------------------------------------------------\n\n/**\n * Node-level layout info record, ported from Agnodeinfo_t in lib/common/types.h.\n *\n * This interface replaces all ND_* macro accesses in the C source. Required\n * (always-present) fields mirror the non-pointer, non-conditional fields of\n * Agnodeinfo_t. Optional fields correspond to pointer fields and to fields\n * gated by #ifndef DOT_ONLY / #ifndef NEATO_ONLY.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/common/types.h:ND_* macros\n */\nexport interface NodeInfo {\n // -------------------------------------------------------------------------\n // Required fields \u2014 always present, non-pointer in C\n // -------------------------------------------------------------------------\n\n /** Final layout coordinate (node center). @see lib/common/types.h:ND_coord */\n coord: Point;\n\n /** Node width in inches. @see lib/common/types.h:ND_width */\n width: number;\n\n /** Node height in inches. @see lib/common/types.h:ND_height */\n height: number;\n\n /** Bounding box. @see lib/common/types.h:ND_bb */\n bb: Box;\n\n /** Total height in points. @see lib/common/types.h:ND_ht */\n ht: number;\n\n /** Left half-width in points. @see lib/common/types.h:ND_lw */\n lw: number;\n\n /** Right half-width in points. @see lib/common/types.h:ND_rw */\n rw: number;\n\n /**\n * Width in points with penwidth taken into account.\n * @see lib/common/types.h:ND_outline_width\n */\n outline_width: number;\n\n /**\n * Height in points with penwidth taken into account.\n * @see lib/common/types.h:ND_outline_height\n */\n outline_height: number;\n\n /**\n * Pre-periphery-growth node width in inches: the box the innermost\n * vertex ring is scaled to before peripheries step outward. Only set\n * by the measurer sizing path; consumed by multi-periphery vertex\n * generation. (C keeps the equivalent implicitly \u2014 poly_init builds\n * vertices and bb in one pass.)\n * @see lib/common/shapes.c:poly_init\n */\n base_width?: number;\n\n /** Pre-periphery-growth node height in inches. @see base_width */\n base_height?: number;\n\n /**\n * Layout state (char in C). @see lib/common/types.h:ND_state\n */\n state: number;\n\n /**\n * Node state for GUI ops (unsigned char in C).\n * Bitfield values: GUI_STATE_ACTIVE, GUI_STATE_SELECTED,\n * GUI_STATE_VISITED, GUI_STATE_DELETED.\n * @see lib/common/types.h:ND_gui_state\n */\n gui_state: number;\n\n /** True if node represents a cluster. @see lib/common/types.h:ND_clustnode */\n clustnode: boolean;\n\n // -------------------------------------------------------------------------\n // Optional pointer / engine-specific fields\n // -------------------------------------------------------------------------\n\n /**\n * Main node label (textlabel_t* in C).\n * Typed as unknown until textlabel_t is ported in Batch 5b.\n * @see lib/common/types.h:ND_label\n */\n label?: unknown;\n\n /**\n * External node label (textlabel_t* in C).\n * Typed as unknown until textlabel_t is ported in Batch 5b.\n * @see lib/common/types.h:ND_xlabel\n */\n xlabel?: unknown;\n\n /**\n * Node shape descriptor (shape_desc* in C).\n * Typed as unknown until shape_desc is ported.\n * @see lib/common/types.h:ND_shape\n */\n shape?: unknown;\n\n /**\n * Shape-specific data (polygon_t*, field_t*, or engine struct in C).\n * Typed as unknown until the concrete shape types are ported.\n * @see lib/common/types.h:ND_shape_info\n */\n shape_info?: unknown;\n\n // -------------------------------------------------------------------------\n // Fields from #ifndef NEATO_ONLY block\n // -------------------------------------------------------------------------\n\n /**\n * During the dot rank-assignment phase this field holds the assigned rank\n * (integer row in the layered graph).\n *\n * DUAL-USE WARNING: During the dot position phase (x-coordinate assignment),\n * `rank` is REPURPOSED as the x-coordinate by network simplex. Do not read\n * this field as a rank value while the position phase is active. The field\n * is restored to its rank value by `set_xcoords` at the end of the position\n * phase. See lib/dotgen/position.c and AD-8.\n *\n * @see lib/common/types.h:ND_rank\n */\n rank?: number;\n\n /**\n * Initial order for ordered edges; also used in crossing-minimization.\n * @see lib/common/types.h:ND_order\n */\n order?: number;\n\n /**\n * AD-8: True while the position phase is active and `rank` holds an\n * x-coordinate instead of a rank index. Set before rank(g,2,\u2026) is called;\n * cleared by setXcoords after ND_coord.x is populated.\n * @see lib/dotgen/position.c:set_xcoords\n */\n rankIsXCoord?: boolean;\n\n /** @see lib/common/types.h:ND_mval */\n mval?: number;\n\n /**\n * Fast-graph / spanning-tree node type (char in C).\n * @see lib/common/types.h:ND_node_type\n */\n node_type?: number;\n\n /**\n * Rank type classification (char in C).\n * @see lib/common/types.h:ND_ranktype\n */\n ranktype?: number;\n\n /**\n * Weight class (char in C).\n * @see lib/common/types.h:ND_weight_class\n */\n weight_class?: number;\n\n /**\n * Visited / traversal mark (size_t in C).\n * @see lib/common/types.h:ND_mark\n */\n mark?: number;\n\n /**\n * On-stack flag for DFS (char in C).\n * @see lib/common/types.h:ND_onstack\n */\n onstack?: number;\n\n /**\n * True if edge has port info. @see lib/common/types.h:ND_has_port\n */\n has_port?: boolean;\n\n /**\n * Showboxes flag (unsigned char in C).\n * @see lib/common/types.h:ND_showboxes\n */\n showboxes?: number;\n\n // Edge adjacency lists (elist fields)\n\n /** In-edges. @see lib/common/types.h:ND_in */\n in?: EdgeList;\n\n /** Out-edges. @see lib/common/types.h:ND_out */\n out?: EdgeList;\n\n /** Flat in-edges (same-rank). @see lib/common/types.h:ND_flat_in */\n flat_in?: EdgeList;\n\n /** Flat out-edges (same-rank). @see lib/common/types.h:ND_flat_out */\n flat_out?: EdgeList;\n\n /** Other (non-tree, non-flat) edges. @see lib/common/types.h:ND_other */\n other?: EdgeList;\n\n /** Saved in-edges (for virtual node removal). @see lib/common/types.h:ND_save_in */\n save_in?: EdgeList;\n\n /** Saved out-edges (for virtual node removal). @see lib/common/types.h:ND_save_out */\n save_out?: EdgeList;\n\n /** Network-simplex tree in-edges. @see lib/common/types.h:ND_tree_in */\n tree_in?: EdgeList;\n\n /** Network-simplex tree out-edges. @see lib/common/types.h:ND_tree_out */\n tree_out?: EdgeList;\n\n // Doubly-linked list through fast-graph\n\n /** Next node in doubly-linked fast-graph list. @see lib/common/types.h:ND_next */\n next?: Node;\n\n /** Previous node in doubly-linked fast-graph list. @see lib/common/types.h:ND_prev */\n prev?: Node;\n\n // Network-simplex fields\n\n /**\n * Network-simplex spanning-tree parent edge.\n * @see lib/common/types.h:ND_par\n */\n par?: Edge;\n\n /** Low limit for network simplex. @see lib/common/types.h:ND_low */\n low?: number;\n\n /** Limit for network simplex subtree. @see lib/common/types.h:ND_lim */\n lim?: number;\n\n /** Priority for network simplex edge selection. @see lib/common/types.h:ND_priority */\n priority?: number;\n\n // Union-find / collapsing\n\n /** Union-find tree size. @see lib/common/types.h:ND_UF_size */\n UF_size?: number;\n\n /** Union-find parent node. @see lib/common/types.h:ND_UF_parent */\n UF_parent?: Node;\n\n /** Representative node for a collapsed set. @see lib/common/types.h:ND_rep */\n rep?: Node;\n\n /** Set leader for min/max rank. @see lib/common/types.h:ND_set */\n set?: Node;\n\n /** Cluster subgraph that this node belongs to. @see lib/common/types.h:ND_clust */\n clust?: Graph;\n\n /**\n * Flat-edge label constraint edge stored in ND_alg during dot position phase.\n * Set by flat.c when a virtual node hosts a flat-edge label; read by\n * make_LR_constraints and dotsplines.c.\n * @see lib/dotgen/flat.c:ND_alg (flat edge label usage)\n */\n posAlg?: Edge;\n\n // -------------------------------------------------------------------------\n // Fields from #ifndef DOT_ONLY block (neato/fdp/sfdp)\n // -------------------------------------------------------------------------\n\n /**\n * True if the node's position is pinned (pos attribute with !).\n * @see lib/common/types.h:ND_pinned\n */\n pinned?: boolean;\n\n /**\n * True if the node has a user-supplied `pos=` (C's ND_pinned >= P_SET, i.e.\n * hasPos). Distinct from {@link pinned} (P_PIN, isFixed): a plain `pos=`\n * seeds the initial layout without fixing the node. neato uses it to start\n * majorization from the input positions (user_pos + hasPos).\n * @see lib/neatogen/neatoinit.c:user_pos; lib/common/macros.h:hasPos\n */\n posSet?: boolean;\n\n /**\n * Internal integer node ID used by neato/fdp.\n * @see lib/common/types.h:ND_id\n */\n id?: number;\n\n /**\n * Index into the priority queue heap (neato/fdp).\n * @see lib/common/types.h:ND_heapindex\n */\n heapindex?: number;\n\n /**\n * BFS hop count from the starting node (neato/fdp).\n * @see lib/common/types.h:ND_hops\n */\n hops?: number;\n\n /**\n * N-dimensional position vector (length = ndim).\n * @see lib/common/types.h:ND_pos\n */\n pos?: number[];\n\n /**\n * Distance value used by neato stress majorization.\n * @see lib/common/types.h:ND_dist\n */\n dist?: number;\n\n // -------------------------------------------------------------------------\n // Engine algorithm data (AD-7)\n // -------------------------------------------------------------------------\n\n /**\n * Per-engine algorithm data. Each layout engine stores a different struct\n * in ND_alg; the `kind` discriminant identifies which engine's data is\n * active without unsafe casting (AD-7).\n *\n * @see lib/common/types.h:ND_alg\n */\n alg?: NodeAlgData;\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Constructs a zero-valued NodeInfo with all required fields set to their\n * zero/false initial state and all optional fields absent (undefined).\n *\n * Mirrors the effect of calloc-initializing an Agnodeinfo_t in C.\n *\n * @see lib/common/types.h:Agnodeinfo_t\n * @see lib/cgraph/node.c:agnode (initial record allocation via agbindrec)\n */\nexport function makeNodeInfo(): NodeInfo {\n // C agbindrec calloc-zeroes the WHOLE Agnodeinfo_t struct, so every ND_*\n // field exists from creation at a fixed offset. The port mirrors that:\n // pre-declaring EVERY optional field (value-preserving `undefined` \u2014 same\n // semantics as absent under the `?? 0`/truthy accessors) pins a single V8\n // hidden class for all NodeInfo objects. Without this, fields added lazily\n // during layout (par/low/lim/rank/tree_in/tree_out/...) drove NodeInfo into\n // dictionary (slow) mode \u2014 the dominant cost of the network-simplex DFS hot\n // path on large graphs (~26x per-op vs C). No code reads node.info via\n // `in`/spread/Object.keys, so adding the keys is behavior-free (verified\n // identical across the full dot corpus). Keep in sync with NodeInfo.\n return {\n coord: { x: 0, y: 0 },\n width: 0,\n height: 0,\n bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },\n ht: 0,\n lw: 0,\n rw: 0,\n outline_width: 0,\n outline_height: 0,\n state: 0,\n gui_state: 0,\n clustnode: false,\n // --- lazily-assigned fields, pre-declared to lock the hidden class ---\n base_width: undefined, base_height: undefined, label: undefined, xlabel: undefined,\n shape: undefined, shape_info: undefined, rank: undefined, order: undefined,\n rankIsXCoord: undefined, mval: undefined, node_type: undefined, ranktype: undefined,\n weight_class: undefined, mark: undefined, onstack: undefined, has_port: undefined,\n showboxes: undefined, in: undefined, out: undefined, flat_in: undefined,\n flat_out: undefined, other: undefined, save_in: undefined, save_out: undefined,\n tree_in: undefined, tree_out: undefined, next: undefined, prev: undefined,\n par: undefined, low: undefined, lim: undefined, priority: undefined,\n UF_size: undefined, UF_parent: undefined, rep: undefined, set: undefined,\n clust: undefined, posAlg: undefined, pinned: undefined, id: undefined,\n heapindex: undefined, hops: undefined, pos: undefined, dist: undefined, alg: undefined,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agnode_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\n\nimport type { Edge } from './edge.js';\nimport type { Graph } from './graph.js';\nimport { type NodeInfo, makeNodeInfo } from './nodeInfo.js';\n\n/**\n * Represents an Agnode_t \u2014 a graph node.\n *\n * In the C implementation, Agnode_t embeds Agobj_t (tag + data), holds a\n * pointer to its root graph, and contains an embedded Agsubnode_t for the\n * main graph. Per-graph membership is tracked via Agsubnode_t records in\n * libcdt dicts. Here node identity is object identity (pointer comparison\n * in C maps to reference equality in TypeScript).\n *\n * The `info` field replaces the agbindrec / ND_* mechanism (AD-1).\n *\n * @see lib/cgraph/cgraph.h:Agnode_s\n * @see lib/cgraph/cgraph.h:Agsubnode_s\n */\nexport class Node {\n /**\n * Unique integer ID per root graph; maps to AGID in the C implementation.\n * @see lib/cgraph/cgraph.h:AGID\n */\n readonly id: number;\n\n /** Node name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */\n readonly name: string;\n\n /**\n * String attributes (agget/agset equivalents).\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /**\n * Layout-engine info; replaces ND_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: NodeInfo;\n\n /**\n * Root graph that owns this node.\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\n readonly root: Graph;\n\n /**\n * Frozen snapshot of the node-attribute defaults (`node [...]`) in effect \u2014\n * from the declaring subgraph up through its ancestors \u2014 at the moment this\n * node was first created. Captured at creation (not read live at render) so\n * that a `node [...]` declared *after* this node in the same or an ancestor\n * scope does not retroactively apply, matching DOT semantics. Present on\n * parser-built nodes (even when empty); undefined on programmatically created\n * nodes, which fall back to live graph defaults in `nodeAttr`.\n */\n nodeDefaultsSnapshot?: Map<string, string>;\n\n /** @see lib/cgraph/node.c:agnode */\n constructor(id: number, name: string, root: Graph) {\n this.id = id;\n this.name = name;\n this.attrs = new Map();\n this.info = makeNodeInfo();\n this.root = root;\n }\n\n /**\n * Returns out-edges of this node in graph g (where this node is the tail).\n *\n * ORDER: cgraph's per-node out-edge dict sorts by the SEQ of the edge's\n * out-half node \u2014 the HEAD \u2014 then by edge seq (edge.c:agedgeseqcmpf,\n * Ag_mainedge_seq_disc). Iteration is therefore by (head creation order,\n * edge creation order), NOT by plain edge insertion order. This ordering\n * is load-bearing for emission order and for force-accumulation order in\n * the iterative engines.\n *\n * INVARIANT: In the C cgraph library, the adjacency list for a node begins\n * with the node's own self-loop (index 0), and neighbor traversal MUST start\n * at index 1. This convention originates from lib/cgraph/edge.c and\n * agfstedge/agnxtedge. In this TypeScript implementation, self-loops are\n * included in outEdges() but are excluded from inEdges() \u2014 matching the\n * agnxtedge behavior that skips self-loops as in-edges. Callers iterating\n * over neighbors must account for self-loop edges appearing in outEdges().\n *\n * @see lib/cgraph/edge.c:agfstout\n * @see lib/cgraph/edge.c:agnxtout\n * @see lib/cgraph/edge.c:agedgeseqcmpf\n */\n outEdges(g: Graph): Edge[] {\n return g.edges\n .filter((e) => e.tail === this)\n .sort((a, b) => (a.head.id - b.head.id) || (a.seq - b.seq));\n }\n\n /**\n * Returns in-edges of this node in graph g (where this node is the head).\n * Self-loops are excluded per C agnxtedge semantics. Ordered by the SEQ\n * of the in-half node \u2014 the TAIL \u2014 then edge seq (agedgeseqcmpf).\n *\n * @see lib/cgraph/edge.c:agfstin\n * @see lib/cgraph/edge.c:agnxtin\n * @see lib/cgraph/edge.c:agnxtedge (skips self-loops as in-edges)\n * @see lib/cgraph/edge.c:agedgeseqcmpf\n */\n inEdges(g: Graph): Edge[] {\n return g.edges\n .filter((e) => e.head === this && e.tail !== this)\n .sort((a, b) => (a.tail.id - b.tail.id) || (a.seq - b.seq));\n }\n}\n\n/**\n * Build a tail\u2192out-edges index for graph `g`. Each list is identical to\n * `node.outEdges(g)` (tail === node, self-loops included, sorted by head.id then\n * seq), but the whole index is O(E log E) instead of O(N\u00B7E log E) from calling\n * outEdges once per node. Use at sites that loop every node's out-edges and do\n * not mutate `g.edges` during the loop. @see Node.outEdges\n */\nexport function buildOutEdgeIndex(g: Graph): Map<Node, Edge[]> {\n const m = new Map<Node, Edge[]>();\n for (const e of g.edges) {\n const l = m.get(e.tail);\n if (l !== undefined) l.push(e);\n else m.set(e.tail, [e]);\n }\n for (const l of m.values()) {\n l.sort((a, b) => (a.head.id - b.head.id) || (a.seq - b.seq));\n }\n return m;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Faithful TypeScript port of the cgraph subgraph/node operations needed by\n * later layout passes (e.g. dot's newrank fill_ranks, which creates a\n * `_new_rank` subgraph and adds anonymous placeholder nodes).\n *\n * Nodes are owned by the root graph and keyed by name in each graph's\n * `nodes` Map; subgraph membership is presence in that subgraph's `nodes`\n * Map. A node in a subgraph is a member of every enclosing graph up to and\n * including root (Subgraph Ownership Semantics).\n *\n * @see lib/cgraph/node.c\n * @see lib/cgraph/subg.c\n */\n\nimport type { Graph } from './graph.js';\nimport { Graph as GraphClass } from './graph.js';\nimport { Node } from './node.js';\n\n/**\n * The C anonymous-name prefix LOCALNAMEPREFIX ('%'); an object with no\n * external name has `agnameof` synthesize \"%<id>\".\n * @see lib/cgraph/cghdr.h:LOCALNAMEPREFIX\n * @see lib/cgraph/id.c:aginternalmapprint\n */\nconst LOCALNAMEPREFIX = '%';\n\n/**\n * Allocate a fresh node id unique within `root.nodes`. Mirrors C's\n * agnextseq/agmapnametoid id reservation: at layout time the per-parse\n * NodeRegistry counter is unavailable, so derive (max existing root id) + 1.\n * @see lib/cgraph/node.c:agnextseq\n */\nfunction freshNodeId(root: Graph): number {\n let max = -1;\n for (const n of root.nodes.values()) {\n if (n.id > max) max = n.id;\n }\n return max + 1;\n}\n\n/**\n * Create-or-get a node by name on the root graph. When `name` is null/empty,\n * mint a fresh anonymous node with a unique id and unique \"%<id>\" name. When\n * `create` is false and a named node is absent, returns null.\n *\n * Unlike the C agnode (which keys by id), this port keys nodes by name in\n * `Graph.nodes`; node creation always installs into the root graph.\n *\n * @see lib/cgraph/node.c:agnode\n */\nexport function agnode(\n g: Graph,\n name: string | null,\n create: boolean,\n): Node | null {\n const root = g.root;\n if (name !== null && name !== '') {\n const existing = root.nodes.get(name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const node = new Node(freshNodeId(root), name, root);\n root.nodes.set(name, node);\n return node;\n }\n if (!create) return null;\n const id = freshNodeId(root);\n const anonName = LOCALNAMEPREFIX + String(id);\n const node = new Node(id, anonName, root);\n root.nodes.set(anonName, node);\n return node;\n}\n\n/**\n * Assign a subgraph its AGSEQ from the root-level counter, mirroring\n * `agnextseq(par, AGRAPH)` in agopen: pre-increment `clos->seq[AGRAPH]` (stored\n * on the root as `subgSeqCounter`) and record it on the subgraph. Call once,\n * only when the subgraph is first created.\n *\n * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))\n * @see lib/cgraph/graph.c:agnextseq\n */\nexport function assignSubgSeq(parent: Graph, sg: Graph): void {\n sg.seq = ++parent.root.subgSeqCounter;\n}\n\n/**\n * Create-or-get a named subgraph under `parent`. On create, sets the new\n * graph's `parent`/`root`, assigns its AGSEQ, and registers it in\n * `parent.subgraphs`. Returns null when `create` is false and the subgraph is\n * absent.\n *\n * @see lib/cgraph/subg.c:agsubg\n */\nexport function agsubg(\n parent: Graph,\n name: string,\n create: boolean,\n): Graph | null {\n const existing = parent.subgraphs.get(name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const subg = new GraphClass(name, parent.kind);\n subg.parent = parent;\n subg.root = parent.root;\n assignSubgSeq(parent, subg);\n parent.subgraphs.set(name, subg);\n return subg;\n}\n\n/**\n * Lookup or insert node `n` in subgraph `g`, recursively installing it into\n * every enclosing graph up to and including root. Returns null if `n` does\n * not belong to `g`'s root. Mirrors the parser's enclosing-graph membership\n * loop (builder.ts processNodeStmt).\n *\n * @see lib/cgraph/node.c:agsubnode\n */\nexport function agsubnode(g: Graph, n: Node, create: boolean): Node | null {\n if (g.root !== n.root) return null;\n const existing = g.nodes.get(n.name);\n if (existing !== undefined) return existing;\n if (!create) return null;\n const par = g.parent;\n if (par !== null) {\n agsubnode(par, n, create);\n }\n g.nodes.set(n.name, n);\n return n;\n}\n\n/**\n * Remove node `n` from graph `g` and every enclosing graph up to and\n * including root (its member graphs). Mirrors agdelnode's image removal\n * applied across the graph and its ancestors.\n *\n * @see lib/cgraph/node.c:agdelnode\n */\nexport function agdelnode(g: Graph, n: Node): void {\n for (let cur: Graph | null = g; cur !== null; cur = cur.parent) {\n cur.nodes.delete(n.name);\n }\n}\n\n/**\n * Remove subgraph `sg` from `parent.subgraphs`. Mirrors agdelsubg, which\n * only deletes the entry in the parent's subgraph dict.\n *\n * @see lib/cgraph/subg.c:agdelsubg\n */\nexport function agdelsubg(parent: Graph, sg: Graph): void {\n parent.subgraphs.delete(sg.name);\n}\n\n/**\n * Faithful `agxget` for a graph attribute, mirroring C's\n * `late_string(g, agfindgraphattr(g, name), NULL)`:\n * - returns `g`'s explicit value if set;\n * - else, if `name` was declared graph-wide (set on any (sub)graph at parse\n * time \u2192 present in `root.declaredGraphAttrs`), returns the empty-string\n * default `\"\"` (cgraph's agsafeset default);\n * - else returns `undefined` (the attribute symbol does not exist).\n *\n * The `undefined` vs `\"\"` distinction is load-bearing: e.g. `ordered_edges`\n * treats a non-NULL (incl. empty) value as \"graph ordering present, do not\n * recurse into subgraphs\". A subgraph-scoped `ordering=out` therefore yields\n * `\"\"` at the root, so the root no-ops it exactly as C does.\n * @see lib/cgraph/attr.c:agxget, lib/common/utils.c:late_string\n */\nexport function agGraphAttr(g: Graph, name: string): string | undefined {\n const own = g.attrs.get(name);\n if (own !== undefined) return own;\n return g.root.declaredGraphAttrs.has(name) ? '' : undefined;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agedgeinfo_t from lib/common/types.h.\n *\n * All ED_* accessor macros in the C source expand to typed reads/writes into\n * this struct. In TypeScript the macro indirection is unnecessary \u2014 consumers\n * access fields directly on the EdgeInfo object attached to each Edge.\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/types.h:ED_* macros\n */\n\nimport type { Port, Spline } from './geom.js';\nimport type { Edge } from './edge.js';\nimport type { TextlabelT } from '../common/types.js';\nimport type { ArrowDrawOp } from '../common/arrows-types.js';\n\n/**\n * Edge layout and rendering info record; replaces the ED_* macro family.\n *\n * Required fields are always initialized when an edge is created (matching C\n * zero-init of the port struct in common_init_edge). All other fields are\n * optional; they are populated by specific phases of the layout pipeline.\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/utils.c:common_init_edge\n */\nexport interface EdgeInfo {\n /**\n * Tail-end port specification. Always initialized on edge creation.\n * @see lib/common/types.h:Agedgeinfo_t.tail_port\n * @see lib/common/types.h:ED_tail_port\n */\n tail_port: Port;\n\n /**\n * Head-end port specification. Always initialized on edge creation.\n * @see lib/common/types.h:Agedgeinfo_t.head_port\n * @see lib/common/types.h:ED_head_port\n */\n head_port: Port;\n\n /**\n * Rendered spline geometry. Undefined before the spline routing phase.\n * Set by clip_and_install() in lib/common/splines.c. Do not read before\n * the layout engine's spline pass has completed.\n *\n * @see lib/common/types.h:Agedgeinfo_t.spl\n * @see lib/common/types.h:ED_spl\n * @see lib/common/splines.c:clip_and_install\n */\n spl?: Spline;\n\n // -------------------------------------------------------------------------\n // Labels \u2014 AD6: typed as TextlabelT (was unknown until TextLabel was ported).\n // -------------------------------------------------------------------------\n\n /**\n * Center edge label.\n * @see lib/common/types.h:Agedgeinfo_t.label\n * @see lib/common/types.h:ED_label\n */\n label?: TextlabelT;\n\n /**\n * Head-end label (arrowhead side).\n * @see lib/common/types.h:Agedgeinfo_t.head_label\n * @see lib/common/types.h:ED_head_label\n */\n head_label?: TextlabelT;\n\n /**\n * Tail-end label (arrowtail side).\n * @see lib/common/types.h:Agedgeinfo_t.tail_label\n * @see lib/common/types.h:ED_tail_label\n */\n tail_label?: TextlabelT;\n\n /**\n * External label (placed outside the edge spline).\n * @see lib/common/types.h:Agedgeinfo_t.xlabel\n * @see lib/common/types.h:ED_xlabel\n */\n xlabel?: TextlabelT;\n\n // -------------------------------------------------------------------------\n // Arrowhead draw-ops (ADR-1). The C port regenerates arrowheads at render\n // time from the rotated spline endpoint; this TS port precomputes the typed\n // primitive list during routing (per arrowhead/arrowtail type) and rotates\n // it in postproc. Replaces the loose `_arrowPts`/`_tailArrowPts` Point[].\n // -------------------------------------------------------------------------\n\n /** Head-end arrow primitives (arrowhead). @see lib/common/arrows.c:arrow_gen */\n headArrowOps?: ArrowDrawOp[];\n\n /** Tail-end arrow primitives (arrowtail). @see lib/common/arrows.c:arrow_gen */\n tailArrowOps?: ArrowDrawOp[];\n\n // -------------------------------------------------------------------------\n // Edge metadata\n // -------------------------------------------------------------------------\n\n /**\n * Whether this edge participates in rank constraints.\n * Corresponds to the `constraint` edge attribute (default: true).\n * When false, the edge does not constrain rank assignment.\n * @see lib/dotgen/class1.c:nonconstraint_edge\n */\n constraint?: boolean;\n\n /**\n * Edge type code: REGULAREDGE / FLATEDGE / SELFEDGE.\n * Stored as char in C; mapped to number here.\n * @see lib/common/types.h:Agedgeinfo_t.edge_type\n * @see lib/common/types.h:ED_edge_type\n */\n edge_type?: number;\n\n /**\n * True for compound edges (cross-cluster).\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.compound\n * @see lib/common/types.h:ED_compound\n */\n compound?: number;\n\n /**\n * True for flat edges whose tail and head are adjacent in rank.\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.adjacent\n * @see lib/common/types.h:ED_adjacent\n */\n adjacent?: number;\n\n /**\n * Whether the edge label is placed above (on top of) the spline.\n * Stored as char in C.\n * @see lib/common/types.h:Agedgeinfo_t.label_ontop\n * @see lib/common/types.h:ED_label_ontop\n */\n label_ontop?: number;\n\n /**\n * GUI state flags (bitwise OR of GUI_STATE_* constants).\n * Stored as unsigned char in C.\n * @see lib/common/types.h:Agedgeinfo_t.gui_state\n * @see lib/common/types.h:ED_gui_state\n * @see lib/common/types.h:GUI_STATE_ACTIVE / GUI_STATE_SELECTED / GUI_STATE_VISITED / GUI_STATE_DELETED\n */\n gui_state?: number;\n\n // -------------------------------------------------------------------------\n // Virtual edge back-pointers (dot layout)\n // -------------------------------------------------------------------------\n\n /**\n * Back-pointer to the original (non-virtual) edge; set on virtual edges\n * created during dot layout for long-range connections.\n * @see lib/common/types.h:Agedgeinfo_t.to_orig\n * @see lib/common/types.h:ED_to_orig\n * @see lib/common/shapes.c\n */\n to_orig?: Edge;\n\n /**\n * Back-pointer to the virtual edge that represents this edge in the\n * rank-spanning chain; set on real edges when a virtual chain exists.\n * @see lib/common/types.h:Agedgeinfo_t.to_virt\n * @see lib/common/types.h:ED_to_virt\n */\n to_virt?: Edge;\n\n /**\n * Routing attempted and routesplines failed: the edge is LOST exactly as in\n * C (no spline installed, map_edge warns, emit skips). Port-only bookkeeping\n * so the routeDotEdges backstop does not re-attempt an edge C routes once.\n * @see lib/common/routespl.c:routesplines (NULL), lib/common/postproc.c:map_edge\n */\n lost?: boolean;\n\n // -------------------------------------------------------------------------\n // Engine scratch\n // -------------------------------------------------------------------------\n\n /**\n * Layout-engine algorithm scratch pointer. Engine-specific; not typed here.\n * @see lib/common/types.h:Agedgeinfo_t.alg\n * @see lib/common/types.h:ED_alg\n */\n alg?: unknown;\n\n // -------------------------------------------------------------------------\n // dot-specific fields (#ifndef NEATO_ONLY in C)\n // -------------------------------------------------------------------------\n\n /**\n * Edge weight for network simplex. Higher weight means the edge length is\n * penalized more, biasing ranks toward short edges.\n * int in C.\n * @see lib/common/types.h:Agedgeinfo_t.weight\n * @see lib/common/types.h:ED_weight\n */\n weight?: number;\n\n /**\n * Minimum rank separation required between tail and head nodes.\n * int in C.\n * @see lib/common/types.h:Agedgeinfo_t.minlen\n * @see lib/common/types.h:ED_minlen\n */\n minlen?: number;\n\n /**\n * Network-simplex cut value; used during feasible-tree construction.\n * @see lib/common/types.h:Agedgeinfo_t.cutvalue\n * @see lib/common/types.h:ED_cutvalue\n */\n cutvalue?: number;\n\n /**\n * Index into the network-simplex spanning tree edge list.\n * @see lib/common/types.h:Agedgeinfo_t.tree_index\n * @see lib/common/types.h:ED_tree_index\n */\n tree_index?: number;\n\n /**\n * Crossing penalty multiplier for this edge.\n * short in C.\n * @see lib/common/types.h:Agedgeinfo_t.xpenalty\n * @see lib/common/types.h:ED_xpenalty\n */\n xpenalty?: number;\n\n /**\n * Multiplicity count \u2014 number of parallel edges merged into this one\n * during concentration.\n * short in C.\n * @see lib/common/types.h:Agedgeinfo_t.count\n * @see lib/common/types.h:ED_count\n */\n count?: number;\n\n /**\n * Concentrated-opposite flag; set when merging parallel edges from\n * opposing directions.\n * @see lib/common/types.h:Agedgeinfo_t.conc_opp_flag\n * @see lib/common/types.h:ED_conc_opp_flag\n */\n conc_opp_flag?: boolean;\n\n /**\n * Debug: render intermediate routing boxes.\n * unsigned char in C.\n * @see lib/common/types.h:Agedgeinfo_t.showboxes\n * @see lib/common/types.h:ED_showboxes\n */\n showboxes?: number;\n\n /**\n * True for edges created inside the flat-adjacent auxiliary clone graph\n * (make_flat_adj_edges). Models C's setState window: cloneGraph re-declares\n * the aux attribute dictionary via agnxtattr (NAME-sorted), so aux ids are\n * alphabetical ranks while the global symbols E_arrowsz/E_penwidth \u2014 which\n * setState does NOT remap \u2014 keep main-graph declaration-order ids. Inside\n * the aux, arrow_length's agxget(e, E_arrowsz) therefore mis-indexes into\n * an unrelated attribute (e.g. #1949: id 1 = `color` \u2192 \"black\"/\"blue\"),\n * strtod fails, and late_double falls back to 1.0 for both arrowsize and\n * penwidth. The arrow POLYGON is unaffected (C regenerates it at emit time\n * from the original edge). Load-bearing C quirk; consumed by the arrow\n * clip-length computation only.\n * @see lib/dotgen/dotsplines.c:setState (E_arrowsz/E_penwidth not remapped)\n * @see lib/common/arrows.c:arrow_length (late_double(e, E_arrowsz, 1.0, 0.0))\n */\n stale_arrow_attrs?: boolean;\n\n // -------------------------------------------------------------------------\n // neato/fdp-specific fields (#ifndef DOT_ONLY in C)\n // -------------------------------------------------------------------------\n\n /**\n * Spring factor (ideal edge length multiplier) for spring-model layout.\n * double in C.\n * @see lib/common/types.h:Agedgeinfo_t.factor\n * @see lib/common/types.h:ED_factor\n */\n factor?: number;\n\n /**\n * Ideal edge length for neato/fdp stress-model layout.\n * double in C.\n * @see lib/common/types.h:Agedgeinfo_t.dist\n * @see lib/common/types.h:ED_dist\n */\n dist?: number;\n\n /**\n * Path used during spline routing (Ppolyline_t in C).\n * Typed in Batch 4 when pathplan types are ported.\n * @see lib/common/types.h:Agedgeinfo_t.path\n * @see lib/common/types.h:ED_path\n */\n path?: unknown; // ED_path \u2014 Ppolyline_t in C\n\n\n // -------------------------------------------------------------------------\n // samehead / sametail \u2014 group id for port merging (sameport.c)\n // -------------------------------------------------------------------------\n\n /**\n * Group id for samehead port merging. Edges sharing this value at the same\n * head node will be merged onto a single port by dot_sameports().\n * Corresponds to agxget(e, E_samehead) in the C source.\n * @see lib/dotgen/sameport.c:dot_sameports\n */\n samehead?: string;\n\n /**\n * Group id for sametail port merging. Edges sharing this value at the same\n * tail node will be merged onto a single port by dot_sameports().\n * Corresponds to agxget(e, E_sametail) in the C source.\n * @see lib/dotgen/sameport.c:dot_sameports\n */\n sametail?: string;\n\n // -------------------------------------------------------------------------\n // Compound edge cluster clipping (dot layout)\n // -------------------------------------------------------------------------\n\n /**\n * Name of the cluster subgraph to clip the head of this edge against.\n * When set, the spline endpoint at the head is clipped to the cluster's\n * bounding box by dot_compoundEdges(). Corresponds to agget(e, \"lhead\").\n * @see lib/dotgen/compound.c:makeCompoundEdge\n */\n lhead?: string;\n\n /**\n * Name of the cluster subgraph to clip the tail of this edge against.\n * When set, the spline start at the tail is clipped to the cluster's\n * bounding box by dot_compoundEdges(). Corresponds to agget(e, \"ltail\").\n * @see lib/dotgen/compound.c:makeCompoundEdge\n */\n ltail?: string;\n\n // -------------------------------------------------------------------------\n // dot layout tracking (TypeScript-only, no C equivalent field)\n // -------------------------------------------------------------------------\n\n /**\n * True if this edge was reversed by acyclic() during cycle-breaking.\n * Set by reverseEdge() in src/layout/dot/fastgr.ts. Used by dot_splines\n * to swap bezier control-point order back to tail\u2192head orientation.\n *\n * There is no corresponding C field; C detects reversal via rank comparison\n * in swap_ends_p() (lib/dotgen/dotsplines.c). The TypeScript port adds this\n * field for explicit tracking.\n *\n * @see lib/dotgen/dotsplines.c:swap_ends_p\n */\n reversed?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a zero-initialized Port matching C's zero-init of the port struct\n * in common_init_edge.\n *\n * @see lib/common/types.h:struct port\n * @see lib/common/utils.c:common_init_edge\n */\nexport function makePort(): Port {\n return {\n p: { x: 0, y: 0 },\n theta: 0,\n bp: null,\n defined: false,\n constrained: false,\n // C's default Center port has clip = true; edges clip to the node\n // boundary unless head/tailclip disables it.\n // @see lib/common/shapes.c:Center\n clip: true,\n dyna: false,\n order: 0,\n side: 0,\n name: null,\n };\n}\n\n/**\n * Constructs a minimal EdgeInfo with required port fields initialized.\n *\n * Both ports are required \u2014 tail_port and head_port are always initialized\n * when an edge is created (never null in the C model).\n *\n * @see lib/common/types.h:Agedgeinfo_t\n * @see lib/common/utils.c:common_init_edge\n */\nexport function makeEdgeInfo(tailPort: Port, headPort: Port): EdgeInfo {\n // C agbindrec calloc-zeroes the WHOLE Agedgeinfo_t struct, so every ED_*\n // field exists from creation at a fixed offset. Mirror that (same treatment\n // as makeNodeInfo): pre-declare EVERY optional field as value-preserving\n // `undefined` to pin a single V8 hidden class for all EdgeInfo objects.\n // Without this, fields added lazily during layout (weight/minlen/cutvalue/\n // tree_index/edge_type/to_virt/...) drove EdgeInfo into dictionary (slow)\n // mode \u2014 the dominant per-op cost of the network-simplex enter-edge scan\n // (dfsEnterInedge ~52% of 2371's profile), which reads tree_index, cutvalue\n // and minlen per candidate edge. No code reads edge.info via `in`/\n // Object.keys/for-in; the `{...e.info}` spreads copy identical key sets, so\n // adding the keys is behavior-free. Keep in sync with EdgeInfo.\n return {\n tail_port: tailPort,\n head_port: headPort,\n // --- lazily-assigned fields, pre-declared to lock the hidden class ---\n spl: undefined, label: undefined, head_label: undefined, tail_label: undefined,\n xlabel: undefined, headArrowOps: undefined, tailArrowOps: undefined,\n constraint: undefined, edge_type: undefined, compound: undefined,\n adjacent: undefined, label_ontop: undefined, gui_state: undefined,\n to_orig: undefined, to_virt: undefined, lost: undefined, alg: undefined,\n weight: undefined, minlen: undefined, cutvalue: undefined, tree_index: undefined,\n xpenalty: undefined, count: undefined, conc_opp_flag: undefined,\n showboxes: undefined, stale_arrow_attrs: undefined,\n factor: undefined, dist: undefined, path: undefined,\n samehead: undefined, sametail: undefined, lhead: undefined, ltail: undefined,\n reversed: undefined,\n };\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * TypeScript port of Agedge_t / Agedgepair_t from lib/cgraph/cgraph.h.\n *\n * @see lib/cgraph/cgraph.h:Agedge_s\n * @see lib/cgraph/cgraph.h:Agedgepair_s\n */\n\nimport type { Node } from './node.js';\nimport { type EdgeInfo, makeEdgeInfo, makePort } from './edgeInfo.js';\n\n/**\n * Represents an abstract edge in the graph, corresponding to Agedgepair_t.\n *\n * IMPORTANT: In Agedgepair_t, the \"out\" half stores the HEAD node and the\n * \"in\" half stores the TAIL node. This is counterintuitive but matches C:\n * out.node == head, in.node == tail\n * See lib/cgraph/cgraph.h Agedgepair_t and the AGTAIL/AGHEAD macro definitions.\n *\n * From cgraph.md \"Edge Direction Semantics\":\n * - tail: source node (arrow comes FROM); AGTAIL(e) = AGMKIN(e)->node = in.node\n * - head: destination node (arrow goes TO); AGHEAD(e) = AGMKOUT(e)->node = out.node\n * - out-edge: stored in tail node's out-edge set; out.node == head\n * - in-edge: stored in head node's in-edge set; in.node == tail\n *\n * The `info` field replaces the agbindrec / ED_* mechanism (AD-1).\n *\n * @see lib/cgraph/cgraph.h:Agedgepair_s\n * @see lib/cgraph/cgraph.h:AGTAIL\n * @see lib/cgraph/cgraph.h:AGHEAD\n * @see lib/cgraph/cgraph.h:AGMKIN\n * @see lib/cgraph/cgraph.h:AGMKOUT\n */\nexport class Edge {\n // Code review: process-wide static seq counter, never reset between renders\n // (C's AGSEQ lives in a per-agopen Agclos_t). Safe today \u2014 every reader only\n // compares .seq values within one graph's own edges for relative ordering \u2014\n // and it evades the module-globals fitness scan (a private static field, not\n // a module-scope `let`). Revisit if any consumer serializes an absolute .seq\n // or relies on the counter resetting per parse/createGraph.\n /** @see lib/cgraph/cgraph.h:AGSEQ */\n private static _nextSeq = 0;\n\n /**\n * Monotone sequence number \u2014 equivalent to AGSEQ(e) in C.\n * Used by edgeidcmpf for stable sort ordering.\n *\n * Mutable because C's new_virtual_edge overwrites a virtual edge's AGSEQ\n * with AGSEQ(orig) after allocation; copyVirtualEdgeInfo mirrors that.\n * @see lib/cgraph/cgraph.h:AGSEQ, lib/dotgen/fastgr.c:new_virtual_edge\n */\n seq: number;\n\n /**\n * Per-graph creation-order index (1-based).\n * Set by the parser after root.edges.push(). Used as the SVG edge id,\n * matching C's AGSEQ-based edge id assignment.\n */\n graphSeq: number = 0;\n\n /**\n * Source node (arrow origin).\n * C: AGTAIL(e) = AGMKIN(e)->node = in.node\n * @see lib/cgraph/cgraph.h:AGTAIL\n */\n readonly tail: Node;\n\n /**\n * Destination node (arrow target).\n * C: AGHEAD(e) = AGMKOUT(e)->node = out.node\n * @see lib/cgraph/cgraph.h:AGHEAD\n */\n readonly head: Node;\n\n /**\n * Edge key; empty string for anonymous edges.\n * Corresponds to the `name` argument of agedge().\n * @see lib/cgraph/cgraph.h:agedge\n */\n readonly name: string;\n\n /**\n * String attributes (agget/agset equivalents).\n * @see lib/cgraph/cgraph.h:Agattr_s\n */\n attrs: Map<string, string>;\n\n /**\n * Layout-engine info; replaces ED_* macros via agbindrec (AD-1).\n * @see lib/cgraph/cgraph.h:agbindrec\n */\n info: EdgeInfo;\n\n /** @see lib/cgraph/edge.c:agedge */\n constructor(tail: Node, head: Node, name: string) {\n this.seq = Edge._nextSeq++;\n this.tail = tail;\n this.head = head;\n this.name = name;\n this.attrs = new Map();\n this.info = makeEdgeInfo(makePort(), makePort());\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Safe edge-creation helper \u2014 the `agedge` equivalent for programmatic graph\n * construction. Mirrors the parser's dual-list insertion and adds the\n * strict-graph deduplication mandated by cgraph.\n *\n * @see lib/cgraph/edge.c:agedge\n */\n\nimport type { Graph } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport { Edge } from '../model/edge.js';\n\n/** @see lib/cgraph/cgraph.h:agisundirected */\nfunction isUndirected(g: Graph): boolean {\n const k = g.root.kind;\n return k === 'undirected' || k === 'strict-undirected';\n}\n\n/** @see lib/cgraph/cgraph.h:agisstrict */\nfunction isStrict(g: Graph): boolean {\n const k = g.root.kind;\n return k === 'strict-directed' || k === 'strict-undirected';\n}\n\n/**\n * Probe root.edges for an existing (tail,head) pair.\n * For undirected graphs, (h,t) matches (t,h) \u2014 mirrors the C wildcard\n * key probe in ok_to_make_edge / agedge pre-creation check.\n * @see lib/cgraph/edge.c:ok_to_make_edge\n */\nfunction findExistingEdge(\n root: Graph,\n tail: Node,\n head: Node,\n undirected: boolean,\n): Edge | null {\n for (const e of root.edges) {\n if (e.tail === tail && e.head === head) return e;\n if (undirected && e.tail === head && e.head === tail) return e;\n }\n return null;\n}\n\n/**\n * Insert edge into root.edges and walk the subgraph chain toward root,\n * adding the edge (and its endpoint nodes) to every enclosing graph.\n * Mirrors processEdgePair's enclosing-graph loop and installedge.\n * @see lib/cgraph/edge.c:installedge\n * @see src/parser/builder.ts:238-246\n */\nfunction insertEdge(g: Graph, root: Graph, edge: Edge): void {\n root.edges.push(edge);\n edge.graphSeq = root.edges.length;\n for (let cur: Graph | null = g; cur !== null && cur !== root; cur = cur.parent) {\n cur.nodes.set(edge.tail.name, edge.tail);\n cur.nodes.set(edge.head.name, edge.head);\n cur.edges.push(edge);\n }\n}\n\n/**\n * Create and insert an edge from `tail` to `head` in graph `g`.\n *\n * In a strict graph, at most one edge between any (tail, head) pair exists\n * (symmetric for undirected). When a match is found the existing edge is\n * returned without modification. This matches C `agedge` with `cflag=1`.\n *\n * Most callers building a graph programmatically should prefer\n * `createGraph().addEdge(...)` (the `GvGraphBuilder` method), which accepts\n * node names or handles. Use this lower-level `addEdge` when you already\n * hold `Graph`/`Node` references \u2014 e.g. edges added onto a graph returned by\n * `parse()`.\n *\n * @param g - Owning graph or subgraph; root derived via `g.root`.\n * @param tail - Source node (AGTAIL). @see lib/cgraph/cgraph.h:AGTAIL\n * @param head - Destination node (AGHEAD). @see lib/cgraph/cgraph.h:AGHEAD\n * @param name - Edge key; defaults to empty string for anonymous edges.\n * Ignored for strict-graph dedup (wildcard match).\n * @returns The new (or existing, for strict graphs) edge.\n *\n * @example\n * ```ts\n * import { parse, addEdge } from '@knowvah/dot-engine';\n *\n * const g = parse('digraph { a; b; }');\n * const a = g.nodes.get('a')!;\n * const b = g.nodes.get('b')!;\n * const edge = addEdge(g, a, b, 'ab1');\n * // edge.tail === a, edge.head === b\n * ```\n *\n * @see lib/cgraph/edge.c:agedge\n */\nexport function addEdge(\n g: Graph,\n tail: Node,\n head: Node,\n name?: string,\n): Edge {\n const root = g.root;\n const undirected = isUndirected(g);\n\n if (isStrict(g)) {\n const existing = findExistingEdge(root, tail, head, undirected);\n if (existing !== null) return existing;\n }\n\n const edge = new Edge(tail, head, name ?? '');\n insertEdge(g, root, edge);\n return edge;\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Public structured-error contract for @knowvah/dot-engine.\n *\n * Consumers branch on the stable `code` / `type` fields; the\n * `code -> friendlyMessage` map is the single seam a future i18n library\n * replaces. This module is a runtime leaf: the only project import is the\n * type-only `Expectation`, which is erased at compile time.\n *\n * @see plans/structured-errors/decisions.md\n */\n\nimport type { Expectation } from './parser/dot.js';\n\n/** Stable public alias of peggy's expectation union (SYNTAX_* errors only). */\nexport type GvExpectation = Expectation;\n\n/** Coarse classification of where an error originated. */\nexport type GvErrorType = 'syntax' | 'semantic' | 'render';\n\n/** Closed union of stable error codes \u2014 each is an i18n key. */\nexport type GvErrorCode =\n | 'SYNTAX_ERROR' // peggy parse failure (token found)\n | 'SYNTAX_UNEXPECTED_EOF' // peggy parse failure, found === null\n | 'EDGE_OP_DIRECTED_IN_UNDIRECTED' // '->' used in an undirected graph\n | 'EDGE_OP_UNDIRECTED_IN_DIRECTED' // '--' used in a digraph\n | 'HTML_PARSE_ERROR' // HTML-like label parse failure\n | 'RENDER_ERROR' // known layout/render-stage failure\n | 'GENERIC_ERROR'; // catch-all fallback\n\n/** Structured error contract shared by every error source. */\nexport interface GvError {\n type: GvErrorType;\n /** Stable i18n key. */\n code: GvErrorCode;\n /** Concise technical text we own (may diverge from C). */\n message: string;\n /** Approachable, non-localized English (delivery). */\n friendlyMessage: string;\n /** Real error position; the highest-value field. */\n location?: { line: number; column: number; offset?: number };\n /** Peggy's discriminated union, passed through unmapped; SYNTAX_* only. */\n expected?: GvExpectation[];\n}\n\n/** Result of a result-style render: `svg` XOR `errors` for v1. */\nexport interface RenderResult {\n /** Present on success. */\n svg?: string;\n /** Present on failure; length <= 1 (first failure only) for v1. */\n errors?: GvError[];\n}\n\n/**\n * Central `code -> friendlyMessage` map. Non-localized, approachable English.\n * This is the seam a future i18n library replaces.\n */\nexport const FRIENDLY_MESSAGES: Record<GvErrorCode, string> = {\n SYNTAX_ERROR: 'There is a syntax error in the DOT source.',\n SYNTAX_UNEXPECTED_EOF:\n 'The DOT source ended unexpectedly \u2014 a bracket or statement may be unclosed.',\n EDGE_OP_DIRECTED_IN_UNDIRECTED:\n \"A directed edge '->' was used in an undirected graph; use '--' instead.\",\n EDGE_OP_UNDIRECTED_IN_DIRECTED:\n \"An undirected edge '--' was used in a directed graph; use '->' instead.\",\n HTML_PARSE_ERROR: 'An HTML-like label could not be parsed.',\n RENDER_ERROR: 'The graph could not be laid out or rendered.',\n GENERIC_ERROR: 'An unexpected error occurred while rendering the graph.',\n};\n\n/**\n * Look up the approachable English message for a code. The single seam a\n * future i18n library replaces.\n */\nexport function friendlyMessageFor(code: GvErrorCode): string {\n return FRIENDLY_MESSAGES[code];\n}\n\n/**\n * Error thrown for known layout/render-stage failures. Only `RENDER_ERROR`\n * and `GENERIC_ERROR` are valid render-stage codes.\n */\nexport class RenderError extends Error implements GvError {\n readonly type = 'render';\n readonly code: GvErrorCode;\n readonly friendlyMessage: string;\n\n constructor(message: string, code: GvErrorCode = 'RENDER_ERROR') {\n super(message);\n this.name = 'RenderError';\n this.code = code;\n this.friendlyMessage = friendlyMessageFor(code);\n }\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * HTML-string marking. cgraph tags strings created from `label=<...>` via\n * a flag on the refstr (aghtmlstr); this port marks the attribute value\n * with a leading control character that cannot appear in DOT input.\n *\n * @see lib/cgraph/refstr.c:aghtmlstr\n */\n\n/** Marker prefix identifying an attribute value parsed from `<...>`. */\nexport const HTML_STRING_MARK = '\\u0001';\n\n/** True when the attribute value came from an HTML `<...>` literal. */\nexport function isHtmlValue(s: string): boolean {\n return s.startsWith(HTML_STRING_MARK);\n}\n\n/** Strip the HTML marker, returning the markup content. */\nexport function htmlValueContent(s: string): string {\n return s.slice(HTML_STRING_MARK.length);\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Programmatic graph builder \u2014 constructs a Graph model without emitting DOT.\n *\n * Exposes lightweight typed handles (GvNode, GvEdge) that wrap internal\n * model references without leaking the mutable model classes (ADR-1, ADR-6,\n * ADR-8).\n *\n * @see lib/cgraph/graph.c:agopen\n * @see lib/cgraph/node.c:agnode\n * @see lib/cgraph/subg.c:agsubg\n */\n\nimport { Graph, type GraphKind } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport type { Edge } from '../model/edge.js';\nimport { agnode, agsubg, agsubnode } from '../model/cgraph-ops.js';\nimport { addEdge as cgraphAddEdge } from './edge-ops.js';\nimport { RenderError } from '../errors.js';\nimport { HTML_STRING_MARK } from '../common/html-string.js';\n\n// \u2500\u2500 Public interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Options for createGraph. */\nexport interface CreateGraphOptions {\n directed?: boolean;\n strict?: boolean;\n name?: string;\n}\n\n/**\n * Opaque handle for a graph node.\n * @see lib/cgraph/cgraph.h:Agnode_s\n */\nexport interface GvNode {\n readonly name: string;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute to an HTML-string value (e.g. `label` to an HTML-table\n * label). Equivalent to `label=<...>` in DOT text: the value is tagged as\n * HTML so the layout engine measures it as markup, not as literal text.\n * @see lib/cgraph/refstr.c:aghtmlstr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n}\n\n/**\n * Opaque handle for a graph edge.\n * @see lib/cgraph/cgraph.h:Agedge_s\n */\nexport interface GvEdge {\n readonly tail: string;\n readonly head: string;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute to an HTML-string value (e.g. an HTML-table edge label).\n * @see GvNode.setHtmlAttr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n}\n\n/**\n * Builder returned by createGraph. Provides idiomatic programmatic\n * construction of a graph model.\n */\nexport interface GvGraphBuilder {\n addNode(name: string, attrs?: Record<string, string>): GvNode;\n addEdge(\n tail: GvNode | string,\n head: GvNode | string,\n attrs?: Record<string, string>,\n ): GvEdge;\n addSubgraph(name: string, attrs?: Record<string, string>): GvGraphBuilder;\n setAttr(k: string, v: string): void;\n /**\n * Set an attribute on this (sub)graph to an HTML-string value \u2014 the\n * supported way to give a cluster an HTML-table `label` (the header-size\n * reservation the jar drives) without hand-writing the internal marker.\n * @see GvNode.setHtmlAttr\n */\n setHtmlAttr(k: string, v: string): void;\n getAttr(k: string): string | undefined;\n readonly graph: Graph;\n}\n\n// \u2500\u2500 Internal handle implementations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Internal node handle \u2014 wraps Node ref without leaking the class. */\nclass NodeHandle implements GvNode {\n /** @internal */ readonly _node: Node;\n\n constructor(node: Node) {\n this._node = node;\n }\n\n get name(): string {\n return this._node.name;\n }\n\n setAttr(k: string, v: string): void {\n this._node.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._node.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._node.attrs.get(k);\n }\n}\n\n/** Internal edge handle \u2014 wraps Edge ref without leaking the class. */\nclass EdgeHandle implements GvEdge {\n /** @internal */ readonly _edge: Edge;\n\n constructor(edge: Edge) {\n this._edge = edge;\n }\n\n get tail(): string {\n return this._edge.tail.name;\n }\n\n get head(): string {\n return this._edge.head.name;\n }\n\n setAttr(k: string, v: string): void {\n this._edge.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._edge.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._edge.attrs.get(k);\n }\n}\n\n// \u2500\u2500 Builder implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Resolve a GvNode handle or a name string to an internal Node. */\nfunction resolveNode(g: Graph, ref: GvNode | string): Node {\n if (typeof ref === 'string') {\n const node = agnode(g, ref, true);\n if (node === null) {\n throw new RenderError(`Failed to resolve node '${ref}' in graph '${g.name}'`, 'GENERIC_ERROR');\n }\n return node;\n }\n return (ref as NodeHandle)._node;\n}\n\n/** Apply an attrs Record to a Map. */\nfunction applyAttrs(\n map: Map<string, string>,\n attrs: Record<string, string> | undefined,\n): void {\n if (attrs === undefined) return;\n for (const [k, v] of Object.entries(attrs)) {\n map.set(k, v);\n }\n}\n\n/**\n * Builder backed by a Graph + optional subgraph context.\n * Root builder: _context === _graph (the root Graph).\n * Subgraph builder: _context is the subgraph; _graph is always the root.\n */\nclass GraphBuilder implements GvGraphBuilder {\n private readonly _graph: Graph;\n private readonly _context: Graph;\n\n constructor(graph: Graph, context: Graph) {\n this._graph = graph;\n this._context = context;\n }\n\n get graph(): Graph {\n return this._graph;\n }\n\n addNode(name: string, attrs?: Record<string, string>): GvNode {\n return addNodeToContext(this._graph, this._context, name, attrs);\n }\n\n addEdge(\n tail: GvNode | string,\n head: GvNode | string,\n attrs?: Record<string, string>,\n ): GvEdge {\n const tailNode = resolveNode(this._graph, tail);\n const headNode = resolveNode(this._graph, head);\n const edge = cgraphAddEdge(this._graph, tailNode, headNode);\n applyAttrs(edge.attrs, attrs);\n return new EdgeHandle(edge);\n }\n\n addSubgraph(name: string, attrs?: Record<string, string>): GvGraphBuilder {\n const sg = agsubg(this._context, name, true);\n if (sg === null) {\n throw new RenderError(`Failed to create subgraph '${name}'`, 'GENERIC_ERROR');\n }\n applyAttrs(sg.attrs, attrs);\n return new GraphBuilder(this._graph, sg);\n }\n\n setAttr(k: string, v: string): void {\n this._context.attrs.set(k, v);\n }\n\n setHtmlAttr(k: string, v: string): void {\n this._context.attrs.set(k, HTML_STRING_MARK + v);\n }\n\n getAttr(k: string): string | undefined {\n return this._context.attrs.get(k);\n }\n}\n\n/**\n * Create a node in root and, when context is a subgraph, install it there too.\n * Extracted to keep GraphBuilder.addNode below the CCN/length threshold.\n */\nfunction addNodeToContext(\n root: Graph,\n context: Graph,\n name: string,\n attrs: Record<string, string> | undefined,\n): GvNode {\n const node = agnode(root, name, true);\n if (node === null) throw new RenderError(`Failed to create node '${name}'`, 'GENERIC_ERROR');\n applyAttrs(node.attrs, attrs);\n if (context !== root) agsubnode(context, node, true);\n return new NodeHandle(node);\n}\n\n// \u2500\u2500 GraphKind mapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Lookup table: [directed][strict] \u2192 GraphKind.\n * Per parser/builder.ts:272-274 (the canonical mapping in this codebase).\n * @see lib/cgraph/graph.c:Agdirected / Agstrictdirected / Agundirected / Agstrictundirected\n */\nconst KIND_TABLE: Record<string, GraphKind> = {\n 'true:true': 'strict-directed',\n 'true:false': 'directed',\n 'false:true': 'strict-undirected',\n 'false:false': 'undirected',\n};\n\n/** Derive GraphKind from CreateGraphOptions. */\nfunction resolveKind(opts: CreateGraphOptions | undefined): GraphKind {\n const d = String(opts?.directed ?? true);\n const s = String(opts?.strict ?? false);\n return KIND_TABLE[`${d}:${s}`] ?? 'directed';\n}\n\n// \u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Create a new programmatic graph builder.\n *\n * The builder's `.graph` is a fresh Graph ready for handoff to layout/render.\n * Defaults: directed=true, strict=false, name=''.\n *\n * @see lib/cgraph/graph.c:agopen\n */\nexport function createGraph(opts?: CreateGraphOptions): GvGraphBuilder {\n const g = new Graph(opts?.name ?? '', resolveKind(opts));\n return new GraphBuilder(g, g);\n}\n", "// SPDX-License-Identifier: EPL-2.0\n\n/**\n * Geometry snapshot API \u2014 reads computed layout geometry from an\n * internal Graph after ctx.layout() has run, and returns a plain,\n * JSON-serializable snapshot.\n *\n * Coordinate systems\n * ------------------\n * Native graphviz coordinates are y-up (origin at lower-left).\n * Most screen consumers want y-down (origin at upper-left).\n *\n * With the default `yAxis: 'down'`, every y coordinate is flipped:\n * y' = bbHeight - y\n * where bbHeight = graph bb.ur.y - graph bb.ll.y.\n * The `bounds` origin is normalised to (0, 0) at the top-left corner.\n *\n * With `yAxis: 'up'`, values are returned unchanged (native y-up frame).\n *\n * Units\n * -----\n * The internal model stores node `width` and `height` in inches\n * (matching C ND_width / ND_height in lib/common/types.h).\n * `NodeGeometry.width` and `NodeGeometry.height` are converted to\n * **points** (1 inch = 72 points) before being returned.\n *\n * All other coordinates (x, y, bbox dimensions, spline points,\n * label positions) are in the native graphviz point unit.\n *\n * @see lib/common/types.h\n */\n\nimport type { Graph } from '../model/graph.js';\nimport type { Node } from '../model/node.js';\nimport type { Edge } from '../model/edge.js';\nimport type { TextlabelT } from '../common/types.js';\nimport { RenderError } from '../errors.js';\n\n// ---------------------------------------------------------------------------\n// Public coordinate types (canonical home \u2014 T5 imports GeometryOptions here)\n// ---------------------------------------------------------------------------\n\n/** Coordinate system for returned geometry. */\nexport type YAxis = 'up' | 'down';\n\n/**\n * Options for {@link getLayout}.\n *\n * @property yAxis - Coordinate direction. Default `'down'` (origin top-left,\n * y increases downward \u2014 screen convention). Use `'up'` to get native\n * graphviz coordinates (origin bottom-left, y increases upward).\n */\nexport type GeometryOptions = { yAxis?: YAxis };\n\n/**\n * Overall bounding box of the graph, in points.\n *\n * With `yAxis:'down'`, x and y are 0 (normalised to top-left origin)\n * and width/height are the natural dimensions.\n * With `yAxis:'up'`, x and y match the raw lower-left corner of the\n * graph bounding box.\n */\nexport interface BoundsGeometry {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Per-node geometry extracted after layout, in points.\n *\n * `x` and `y` are the node centre coordinates.\n * `width` and `height` are in **points** (converted from the inches\n * stored on the model: `NodeInfo.width * 72`, `NodeInfo.height * 72`).\n *\n * @see lib/common/types.h:ND_coord, ND_width, ND_height\n */\nexport interface NodeGeometry {\n name: string;\n x: number;\n y: number;\n /**\n * Node width in **points** (model stores inches; multiplied by 72 here).\n * @see lib/common/types.h:ND_width\n */\n width: number;\n /**\n * Node height in **points** (model stores inches; multiplied by 72 here).\n * @see lib/common/types.h:ND_height\n */\n height: number;\n}\n\n/**\n * Per-edge geometry extracted after spline routing, in points.\n *\n * `points` concatenates all bezier control points from the edge spline,\n * in order. An edge with no routed spline produces an empty `points` array.\n * `label` is present only when the edge carries a centre label.\n *\n * `tailLabel`/`headLabel` are the `taillabel`/`headlabel` port labels. They\n * are present only once the layout has actually *placed* them (C `lp->set`),\n * which is the same gate `render()` applies before emitting the `<text>`:\n * a declared port label that place_portlabel skipped (no spline, IGNORED edge\n * type) still holds calloc-zero coordinates, so it is reported as absent\n * rather than as a label at the origin.\n *\n * `sp`/`ep` are the arrow attachment points. When an end carries an arrow the\n * spline is shortened to leave room for it, and the arrow spans from the\n * terminal control point out to this point \u2014 so a consumer drawing its own\n * arrowheads reads the tip here instead of extrapolating one. Each is present\n * only when that end actually has an arrow (C `sflag`/`eflag`); with no arrow\n * the field holds the calloc-zero point, which is not geometry.\n *\n * These are the *attachment* points on the node boundary, verbatim from the\n * bezier \u2014 with `arrowhead=none` the spline simply ends there. Graphviz's own\n * renderer insets the arrow polygon it draws by a penwidth-dependent amount\n * (measured: ~1.5pt at `penwidth=1`, ~6.2pt at `penwidth=5`), so `ep` is the\n * point to draw an arrow *to*, not a copy of the rendered polygon's tip.\n *\n * @see lib/common/types.h:ED_spl, ED_label, ED_tail_label, ED_head_label\n * @see lib/common/types.h:bezier (sflag/eflag, sp/ep)\n */\nexport interface EdgeGeometry {\n tail: string;\n head: string;\n /** Bezier control points for the edge spline, in points. */\n points: { x: number; y: number }[];\n /**\n * Arrow attachment point at the tail end, if that end carries an arrow.\n * @see lib/common/types.h:bezier.sp\n */\n sp?: { x: number; y: number };\n /**\n * Arrow attachment point at the head end, if that end carries an arrow.\n * @see lib/common/types.h:bezier.ep\n */\n ep?: { x: number; y: number };\n /** Centre edge label position, if present. @see lib/common/types.h:ED_label */\n label?: { x: number; y: number };\n /**\n * `taillabel` position, if placed.\n * @see lib/common/types.h:ED_tail_label\n */\n tailLabel?: { x: number; y: number };\n /**\n * `headlabel` position, if placed.\n * @see lib/common/types.h:ED_head_label\n */\n headLabel?: { x: number; y: number };\n}\n\n/**\n * Per-cluster geometry extracted after layout, in points.\n *\n * `name` is the cluster subgraph's name (e.g. `cluster6`); for nested\n * clusters the name encodes the hierarchy, so no explicit parent link is\n * exposed. `x`/`y`/`width`/`height` describe the cluster's bounding box,\n * following the same frame convention as {@link BoundsGeometry}: with\n * `yAxis:'down'` (x, y) is the top-left corner; with `yAxis:'up'` (x, y) is\n * the lower-left corner (native graphviz frame). These are the raw box\n * corners graphviz computed \u2014 the same values `render()` rounds to emit the\n * `class=\"cluster\"` polygon, so a consumer quantizing to SVG precision gets\n * byte-conformant geometry.\n *\n * @see lib/common/types.h:GD_bb (of a cluster subgraph)\n */\nexport interface ClusterGeometry {\n /** Cluster subgraph name (e.g. `cluster6`); encodes nesting. */\n name: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Plain, JSON-serializable snapshot of the graph's computed geometry.\n *\n * `clusters` lists every cluster subgraph (recursively, nested clusters each\n * get their own entry) with a computed bounding box; it is empty for graphs\n * without clusters.\n *\n * @see lib/common/types.h:GD_bb, ND_coord, ED_spl, GD_clust\n */\nexport interface LayoutSnapshot {\n bounds: BoundsGeometry;\n nodes: NodeGeometry[];\n edges: EdgeGeometry[];\n clusters: ClusterGeometry[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants\n// ---------------------------------------------------------------------------\n\n/** Points per inch \u2014 matches graphviz's DPI constant. @see lib/common/geom.h */\nconst INCHES_TO_POINTS = 72;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Build a y-flip function bound to a specific graph bounding-box height. */\nfunction makeFlipY(bbHeight: number, yAxis: YAxis): (y: number) => number {\n if (yAxis === 'up') return (y) => y;\n return (y) => bbHeight - y;\n}\n\n/**\n * Snapshot one node's geometry.\n * @see lib/common/types.h:ND_coord, ND_width, ND_height\n */\nfunction snapshotNode(node: Node, flipY: (y: number) => number): NodeGeometry {\n const coord = node.info.coord;\n return {\n name: node.name,\n x: coord.x,\n y: flipY(coord.y),\n width: node.info.width * INCHES_TO_POINTS,\n height: node.info.height * INCHES_TO_POINTS,\n };\n}\n\n/**\n * Collect bezier control points from an edge's spline.\n * Uses `.size` (not `.list.length`) because C over-allocates `list`\n * and `size` holds the actual count after clip_and_install.\n * @see lib/common/splines.c:clip_and_install\n */\nfunction collectEdgePoints(\n edge: Edge,\n flipY: (y: number) => number,\n): { x: number; y: number }[] {\n const spl = edge.info.spl;\n if (spl === undefined) return [];\n const pts: { x: number; y: number }[] = [];\n for (const bz of spl.list) {\n for (let k = 0; k < bz.size; k++) {\n const pt = bz.list[k];\n pts.push({ x: pt.x, y: flipY(pt.y) });\n }\n }\n return pts;\n}\n\n/**\n * Position of a port label that layout actually placed, else undefined.\n * `set` is C's own \"this label has coordinates\" flag, and the gate\n * emit_edge_label uses before drawing; an unplaced label still holds the\n * calloc-zero pos and must not be published as geometry.\n * @see lib/common/splines.c:place_portlabel (l->set = TRUE)\n * @see lib/common/emit.c:emit_edge_label (lbl == NULL || !lbl->set)\n */\nfunction placedLabelPos(\n lbl: TextlabelT | undefined,\n flipY: (y: number) => number,\n): { x: number; y: number } | undefined {\n if (lbl === undefined || !lbl.set) return undefined;\n return { x: lbl.pos.x, y: flipY(lbl.pos.y) };\n}\n\n/**\n * Arrow attachment points, one per end that actually carries an arrow.\n * C keeps `sp`/`ep` beside the control points and gates them on\n * `sflag`/`eflag`; with no arrow at that end the flag is 0 and the point is\n * still calloc-zero, so an ungated read would publish (0, 0) as geometry.\n * Indexes the bezier array by `spl.size` (not `list.length`), matching C.\n * @see lib/common/types.h:bezier\n * @see lib/common/postproc.c:endPoints\n */\nfunction arrowAttachPoints(\n edge: Edge,\n flipY: (y: number) => number,\n): { sp?: { x: number; y: number }; ep?: { x: number; y: number } } {\n const spl = edge.info.spl;\n if (spl === undefined || spl.size === 0) return {};\n const out: { sp?: { x: number; y: number }; ep?: { x: number; y: number } } = {};\n const first = spl.list[0];\n if (first.sflag !== 0) out.sp = { x: first.sp.x, y: flipY(first.sp.y) };\n const last = spl.list[spl.size - 1];\n if (last.eflag !== 0) out.ep = { x: last.ep.x, y: flipY(last.ep.y) };\n return out;\n}\n\n/**\n * Snapshot one edge's geometry.\n * @see lib/common/types.h:ED_spl, ED_label, ED_tail_label, ED_head_label\n * (textlabel_t.pos), bezier.sp/ep\n */\nfunction snapshotEdge(edge: Edge, flipY: (y: number) => number): EdgeGeometry {\n const geom: EdgeGeometry = {\n tail: edge.tail.name,\n head: edge.head.name,\n points: collectEdgePoints(edge, flipY),\n };\n const lbl = edge.info.label;\n if (lbl !== undefined) {\n geom.label = { x: lbl.pos.x, y: flipY(lbl.pos.y) };\n }\n const tailLabel = placedLabelPos(edge.info.tail_label, flipY);\n if (tailLabel !== undefined) geom.tailLabel = tailLabel;\n const headLabel = placedLabelPos(edge.info.head_label, flipY);\n if (headLabel !== undefined) geom.headLabel = headLabel;\n const { sp, ep } = arrowAttachPoints(edge, flipY);\n if (sp !== undefined) geom.sp = sp;\n if (ep !== undefined) geom.ep = ep;\n return geom;\n}\n\n/**\n * Snapshot one cluster's bounding box, in the requested frame.\n *\n * Mirrors the {@link BoundsGeometry} convention: `yAxis:'up'` returns the\n * native lower-left corner (ll); `yAxis:'down'` returns the top-left corner\n * (ll.x, flipped ur.y). `width`/`height` are frame-independent (ur - ll).\n *\n * @see lib/common/types.h:GD_bb (cluster subgraph)\n */\nfunction snapshotCluster(\n sg: Graph, yAxis: YAxis, flipY: (y: number) => number,\n): ClusterGeometry {\n const bb = sg.info.bb;\n return {\n name: sg.name,\n x: bb.ll.x,\n // 'up' keeps the lower-left y; 'down' flips the upper-right y to the\n // top-left of the box (flipY is monotonic-decreasing there).\n y: yAxis === 'up' ? bb.ll.y : flipY(bb.ur.y),\n width: bb.ur.x - bb.ll.x,\n height: bb.ur.y - bb.ll.y,\n };\n}\n\n/**\n * Collect every cluster subgraph (depth-first, nested clusters included).\n * C stores clusters 1-indexed in GD_clust; the TS model exposes a 0-indexed\n * `info.clust` array on each (sub)graph. A cluster without a computed bb\n * (never laid out) is skipped.\n * @see lib/common/types.h:GD_clust, GD_n_cluster\n */\nfunction collectClusters(\n sg: Graph, yAxis: YAxis, flipY: (y: number) => number,\n out: ClusterGeometry[],\n): void {\n for (const c of sg.info.clust ?? []) {\n if (c.info.bb !== undefined) out.push(snapshotCluster(c, yAxis, flipY));\n collectClusters(c, yAxis, flipY, out);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a plain, JSON-serializable snapshot of the computed geometry for\n * all nodes and edges in graph `g`.\n *\n * Must be called **after** `ctx.layout(g, engine)` (or `render`) has run.\n * Before layout the geometry fields hold calloc-zero defaults (every node at\n * the origin, an empty bounding box), so a not-yet-laid-out graph is rejected\n * with a `RenderError` rather than returning that all-zero snapshot as if it\n * were real geometry.\n *\n * @param g - Laid-out graph (internal model; not mutated by this function).\n * @param opts - Coordinate options; defaults to `{ yAxis: 'down' }`.\n * @throws RenderError if `g` has not been laid out.\n *\n * @see lib/common/types.h:GD_bb, ND_coord, ED_spl\n */\nexport function getLayout(g: Graph, opts?: GeometryOptions): LayoutSnapshot {\n if (g.info?.laidOut !== true) {\n throw new RenderError(\n 'getLayout requires a laid-out graph; run ctx.layout(g, engine) or render() first',\n 'GENERIC_ERROR',\n );\n }\n const yAxis: YAxis = opts?.yAxis ?? 'down';\n const bb = g.info.bb;\n const bbWidth = bb.ur.x - bb.ll.x;\n const bbHeight = bb.ur.y - bb.ll.y;\n const flipY = makeFlipY(bbHeight, yAxis);\n\n const bounds: BoundsGeometry = yAxis === 'down'\n ? { x: 0, y: 0, width: bbWidth, height: bbHeight }\n : { x: bb.ll.x, y: bb.ll.y, width: bbWidth, height: bbHeight };\n\n const nodes = Array.from(g.nodes.values()).map((n) => snapshotNode(n, flipY));\n const edges = g.edges.map((e) => snapshotEdge(e, flipY));\n const clusters: ClusterGeometry[] = [];\n collectClusters(g, yAxis, flipY, clusters);\n\n return { bounds, nodes, edges, clusters };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["node"]
7
7
  }