# TypeScript API

Canonical: https://docs.shumoku.dev/en/library/api
Language: en

@shumoku/core · 0.4.0

# TypeScript API

155 public functions generated from TypeScript and TSDoc.

[**`absenceImpliesRetraction`** True when absence in a snapshot should be treated as evidence (retract / mark stale). False when the snapshot 's absence is meaningless because the node was deliberately not asked. This is the \*critical\* anti-footgun call codex flagged: if we post-filter excluded nodes out of a NetBox snapshot whose status is \`ok\` (= absence trusted), the resolver would happily interpret the missing entry as "NetBox says it 's gone". We never want that.](https://docs.shumoku.dev/en/library/api/absenceImpliesRetraction) [**`addHttpWarning`** Add HTTP warning to connection result if URL uses insecure HTTP](https://docs.shumoku.dev/en/library/api/addHttpWarning) [**`addLink`** Add a link between two ports (existing or new). If fromPortId/toPortId refer to existing ports, use them. If toTarget is a nodeId (no port), create a new port on that node. Returns the updated graph with the new link, or null if invalid.](https://docs.shumoku.dev/en/library/api/addLink) [**`addPort`** Add an unnamed port to a node on the specified side. The port is appended to \`Node.ports\` (the data model) and to the resolved-port map (layout). Label stays empty — the renderer hides labelless ports' label rectangle, and the user can name it later from the detail panel.](https://docs.shumoku.dev/en/library/api/addPort) [**`alignPortsToPeers`** No summary.](https://docs.shumoku.dev/en/library/api/alignPortsToPeers) [**`applyOctilinearRoutes`** Mutates eligible edges: sets \`route\` to an octilinear polyline (and mirrors the corner points into \`points\` for labels / hit testing). Returns the number of edges routed.](https://docs.shumoku.dev/en/library/api/applyOctilinearRoutes) [**`applyThemeToCSS`** Apply theme to CSS variables](https://docs.shumoku.dev/en/library/api/applyThemeToCSS) [**`asEntityId`** Trust-boundary cast — DB reads, registry mint, wire-format parse. Use ONLY at: mint, DB row reads, HTTP param boundaries, and tests. If you find yourself casting mid-logic, fix the signature above you instead.](https://docs.shumoku.dev/en/library/api/asEntityId) [**`assertLayoutConstraints`** Standing-fixture gate (engine-v3-migration.md §B4, finally wired): verify after a layout and — when a BLOCKING constraint is violated — throw under \`NODE\_ENV=test\` (CI fixtures pin the guarantee), log an error otherwise. A runtime layout must NEVER fail to render because of the gate: Bun defaults NODE\_ENV to 'development', and throwing there turned a constraint report into "no diagram at all" (the flat-tree path on a 1.5k-node graph violates massively — that's a finding for #481, not a reason to refuse the bake).](https://docs.shumoku.dev/en/library/api/assertLayoutConstraints) [**`attachmentKey`** Stable merge / suppression key for an attachment. \`access\` is keyed per protocol (SNMP and SSH are distinct slots); \`metrics-binding\` is keyed per metrics source (two metrics sources can each bind one element — but ONE binding per (source, element) is the invariant; extend with a role segment if per-metric-role binding is ever needed, don't overload the slot); every other kind keys by its \`kind\`. The resolver merges attachments by this key (highest-priority contribution wins per key) and the human suppresses by it (\`suppressedAttachments\`). One definition so merge and suppression never disagree on what "the same attachment slot" means.](https://docs.shumoku.dev/en/library/api/attachmentKey) [**`autoLayoutFlatTree`** Lay out a network graph using the flat-tree algorithm, using the supplied \`LayoutEngine\` for rules (sizes, gaps, label widths). Equivalent to the legacy \`layoutNetwork(graph, options)\`, but the engine is now a first-class concept. The same engine instance can also serve manual placement (\`engine.tryPlace\`) so layout and drag-snap stay consistent.](https://docs.shumoku.dev/en/library/api/autoLayoutFlatTree) [**`bezierEdgePath`** Build a cubic-Bezier SVG path \`d\` string from one port to another. Each port is described by its absolute (x, y) position and which side of the node it sits on. The path leaves \`from\` along that side's outward normal, sweeps through two symmetric control points, and enters \`to\` along its own side's inward normal. \`lateralOffset\` (optional, on either endpoint) shifts that endpoint \*\*and\*\* its control point perpendicular to the port's outward normal, so the curve still leaves / arrives straight along the normal but starts (or ends) sideways from the port centre. Used by the router to fan multiple edges sharing one port apart visually without rerouting through orthogonal segments. Positive = right of the outward normal in screen coords (y-down). The endpoint \*visibly detaches from the port centre\* by the offset — fine for fan-out decoration but not for tight-tolerance hit testing on the port itself.](https://docs.shumoku.dev/en/library/api/bezierEdgePath) [**`bezierOffsetPath`** \`bezierEdgePath\` translated as a whole by \`offset\` SVG units along the chord-perpendicular direction. Used to draw parallel "in" / "out" lanes for weathermap-style flow overlays. This is a \*parallel translate\*, NOT a true cubic offset curve (which cannot in general be represented exactly as another cubic). All four control points shift by the same vector, so: • lane spacing is exactly \`2 \* |offset|\` everywhere along the curve, • the offset curve never crosses the base curve or the opposite lane regardless of port-side configuration (incl. same-side U-turns and adjacent-side L-turns), • tangents are preserved at every point — but the lane endpoint is shifted off the port by \`(nx, ny)\`. Lanes are decorative; they are not meant to "connect to" the port. The shift direction is the chord normal (perpendicular to b-a). Sign convention: in screen coordinates (y-down), \`+offset\` shifts to the right-hand side of the chord A→B, \`-offset\` to the left. Pass \`+offset\` and \`-offset\` for the two lanes; which one ends up labelled "in" is a data-source semantic, not enforced here. Computed analytically — no DOM measurement, so callers don't need a mounted SVGPathElement.](https://docs.shumoku.dev/en/library/api/bezierOffsetPath) [**`bindingInterfaceName`** The best name to hand the plugin for an interface binding. Identity (\`ifName\`) is the durable key; \`interfaceName\` is the migration / label fallback. The poll-time resolution layer may refine this against the source's live interface list, but a name is always derivable here.](https://docs.shumoku.dev/en/library/api/bindingInterfaceName) [**`bpsToLinkWidth`** Map a raw bits/sec value to the calibrated stroke width.](https://docs.shumoku.dev/en/library/api/bpsToLinkWidth) [**`bpsToLinkWidthMode`** Map bits/sec to stroke width under the given mode. Unknown bandwidth returns 0 (caller decides the fallback), matching \`bpsToLinkWidth\`.](https://docs.shumoku.dev/en/library/api/bpsToLinkWidthMode) [**`buildAlertTitle`** Display title: \`"<alertname> - <host>"\`, or just the name when no host.](https://docs.shumoku.dev/en/library/api/buildAlertTitle) [**`buildChildSheetGraph`** Build a sub-sheet \`NetworkGraph\` for the given subgraph id — the filtered nodes/subgraphs/links plus export-connector nodes that represent cross-boundary links. This is the layout-free half of what \`buildHierarchicalSheets\` does per subgraph. Callers that want to run their own layout engine (e.g. the interactive editor, which already owns a \`computeNetworkLayout\` path) can use this directly and skip the \`LayoutEngine\` indirection. Returns \`null\` when the requested subgraph isn't in \`rootGraph\`.](https://docs.shumoku.dev/en/library/api/buildChildSheetGraph) [**`buildCompositeRoutingPlan`** No summary.](https://docs.shumoku.dev/en/library/api/buildCompositeRoutingPlan) [**`buildHaHullPath`** Build the glasses hull path for one redundancy group. Falls back to a rounded rect over the union bbox when the chain isn't a clean horizontal row (\`fallback: true\`), so callers never have to guard — every group gets SOME hull.](https://docs.shumoku.dev/en/library/api/buildHaHullPath) [**`buildHierarchicalSheets`** Build hierarchical sheets from a root graph Creates child sheets for each subgraph with: - Filtered nodes (only those belonging to the subgraph and descendants) - Internal links (both endpoints in subgraph/descendants) - Nested subgraph definitions - Export connector nodes/links for boundary connections](https://docs.shumoku.dev/en/library/api/buildHierarchicalSheets) [**`buildIdentity`** Build an \`Identity\` from raw parts, dropping empty values. Returns \`undefined\` when nothing identifying was supplied (so callers don't stamp an empty identity object that the resolver would treat as \`unbound\`).](https://docs.shumoku.dev/en/library/api/buildIdentity) [**`buildLayoutProblem`** No summary.](https://docs.shumoku.dev/en/library/api/buildLayoutProblem) [**`cableConnectorForStandard`** Derive the cable-end connector from the link's effective standard. The model no longer stores \`cable.connector\` explicitly — call this from display / validation code that needs the connector value.](https://docs.shumoku.dev/en/library/api/cableConnectorForStandard) [**`cableGradesForStandard`** Cable grades available given the link's standard. Returns an empty array when grade isn't a meaningful axis (DAC / AOC) — the UI hides the third select in that case.](https://docs.shumoku.dev/en/library/api/cableGradesForStandard) [**`cableVariantsForPlug`** Cable variants for a plug — all standards reachable through that cage form factor, ordered by speed. For RJ45 this includes 10M / 100M / 1G / 2.5G / 5G / 10G; for SFP+ it includes SR / LR / CR / AOC.](https://docs.shumoku.dev/en/library/api/cableVariantsForPlug) [**`chamferCorners`** Cut each ~90° corner with a 45° chamfer for the metro look.](https://docs.shumoku.dev/en/library/api/chamferCorners) [**`checkLayoutInvariants`** No summary.](https://docs.shumoku.dev/en/library/api/checkLayoutInvariants) [**`classifyIcon`** Classify a raw icon string into inline-SVG vs URL. Inline content starts with \`<\` (either a bare \`<path .../>\` fragment or a full \`<svg>...</svg>\` block). Anything else is treated as a URL, including \`data:\` URIs.](https://docs.shumoku.dev/en/library/api/classifyIcon) [**`classifyStandardGroup`** No summary.](https://docs.shumoku.dev/en/library/api/classifyStandardGroup) [**`collectObstacles`** Collect all obstacles as centre-based rects, excluding entities related to \`excludeId\`. Both nodes and subgraphs are treated as rectangles.](https://docs.shumoku.dev/en/library/api/collectObstacles) [**`computeEffectivePolicy`** No summary.](https://docs.shumoku.dev/en/library/api/computeEffectivePolicy) [**`computeNetworkLayout`** Compute layout and return both ResolvedLayout and legacy LayoutResult. Use this when you need both (e.g., renderer-svg uses ResolvedLayout directly).](https://docs.shumoku.dev/en/library/api/computeNetworkLayout) [**`computeRoleDrivenRanks`** Role-driven depth rank, robust to UNDIRECTED inventory cables (NetBox/Zabbix arrow:none). Direction can't be trusted, so the root is chosen by role + structure (the most peripheral boundary device = WAN edge) and depth is the BFS distance over the undirected graph — not raw link from→to (which makes a node whose cable happens to point "outward" a false source, e.g. a server at the top). High fan-out / thin-link sinks are not traversed through; a second pass reaches nodes that sit behind them.](https://docs.shumoku.dev/en/library/api/computeRoleDrivenRanks) [**`createEngine`** Build an engine instance. Stateless across queries (every call returns the same answer for the same input given the same config). Memoization is allowed inside; the \`fingerprint\` is the cache key.](https://docs.shumoku.dev/en/library/api/createEngine) [**`createMemoryFileResolver`** Browser/in-memory file resolver for testing](https://docs.shumoku.dev/en/library/api/createMemoryFileResolver) [**`createNetworkLayoutEngine`** Create a LayoutEngine that uses the custom network layout + libavoid routing. Returns LayoutResult for backward compatibility with existing consumers.](https://docs.shumoku.dev/en/library/api/createNetworkLayoutEngine) [**`createNodeFileResolver`** Node.js file resolver implementation](https://docs.shumoku.dev/en/library/api/createNodeFileResolver) [**`createTheme`** Create custom theme](https://docs.shumoku.dev/en/library/api/createTheme) [**`defaultCableGrade`** Default grade for a freshly-picked standard. Used when the user picks/changes the cable medium and we want the third select to land on a sensible value rather than empty.](https://docs.shumoku.dev/en/library/api/defaultCableGrade) [**`defaultStandardForCages`** Propose a sensible default standard given the cages on both ends.](https://docs.shumoku.dev/en/library/api/defaultStandardForCages) [**`deriveMappingFromGraph`** Derive a legacy \`MetricsMapping\` from a resolved graph's bindings. - Node host binding → \`mapping.nodes\[node.id\]\`. - Link interface binding lives on a \`NodePort\`; we attribute it to the link whose endpoint references that port, keyed \`link.id || link-${i}\` to match the rest of the server (server.ts / topologies.ts). Pure; never mutates the input.](https://docs.shumoku.dev/en/library/api/deriveMappingFromGraph) [**`detectClickSide`** Determine which side of a node was clicked based on click position.](https://docs.shumoku.dev/en/library/api/detectClickSide) [**`dumpGraph`** Serialize a graph to authoring YAML, the inverse of \`YamlParser.parse()\`: \`parse(dumpGraph(g))\` is a fixed point over the schema the parser reads. Hand-rolling this as string concatenation is not safe: any label carrying a newline (a two-line segment name, say) has to be quoted or the document it produces is invalid YAML — and a value the writer forgets to emit is simply gone the next time the text is parsed. Both failure modes are silent. So this delegates quoting and escaping to js-yaml, and spreads the graph rather than listing keys: enumerating is what silently drops a field when the model gains one. Only the three shapes the parser stores differently from how it reads them are converted (\`spec\` on nodes and subgraphs, \`plug\` on link endpoints). The fixed point holds over the schema the parser reads, which is NARROWER than the model. Everything else is written to the document — nothing is dropped here — but is lost the next time that text is parsed: - graph: \`terminations\`, \`exclusions\`, \`attachments\` - node: \`presence\`, \`attachments\`, \`suppressedAttachments\`, \`entityId\`, \`position\`, \`size\`, \`termination\`, \`productId\`, \`provenance\`, \`fieldSources\` - link: \`via\`, \`bends\`, \`rateBps\`, \`metadata\`, \`presence\`, \`provenance\`, \`entityId\` - subgraph: \`entityId\`, \`bounds\`, \`pinPositions\` \`presence\` and \`attachments\` are the ones that bite: an \`'anchor'\` node comes back as a \`'scoop'\` (resolve then keeps a device alive that was only meant to carry identity) and a metrics binding is simply gone. So anything a source mints beyond the authoring schema — the editor, the resolver, the entity registry — must not be round-tripped through the YAML pane. Keep this list honest against the parser. A field the parser stopped reading (or never read) does not announce itself: \`subgraph.identity\` was absent here and unread, so an exported region came back without the key \`resolve()\` clusters regions by — one region became two same-labelled boxes with the members split between them, from a document that looked correct. \`sheets\` is the exception that is NOT written: it is a \`Map\`, which cannot be expressed here without inventing an authoring form for it (see \`pruneUndefined\`). \`lineWidth: -1\` disables line folding: folded output re-parses to the same value, but it makes hand-editing the result confusing.](https://docs.shumoku.dev/en/library/api/dumpGraph) [**`effectiveLinkStandard`** No summary.](https://docs.shumoku.dev/en/library/api/effectiveLinkStandard) [**`effectivePlugCage`** Resolve the effective plug cage for an endpoint with the canonical priority: explicit \`plug.cage\` > module's required cage > port.connectors\[0\] (only when the port has a single connector — multi-connector combo ports require an explicit choice on the link side). Returns undefined when none are known.](https://docs.shumoku.dev/en/library/api/effectivePlugCage) [**`effectivePolicyForNode`** Convenience: compute the effective discovery policy for a node in the context of a full \`NetworkGraph\`. Walks the graph 's subgraphs once into a lookup map, then delegates to \`computeEffectivePolicy\`. The resolver, the scheduler, and the API "GET effective policy for this node" surface all need the same answer; they call this helper.](https://docs.shumoku.dev/en/library/api/effectivePolicyForNode) [**`endpointModule`** No summary.](https://docs.shumoku.dev/en/library/api/endpointModule) [**`endpointPlugCage`** No summary.](https://docs.shumoku.dev/en/library/api/endpointPlugCage) [**`endpointStandard`** No summary.](https://docs.shumoku.dev/en/library/api/endpointStandard) [**`ensurePorts`** Walk the graph and ensure every link endpoint references an existing \`NodePort\`. Idempotent — graphs that already comply pass through unchanged (modulo deep-copy).](https://docs.shumoku.dev/en/library/api/ensurePorts) [**`extractHost`** Host from the common label dialects, in priority order.](https://docs.shumoku.dev/en/library/api/extractHost) [**`filterAlertLabels`** Drop Alertmanager-internal labels (\`\_\_name\_\_\`, other \`\_\_…\`).](https://docs.shumoku.dev/en/library/api/filterAlertLabels) [**`findCollinearOverlaps`** Find pairs of edges with segments that run on the same track: parallel (within ~1°), closer than the sum of their half-widths + clearance, and sharing more than \`minSharedLength\` of extent. Reports each edge pair once with its longest shared run.](https://docs.shumoku.dev/en/library/api/findCollinearOverlaps) [**`findContainerOverlaps`** Find pairs of container boxes that overlap where NEITHER is an ancestor of the other (nesting is legal containment, not interference).](https://docs.shumoku.dev/en/library/api/findContainerOverlaps) [**`findContainmentViolations`** Find member nodes whose box is not fully inside their container's bounds (shrunk by \`pad\` on each side). Unknown member ids are ignored — membership resolution is the caller's concern.](https://docs.shumoku.dev/en/library/api/findContainmentViolations) [**`findDetachedTerminals`** Find wires whose polyline does not terminate ON its ports in from→to order (port-attachment constraint). Checks both the position (each terminal within \`epsilon\` of its port) and the direction contract (\`points\[0\]\` is the FROM port specifically — a reversed but touching polyline is reported as detached at both ends). Lines with fewer than two points are skipped: there is no path to check.](https://docs.shumoku.dev/en/library/api/findDetachedTerminals) [**`findEdgeNodePiercing`** Find wires that run THROUGH a foreign node box (inflated by \`inflate\`): any segment crossing one of the box's four borders. A wire may touch its own endpoints' boxes. Grid-accelerated, exact (same predicate as the old routed-score inner loop — this IS that loop, extracted so the score and the constraint check share one definition).](https://docs.shumoku.dev/en/library/api/findEdgeNodePiercing) [**`findNodeOverlaps`** Find pairs of node boxes that overlap (optionally requiring \`margin\` clear space between boxes). O(n²) — layout-sized inputs only.](https://docs.shumoku.dev/en/library/api/findNodeOverlaps) [**`findPortClutter`** Find collisions among port boxes, among label boxes, and between a label and a FOREIGN node box (a label may touch its own node). O(n²) on layout-sized inputs.](https://docs.shumoku.dev/en/library/api/findPortClutter) [**`flattenObject`** Flatten an arbitrary upstream record into \`DiscoveredMetric\[\]\` for the plugin "All metrics" passthrough panel. This is the generic dumper every \`HostsCapable\` plugin should use instead of hand-enumerating fields (which is how zabbix ended up coercing every value to a number and dropping categorical attributes). Walk the object: - object children join into the metric name with \`\_\` (\`{ a: { b: 1 } }\` with prefix \`p\` → \`p\_a\_b\`), - array-of-primitives emits a \`<name>\_count\`, - array-of-objects emits a \`<name>\_count\` then expands each element with a \`<key>\_index\` label, - null / undefined / empty-string / non-finite leaves are skipped as noise. Lifted verbatim (behavior-preserving) from the aruba-instant-on plugin, which had the only correct generic implementation.](https://docs.shumoku.dev/en/library/api/flattenObject) [**`formatReachMeters`** Format a meters value for UI display (1000+ → km).](https://docs.shumoku.dev/en/library/api/formatReachMeters) [**`getBandwidthWidth`** Map a bandwidth label/number to the calibrated stroke width.](https://docs.shumoku.dev/en/library/api/getBandwidthWidth) [**`getDeviceIcon`** Get SVG icon content for a device type](https://docs.shumoku.dev/en/library/api/getDeviceIcon) [**`getLinkWidth`** Compute the visual line width for a link. Single source of truth — used by layout, routing, and rendering.](https://docs.shumoku.dev/en/library/api/getLinkWidth) [**`getLinkWidthForMode`** Visual line width for a link under a width mode. Same precedence as \`getLinkWidth\` (explicit style > bandwidth > type > default). Because a core \`Link\` is one physical link, this IS the per-strand width — a LAG renders as N parallel links each at its own width, never as one merged stroke (v3 "railway discipline": no two services share a drawn line).](https://docs.shumoku.dev/en/library/api/getLinkWidthForMode) [**`getNodeId`** Helper to get node ID from endpoint. Kept as a tiny accessor so callers read intent ("the link's source node") rather than reaching into shape.](https://docs.shumoku.dev/en/library/api/getNodeId) [**`getStandardSpec`** No summary.](https://docs.shumoku.dev/en/library/api/getStandardSpec) [**`getThemeFromCSS`** Get theme from CSS variables](https://docs.shumoku.dev/en/library/api/getThemeFromCSS) [**`groupCableVariants`** Group cable variants by \`cableKind\` for sectioned \`<optgroup>\` rendering.](https://docs.shumoku.dev/en/library/api/groupCableVariants) [**`groupCouplingPairs`** Union-find grouping of coupling pairs into redundancy groups (an N-member stack is a chain of pair links). Generic over ids so both renderers can feed their edge shapes in. \`kind\` of the group = the first pair's kind.](https://docs.shumoku.dev/en/library/api/groupCouplingPairs) [**`groupStandards`** Group a list of \`StandardOption\`s by their \`group\` for sectioned dropdowns.](https://docs.shumoku.dev/en/library/api/groupStandards) [**`hasAlertsCapability`** No summary.](https://docs.shumoku.dev/en/library/api/hasAlertsCapability) [**`hasAutoscanCapability`** No summary.](https://docs.shumoku.dev/en/library/api/hasAutoscanCapability) [**`hasConfigOptions`** Duck-type: can this plugin supply dynamic candidates for \`optionsSource\` fields?](https://docs.shumoku.dev/en/library/api/hasConfigOptions) [**`hasConnectionInfo`** Duck-type: does this plugin expose derived, display-only connection info?](https://docs.shumoku.dev/en/library/api/hasConnectionInfo) [**`hasHostsCapability`** No summary.](https://docs.shumoku.dev/en/library/api/hasHostsCapability) [**`hasMetricsCapability`** No summary.](https://docs.shumoku.dev/en/library/api/hasMetricsCapability) [**`hasNativeApi`** Duck-type check: does this plugin expose a native-API passthrough? Intentionally not bound to a \`DataSourceCapability\` literal — this is a developer-only escape hatch, not a product feature, so it stays off the advertised capability list.](https://docs.shumoku.dev/en/library/api/hasNativeApi) [**`hasTopologyCapability`** No summary.](https://docs.shumoku.dev/en/library/api/hasTopologyCapability) [**`interfaceMatchScore`** Score how well two normalized interface names match (0–1): same canonical prefix, then port numbers compared right-to-left (rightmost = most specific).](https://docs.shumoku.dev/en/library/api/interfaceMatchScore) [**`interfaceNamesMatch`** Whether two interface names denote the same physical port. Same canonical prefix with aligning numbers (score ≥ 0.5), OR — for cross-vocabulary cases like TTDB's speed-code \`hg\` vs Juniper \`et-\` where prefixes differ but the port number is identical — the full number sequence matches.](https://docs.shumoku.dev/en/library/api/interfaceNamesMatch) [**`isExcluded`** True when the node should NOT be auto-touched by the scheduler or by any source's discovery pass — convenience predicate that hides the mode comparison from callers that just need a yes/no answer.](https://docs.shumoku.dev/en/library/api/isExcluded) [**`isExportLink`** Check if a link is a virtual export connector link](https://docs.shumoku.dev/en/library/api/isExportLink) [**`isExportNode`** Check if a node is a virtual export connector](https://docs.shumoku.dev/en/library/api/isExportNode) [**`isPluggableConnector`** No summary.](https://docs.shumoku.dev/en/library/api/isPluggableConnector) [**`isPoeCapableConnector`** No summary.](https://docs.shumoku.dev/en/library/api/isPoeCapableConnector) [**`isPortLinked`** Whether the given port already appears as an endpoint of any link. A physical port models one cable termination, so it can host at most one link at a time. Callers use this to reject attempts to wire a second link onto an already-connected port — \`linkExists\` only catches exact-duplicate links, which isn't enough for the "1 port = 1 link" invariant.](https://docs.shumoku.dev/en/library/api/isPortLinked) [**`isSecretProp`** A field is a write-only secret if it declares \`secret: true\`. \`format: 'password'\` is honoured too so external plugins that predate the flag keep working; new schemas should set \`secret: true\`.](https://docs.shumoku.dev/en/library/api/isSecretProp) [**`issuesForTarget`** Filter issues whose target points at a specific field. Used by the UI.](https://docs.shumoku.dev/en/library/api/issuesForTarget) [**`keyHash`** Stable string form of a key for use as a Map key.](https://docs.shumoku.dev/en/library/api/keyHash) [**`layoutComposite`** No summary.](https://docs.shumoku.dev/en/library/api/layoutComposite) [**`linkExists`** Check if a link already exists between two ports.](https://docs.shumoku.dev/en/library/api/linkExists) [**`linkSpeedBps`** Resolve a link's nominal speed (bits/sec). Reads per-endpoint module standards (the canonical source) and falls back to runtime \`rateBps\` when neither end has a module set. For symmetric links both endpoints return the same speed; for asymmetric links (BiDi etc.) we pick the lower of the two (conservative for utilization math).](https://docs.shumoku.dev/en/library/api/linkSpeedBps) [**`mapAlertmanagerSeverity`** No summary.](https://docs.shumoku.dev/en/library/api/mapAlertmanagerSeverity) [**`mapWithConcurrency`** Run \`fn\` over \`items\` with at most \`limit\` calls in flight at once, preserving input order in the result array. Replaces the two failure modes the audit found: unbounded \`Promise.all\` fan-out (network-scan would open hundreds of SNMP sockets at once) and fully sequential \`await\`-in-a-loop polling (zabbix walked hosts one by one). A rejected \`fn\` rejects the whole call (like \`Promise.all\`); a caller wanting partial results should catch inside \`fn\`.](https://docs.shumoku.dev/en/library/api/mapWithConcurrency) [**`mediumFromGrade`** Derive the cable medium kind from a \`CableGrade\`. Twisted-pair categories map to twisted-pair, OM3/4/5 → multi-mode fiber, OS1/2 → single-mode fiber, DAC and AOC are their own mediums.](https://docs.shumoku.dev/en/library/api/mediumFromGrade) [**`mediumFromStandard`** Derive the cable medium implied by an Ethernet standard. Resolves the standard via \`STANDARD\_SPECS\` and maps \`cableKind\` + \`fiberMode\` onto the unified \`CableMedium\` enum.](https://docs.shumoku.dev/en/library/api/mediumFromStandard) [**`mergeTheme`** Merge theme with overrides](https://docs.shumoku.dev/en/library/api/mergeTheme) [**`metricsBindingOf`** The metrics-binding attachment on an element, if any. When \`activeSourceIds\` is given, only a binding whose \`sourceId\` is active counts — so a binding left behind by a detached/removed metrics source stops driving the mapping.](https://docs.shumoku.dev/en/library/api/metricsBindingOf) [**`migratePortShape`** Backwards-compat shim for saved files: an older NodePort serialized with \`cage: PortConnector\` (or \`cage: 'combo'\`) is normalized into the modern \`connectors: PortConnector\[\]\` shape on read. Combo expands to \['rj45', 'sfp'\] as a best-effort default; users can refine from the editor afterwards.](https://docs.shumoku.dev/en/library/api/migratePortShape) [**`missingCapabilityMethods`** Declared capabilities whose required method is missing on a constructed instance, formatted \`"cap → method()"\`. Empty array = compliant. Unknown (open) capabilities are skipped. The registry calls this once at first instantiate (decision 7) to catch a plugin that advertises a capability it doesn't actually implement (C6).](https://docs.shumoku.dev/en/library/api/missingCapabilityMethods) [**`moveNode`** No summary.](https://docs.shumoku.dev/en/library/api/moveNode) [**`movePort`** Move a port to the nearest edge of its node based on cursor position. Changes the port's side and redistributes all ports. Returns null if the side didn't change.](https://docs.shumoku.dev/en/library/api/movePort) [**`moveSubgraph`** Move an entire subgraph (bounds + all contents) by delta from its current position. Resolves sibling collisions after the move.](https://docs.shumoku.dev/en/library/api/moveSubgraph) [**`newId`** Generate a fresh unique ID for the given entity kind. Collision probability for 10-char nanoid is negligible for editor-scale workloads (~trillions of IDs before 1% collision chance).](https://docs.shumoku.dev/en/library/api/newId) [**`nodeIdentityKeys`** Enumerate Node identity keys in priority order. The first match wins during cluster lookup. Caller should also bind \*every\* key to the resulting cluster after a match so subsequent observations collapse correctly even if they only carry a weaker key.](https://docs.shumoku.dev/en/library/api/nodeIdentityKeys) [**`nodeIdentityQuality`** Coarse identity quality — surfaced in UI to warn users which elements may detach from their metrics/observations across reboots. Node: \`stable\` if chassisId + (mgmtIp or sysName), \`weak\` if any single primary key, \`unbound\` otherwise. Port: \`stable\` if ifName + (mac or ifIndex), \`weak\` if any single key, \`unbound\` otherwise.](https://docs.shumoku.dev/en/library/api/nodeIdentityQuality) [**`nodesOverlap`** Check if two centre-based rectangles overlap with a gap. Free function for callers that want to do their own collision math; the engine's \`tryPlace\` is preferred for full placement validation.](https://docs.shumoku.dev/en/library/api/nodesOverlap) [**`normalizeInterfaceName`** Normalize an interface name to \`{ prefix, numbers, sub }\`. The numeric part is split on \`/\`, \`-\`, or \`:\` so TTDB's dash form ("hg-0-0-0" → \[0,0,0\]) lines up with the slash form ("hg/0/0/0" → \[0,0,0\]).](https://docs.shumoku.dev/en/library/api/normalizeInterfaceName) [**`normalizeMacKey`** Canonical spelling for a MAC-shaped identity key: lowercase, colon-separated. Sources disagree on how to write the same wire address. Huawei NCE returns \`50-04-01-01-D5-50\` in its device list and \`CC:D8:1F:9F:D4:AB\` in its LLDP table; an SNMP walk of the very same switch yields \`cc:d8:1f:9f:d4:ab\`; Cisco gear writes \`ccd8.1f9f.d4ab\`. Identity keys are matched as exact strings, so without one canonical spelling a device arrives as two entities that never merge — the failure is silent, which is what makes it worth normalizing here rather than in each plugin. Anything that is not twelve hex digits once separators are stripped is returned untouched: \`chassisId\` is only sometimes a MAC (LLDP also permits interface names and network addresses) and must survive as written.](https://docs.shumoku.dev/en/library/api/normalizeMacKey) [**`normalizePortConnector`** No summary.](https://docs.shumoku.dev/en/library/api/normalizePortConnector) [**`paperSizeToPixels`** Convert paper size to pixels at given DPI](https://docs.shumoku.dev/en/library/api/paperSizeToPixels) [**`parseAlertmanagerAlerts`** Parse an Alertmanager response into core \`Alert\[\]\`, applying the standard active/timeRange and minSeverity filters. - Active alerts are always kept; resolved alerts are dropped when \`activeOnly\`, otherwise dropped once older than \`timeRange\` (default 1h). - Unparseable \`startsAt\` falls back to \`now\`; missing/unparseable \`endsAt\` yields \`undefined\` (no bogus epoch-0 end time).](https://docs.shumoku.dev/en/library/api/parseAlertmanagerAlerts) [**`parseWithMaps`** No summary.](https://docs.shumoku.dev/en/library/api/parseWithMaps) [**`placeNode`** \*\*Geometric\*\* placement for a single unpositioned node: find the point nearest \`initial\` that doesn't overlap any existing node or subgraph. The graph's link structure is deliberately ignored — this is the primitive for "drop the node exactly where the user clicked", "paste at cursor", "convert BOM item to diagram node at the canvas edge", i.e. user-driven placement where the surrounding topology should not influence the outcome. For \*\*structural\*\* placement (re-layout based on link flow, pin a set of nodes, arrange a selection), use \`layoutNetwork\` with its \`fixed\` / \`hints\` options instead. The two APIs stay separate because their intents differ: \`placeNode\` is O(existing obstacles) geometry, \`layoutNetwork\` is a full Sugiyama run.](https://docs.shumoku.dev/en/library/api/placeNode) [**`placePorts`** Compose the three phases: decide sides → order each side → compute absolute coordinates → emit \`ResolvedPort\`s keyed by \`nodeId:portId\`. Port sides are re-seated from final geometry (see reseatPortsByGeometry) so wires exit toward their peer regardless of how the upstream/downstream tree was inferred. Ports are positioned along each side by distributePortsAlongSide (peer-aligned), with computePortPosition's even spread as the fallback.](https://docs.shumoku.dev/en/library/api/placePorts) [**`plugFromCage`** Build a cage-only plug — the user picked a form factor without a module yet.](https://docs.shumoku.dev/en/library/api/plugFromCage) [**`plugFromStandard`** Build a plug from a chosen module standard. The plug carries only the \`module\` — \`cage\` is intentionally omitted because it's derivable from \`module.standard\` via \`STANDARD\_SPECS\[std\].cage\`. Returns \`undefined\` if the standard is unknown.](https://docs.shumoku.dev/en/library/api/plugFromStandard) [**`plugProfileForStandard`** Resolve a standard to its plug profile (for prefilling the plug select).](https://docs.shumoku.dev/en/library/api/plugProfileForStandard) [**`plugProfilesForConnectors`** Plug profiles compatible with the given port connectors. Empty arrays are permissive (return all plugs); multi-connector combo ports return the union of compatible plugs.](https://docs.shumoku.dev/en/library/api/plugProfilesForConnectors) [**`portBox`** Axis-aligned box of the port marker itself.](https://docs.shumoku.dev/en/library/api/portBox) [**`portIdentityKeys`** Enumerate Port identity keys in priority order. Port matching happens \*within\* a parent node cluster — the same \`ifName\` on a different chassis is a different port. The caller is responsible for that scoping; this list is just the per-port priority order.](https://docs.shumoku.dev/en/library/api/portIdentityKeys) [**`portIdentityQuality`** No summary.](https://docs.shumoku.dev/en/library/api/portIdentityQuality) [**`portLabelBox`** Axis-aligned box of the port's label, or undefined when the port has no label. Mirrors the renderer: - vertical (composite top/bottom): the label runs along the wire, away from the node face, in a 12px-wide strip. - horizontal: classic placement per side (above / below / beside).](https://docs.shumoku.dev/en/library/api/portLabelBox) [**`portLabelLength`** Estimated label box length along the reading direction.](https://docs.shumoku.dev/en/library/api/portLabelLength) [**`portLabelReach`** Reach of a port's label measured from the node face outward, along the wire (vertical labels) — how much corridor the label needs between this row and the next. Horizontal labels reach PORT\_LABEL\_H.](https://docs.shumoku.dev/en/library/api/portLabelReach) [**`reachForLink`** Effective reach in meters for a link, accounting for the cable grade. Falls back to the standard's \`maxReach\_m\` when the grade isn't set or isn't in the registry's per-grade table.](https://docs.shumoku.dev/en/library/api/reachForLink) [**`rebalanceSubgraphs`** Rebalance the entire layout after any mutation. Uniform algorithm: 1. Recompute subgraph bounds (bottom-up, deepest first) 2. Resolve subgraph vs subgraph sibling collisions 3. Resolve subgraph vs free node collisions (push nodes away)](https://docs.shumoku.dev/en/library/api/rebalanceSubgraphs) [**`removePort`** Remove a port and redistribute remaining ports on the same node. Also removes any links that reference this port. Returns updated nodes, ports, and links.](https://docs.shumoku.dev/en/library/api/removePort) [**`resolve`** Resolve the project's own (intrinsic) NetworkGraph against any number of source snapshots into a single graph whose every entity carries \`provenance\`. Model: \*\*all sources are equal, priority-ordered contributions.\*\* The intrinsic (project-owned) graph is just the top-priority contribution; observed snapshots carry their own priority (mirroring \`topology\_data\_sources.priority\`). resolve clusters contributions by identity (any-key match — orthogonal to priority) and then, \*\*per field\*\*, the highest-priority contribution that actually holds a value wins (\`priority desc, capturedAt desc\`). A field nobody holds is omitted. This is the "Git-like" merge: an edit that only renamed a node keeps the observed ports/community flowing through untouched. There is intentionally NO \`=== 'intrinsic'\` special-casing in the field merge — "the project's edits win" falls out of "intrinsic has the highest priority". The reserved id \`'intrinsic'\` survives only as that contribution's source \*label\*, so the UI can tell a project-set value from an observed one (per-field \`fieldSources\`, per-attachment \`provenance\`). It is ownership (intrinsic vs external), NOT a human-vs-machine layer. See \`apps/server/docs/design/topology-source-priority-merge.md\`.](https://docs.shumoku.dev/en/library/api/resolve) [**`resolveBandwidthBps`** Parse a bandwidth string ("10G", "2.5Gbps", "500M") or raw bps number into bits/sec. Pure utility — used by plugin configs that accept user-typed bandwidth values; not tied to Link semantics.](https://docs.shumoku.dev/en/library/api/resolveBandwidthBps) [**`resolveIcon`** Resolve the icon to render for a given spec. Returns the explicit \`spec.icon\` if any; otherwise the bundled device-type fallback; otherwise null.](https://docs.shumoku.dev/en/library/api/resolveIcon) [**`resolveLayout`** Convert a legacy LayoutResult to a ResolvedLayout. Port positions are converted from center-relative to absolute.](https://docs.shumoku.dev/en/library/api/resolveLayout) [**`resolveNodePosition`** Resolve a node's position against all other entities. Wrapper around \`resolvePosition\` for the legacy id-based API.](https://docs.shumoku.dev/en/library/api/resolveNodePosition) [**`resolveNodeSize`** Resolve a node's display size — returns the node's own \`size\` if set, otherwise asks the engine for its body size. Pass \`engine\` to share its TextMeasurer cache with other queries. Without one, a process-wide default engine is created lazily; this is fine for one-off helpers but not ideal for hot loops where explicit control matters.](https://docs.shumoku.dev/en/library/api/resolveNodeSize) [**`resolvePosition`** Resolve position of any rectangle against all obstacles (nodes + subgraphs). Delegates to the engine's geometric solver so editor / renderer / layout all use the same collision rules.](https://docs.shumoku.dev/en/library/api/resolvePosition) [**`routeEdges`** Produce a \`ResolvedEdge\` for every link whose endpoints resolve to known ports. Links pointing at a missing port are dropped (matches the previous behaviour of libavoid-router). The function returns a Promise for backwards compatibility with the old WASM-backed router — every existing caller already awaits the result, and async-of-sync incurs zero cost. \`nodes\` is accepted but unused so the signature mirrors the historical libavoid-router API. Bezier edges read positions off \`ports\` directly via each \`ResolvedPort.absolutePosition\`.](https://docs.shumoku.dev/en/library/api/routeEdges) [**`scoreRoutedEdges`** Score actually-routed geometry (no straight-line proxies).](https://docs.shumoku.dev/en/library/api/scoreRoutedEdges) [**`searchCompositeLayout`** Run the search: parameter multi-start → congestion pass → pair flips. Total evaluations stay within \`maxEvaluations\` (default 16); the result is always the best ROUTED variant seen.](https://docs.shumoku.dev/en/library/api/searchCompositeLayout) [**`segmentsIntersect`** Shared exact segment-intersection predicate (strict crossing).](https://docs.shumoku.dev/en/library/api/segmentsIntersect) [**`severityAtLeast`** True when \`severity\` is at least as severe as \`min\`.](https://docs.shumoku.dev/en/library/api/severityAtLeast) [**`severityRank`** Rank of a severity (unknown → \`info\`).](https://docs.shumoku.dev/en/library/api/severityRank) [**`shouldUseComposite`** No summary.](https://docs.shumoku.dev/en/library/api/shouldUseComposite) [**`specDeviceType`** Extract DeviceType from a spec (hardware and compute only).](https://docs.shumoku.dev/en/library/api/specDeviceType) [**`stampObserved`** Return a copy of \`node\` stamped with observation provenance, identity, and sync metadata. Never mutates the input (callers build node lists with \`.map(stampObserved…)\`). Existing identity/metadata are preserved and only overlaid with the supplied values.](https://docs.shumoku.dev/en/library/api/stampObserved) [**`standardsForConnectors`** Standards available given the connectors on each port. Used by the editor's cascading "Standard" picker so the dropdown only offers links that physically fit. Empty arrays are treated permissively — we'd rather show too many than none. For combo ports (multiple connectors), this returns the union: any standard that fits at least one connector on each side qualifies. Returned as one flat list; group via \`option.group\` if the UI wants to split into sections.](https://docs.shumoku.dev/en/library/api/standardsForConnectors) [**`stringifyWithMaps`** No summary.](https://docs.shumoku.dev/en/library/api/stringifyWithMaps) [**`symmetricPlug`** Produce a symmetric plug spec for a link — both endpoints get the same plug+module. Used by editor flows that default to symmetric links.](https://docs.shumoku.dev/en/library/api/symmetricPlug) [**`unresolveLayout`** Convert a ResolvedLayout back to a legacy LayoutResult. Used for backward compatibility with existing renderers.](https://docs.shumoku.dev/en/library/api/unresolveLayout) [**`validateAgainstSchema`** No summary.](https://docs.shumoku.dev/en/library/api/validateAgainstSchema) [**`validateLinkCompatibility`** Validate a link against its endpoints, ports, and cable. Runs every registered endpoint-level and link-level check; collects all issues the UI / consumers can route to inline markers via \`issuesForTarget\`.](https://docs.shumoku.dev/en/library/api/validateLinkCompatibility) [**`validateTopologyIdentityContract`** Check whether \`graph\` satisfies the topology identity contract. Returns arrays of violating node/port ids. Both arrays empty → contract met.](https://docs.shumoku.dev/en/library/api/validateTopologyIdentityContract) [**`verifyLayoutConstraints`** Verify a ResolvedLayout against the registry. Every check derives its parameters from the specs above — no second source of truth.](https://docs.shumoku.dev/en/library/api/verifyLayoutConstraints) [**`verifySemanticLayout`** No summary.](https://docs.shumoku.dev/en/library/api/verifySemanticLayout)
