import { P as Port, a as NetworkEdge, N as NetworkNode, b as DiagramNodeKind, c as DevicePort, d as DeviceFace, e as PortBlock, f as NetworkNodeDataBase, g as PortCableType, h as PortPosition, i as DiagramNode, j as DiagramEdge, k as DiagramUiState, D as DiagramDocument } from './index-CD1VEhzs.js';
export { B as BreakerPhase, l as BreakerSwitchState, m as BreakerWay, n as BreakerWayInstanceState, o as DeviceBay, p as DeviceBayInstanceState, q as DeviceBaySlot, r as DeviceBayStatus, s as DeviceImageOptions, t as DeviceStatus, u as DiagramEdgeEndpoint, v as DiagramEndpoint, w as DiagramExternalRef, x as DiagramNodePlacement, y as DiagramNodeRender, z as DistributionBoardNodeData, A as NetworkEdgeData, C as NetworkEdgeType, E as NetworkNodeDataByType, F as NetworkNodeType, G as PduBoardType, H as PduSupply, I as PortPositionInput, J as PortType, K as PowerConnectorGender, L as PowerFeedType, M as PowerSide, R as RackConfig, O as RackDevice, Q as RackLayoutMode, S as RackMountMode, T as TemplateBayKind, U as UNumberingDirection, V as VerticalPduNodeData, W as Width } from './index-CD1VEhzs.js';
import '@xyflow/react';
/**
* Normalize source/target directional suffixes used for bidirectional handles.
* Example: `Rear Ports-1-target` -\> `Rear Ports-1`.
*/
declare function normalizeDirectionalHandleId(handleId: string | null | undefined): string;
/**
* Normalize rendered handle ids back to their canonical endpoint id.
* This removes the directional suffix and the split-face alias, in the order
* the renderer composes them: ` [--rear|--front][-source|-target]`.
* (Previously only `--rear` was stripped; `--front` ghost aliases now
* normalize too.)
*
* Losslessness: the strip is unconditional, so it is only safe when the base
* itself cannot end in a reserved suffix. {@link buildPortHandleId} guarantees
* this for device ports — a label like `target` or `foo-target` is escaped at
* build time (`-%74arget`, `-foo%2Dtarget`) so it survives
* normalization intact. Bare power labels and explicit `port.id`s are emitted
* verbatim, so a caller that chooses an id ending in `-source`/`-target`/
* `--rear`/`--front` owns that collision.
*/
declare function normalizeHandleId(handleId: string | null | undefined): string;
/**
* Returns true when a port type is considered power-related.
*/
declare function isPowerPortType(portType: string | undefined): boolean;
/**
* Handle id of a vertical PDU's supply inlet — the single point where a branch
* circuit from a distribution-board breaker way lands.
*
* Deliberately non-numeric so it can never collide with an outlet id, which is
* always a bare 1-based number (`"1"…"N"`).
* @public
*/
declare const PDU_INLET_HANDLE_ID = "inlet";
/**
* Handle id of a distribution board's incomer — the board's own supply inlet,
* mounted by `DistributionBoardNode`. The board analogue of
* {@link PDU_INLET_HANDLE_ID}.
* @public
*/
declare const BOARD_INCOMER_HANDLE_ID = "incomer";
/**
* Whether a handle id names a power-distribution node's supply inlet
* (`inlet` on a PDU strip, `incomer` on a distribution board). Directional and
* face-alias suffixes are normalized away first.
* @public
*/
declare function isPowerInletHandleId(handleId: string | null | undefined): boolean;
/**
* Build a stable handle id for a port.
*
* Three shapes, in priority order:
* 1. An explicit `port.id` is returned verbatim — it is the caller's contract,
* including any collision with a fixed grammar shape.
* 2. A power port returns its `label` verbatim, preserving legacy ids (bare
* outlet numbers, `PSU-A`, ...).
* 3. Otherwise the group and label are percent-escaped and joined
* (`-`) so the result is injective and never aliases a fixed
* grammar production or a directional/face suffix. Invert with
* `decodePortHandleId`.
*
* NOTE (pre-1.0 wire-format revision): device-port segments containing `-` or
* `%` now encode those characters (`%2D` / `%25`), and a clean label that would
* otherwise collide (e.g. group `fiber` + label `3` becomes `fiber-%33`) gets a
* first-character escape. Segments with no reserved character are unchanged, so
* common ids are stable.
*/
declare function buildPortHandleId(portBlockName: string, port: Pick & {
type?: string;
}): string;
/**
* Heuristic for whether a handle id represents a power endpoint.
*
* Prefer {@link applyDirectionalPortHandleId}'s `portType` option when the port
* is available — this string heuristic is a fallback only.
*
* Matches library power-id shapes (`Power Ports-1`, `pwr-1`, `power_ac-1`,
* `PSU-A`, `outlet-3`) without treating every label that merely contains the
* word "power" (e.g. `Power Room Uplinks-1`) as a power endpoint.
*/
declare function looksLikePowerHandleId(handleId: string): boolean;
/**
* Options for {@link applyDirectionalPortHandleId}.
*/
interface ApplyDirectionalPortHandleIdOptions {
/**
* The port's `type` (e.g. `rj45`, `power_ac`). When provided, power-ness is
* decided from it via {@link isPowerPortType} — the same rule the renderer
* uses to decide whether a port mounts directional handles at all. Always
* pass this when the port is available.
*/
portType?: string;
}
/**
* Apply a directional (`source`/`target`) suffix to a rendered handle id — the
* inverse of {@link normalizeDirectionalHandleId}. Any existing directional
* suffix is replaced first, and passing no direction is a no-op.
* Example: `Rear Ports-1` + `target` -\> `Rear Ports-1-target`.
*
* Power endpoints are matched without a direction, so power handles are
* returned unchanged. Whether a handle is a power handle is decided from
* `options.portType` when given (exact — the renderer's rule). Without it the
* decision falls back to {@link looksLikePowerHandleId}, which recognises
* library power-id shapes (`Power Ports-1`, `pwr-*`, `PSU-*`, `outlet-*`)
* without treating labels that merely contain the word "power" as power.
* Pass `portType` whenever you have the port.
*/
declare function applyDirectionalPortHandleId(handleId: string, direction?: "source" | "target" | null, options?: ApplyDirectionalPortHandleIdOptions): string;
/**
* The structural shape of a handle id's base (suffixes already stripped).
*
* - `outlet` — vertical-PDU outlets: a bare number (`"4"`).
* - `fiber` — `fiber-` (tube/cable nodes) or `fiber--`
* (FibreFlowMap rows).
* - `tube` — `tube-` (tube and multi-tube-cable nodes).
* - `splice-side` — bare `left` / `right` (splice nodes).
* - `side-port` — `left-` / `right-` (coupler ports).
* - `split-io` — `input-` / `output-` (fibre-split nodes).
* - `cable-button` — the literal `cable-button` connector on cable nodes.
* - `breaker-way` — `way-` (distribution-board breaker ways).
* - `port` — everything else: device-family ports built by
* {@link buildPortHandleId} (`-`, a bare power label, or an
* explicit `port.id`). A dashed base is decoded into `group`/`label` (the
* segments are percent-escaped so the join delimiter is unambiguous); a
* dash-free base, which can never be a group-label join, is flagged
* `verbatim` (an opaque `port.id` or bare power label).
* @public
*/
type HandleShape = {
kind: "outlet";
outlet: number;
} | {
kind: "fiber";
fiber: number;
tube?: number;
} | {
kind: "tube";
tube: number;
} | {
kind: "splice-side";
side: "left" | "right";
} | {
kind: "side-port";
side: "left" | "right";
index: number;
} | {
kind: "split-io";
io: "input" | "output";
index: number;
} | {
kind: "cable-button";
} | {
kind: "breaker-way";
wayId: string;
} | {
kind: "port";
/**
* Decoded port group, when the base is a `-` join (has a
* dash). Best-effort structural decode: for a verbatim `port.id` or power
* label that happens to contain a dash (`PSU-A`) this is the split before
* the first dash, not necessarily a real group.
*/
group?: string;
/** Decoded port label — same best-effort decode as `group` above. */
label?: string;
/**
* `true` when the base has no dash, so it cannot be a group-label join —
* it is an opaque `port.id` or a bare power label with no `group`/`label`
* reading.
*/
verbatim?: boolean;
};
/**
* A structurally parsed handle id.
* @public
*/
interface ParsedHandleId {
/** The id exactly as given. */
raw: string;
/** `raw` with the directional suffix and face alias stripped. */
base: string;
/** Directional suffix, when present (`X-source` / `X-target`). */
direction?: "source" | "target";
/**
* Split-face alias, when present (`X--rear` / `X--front`) — added by the
* renderer when both faces of a device are shown and a handle id would
* otherwise collide with its opposite face.
*/
faceAlias?: "front" | "rear";
/** Structural classification of {@link ParsedHandleId.base}. */
shape: HandleShape;
/**
* Other shapes the base could structurally be, when it is genuinely
* ambiguous. Present (and non-empty) only when there is a second reading;
* absent otherwise. The primary {@link ParsedHandleId.shape} is never changed
* by this — `alternates` is a pure signal.
*
* The classic case is a verbatim `port.id` that happens to match a fixed
* shape: `tube-5` classifies as `{ kind: "tube", tube: 5 }` but could be an
* explicit port id, so `alternates` is `[{ kind: "port", verbatim: true }]`.
* A dashed group-label port carries the same verbatim alternate (it could be
* one opaque id). Only node-context-aware classification (which handle-ids
* cannot do from the string alone) can pick the right reading.
*/
alternates?: HandleShape[];
}
/**
* Parse a rendered/stored handle id into its structural parts: base, optional
* directional suffix, optional split-face alias, and the {@link HandleShape}
* of the base. Total — never throws; unrecognized bases classify as `port`.
* @public
*/
declare function parseHandleId(handleId: string | null | undefined): ParsedHandleId;
/**
* Build a fiber handle id: `fiber-`, or `fiber--` when a tube
* index is given (the FibreFlowMap row form).
* @public
*/
declare function buildFibreHandleId(fiber: number, tube?: number): string;
/**
* Build a tube handle id: `tube-`.
* @public
*/
declare function buildTubeHandleId(tube: number): string;
/**
* Build a splice handle id: the bare `left` / `right` sides of a splice node.
* @public
*/
declare function buildSpliceHandleId(side: "left" | "right"): string;
/**
* Build a coupler port handle id: `left-` / `right-` (1-based).
* @public
*/
declare function buildCouplerHandleId(side: "left" | "right", index: number): string;
/**
* Build a fibre-split io handle id: `input-` / `output-`.
* @public
*/
declare function buildSplitIoHandleId(io: "input" | "output", index: number): string;
/**
* Build a distribution-board breaker way handle id: `way-`.
*
* The way id is percent-escaped exactly as {@link buildPortHandleId} escapes
* its segments, so a way id containing `-` or `%` (or the `--rear` face alias
* suffix) cannot be misread by {@link parseHandleId}; the parse side decodes
* it symmetrically. Ids with no reserved character are unchanged.
*
* Ways are power endpoints, so the id carries no directional suffix (see
* {@link applyDirectionalPortHandleId}) and {@link looksLikePowerHandleId}
* recognises the prefix.
* @public
*/
declare function buildBreakerWayHandleId(wayId: string): string;
/**
* Append a split-face alias to a handle id: `X` + `rear` -\> `X--rear`.
* Any existing alias is replaced. The renderer applies this when a hidden or
* ghost face would mount a handle whose id collides with the visible face.
* @public
*/
declare function applyFaceAliasHandleId(handleId: string, face: "front" | "rear"): string;
/**
* True iff two handle ids refer to the same handle under the library's own
* equivalence relation. This is the one function to use for "is this edge on
* this port?" — it replaces bespoke candidate-set matchers.
*
* @remarks
* The relation, applied to BOTH sides: strip the directional suffix and the
* split-face alias ({@link normalizeHandleId} semantics), then apply the
* narrowed `+`-insensitive rule (drop `+`, escaping-aware), then
* compare. So `Rear Ports-1-target`, `Rear Ports-1--rear` and `Rear Ports-1`
* all match, and `SFP+-1` matches `SFP-1`.
*
* It is intentionally string-structural and does NOT percent-decode: it matches
* builder-produced ids (the single source of composite ids), where both sides
* share the same escaping discipline. A hand-built composite that drops `+` AND
* writes a raw `-` where the builder writes `%2D` (`Group-SFP-1` vs
* `Group-SFP+%2D1`) is a deliberate non-match — that form is unreachable through
* {@link buildPortHandleId}. Prefer this over the raw sanitizer, which leaks
* matching concerns onto callers.
*
* @example
* ```ts
* handlesMatch("Rear Ports-1-target", "Rear Ports-1"); // true
* handlesMatch("Ports-1--rear", "Ports-1"); // true
* handlesMatch("SFP+-1", "SFP-1"); // true
* handlesMatch("Ports-1", "Ports-2"); // false
* ```
* @public
*/
declare function handlesMatch(a: string, b: string): boolean;
/**
* Return the ids of every edge attached to the given `handleId` on `nodeId`,
* matching handles with {@link handlesMatch}. An edge qualifies when
* `source === nodeId` and its `sourceHandle` matches, or `target === nodeId` and
* its `targetHandle` matches. Edges with an undefined `sourceHandle`/
* `targetHandle` on the relevant side are skipped.
*
* @remarks
* This is the multi-edge companion to {@link handlesMatch} and the replacement
* for consumers' hand-rolled per-port edge scans. A self-loop edge (both ends on
* `nodeId`, both handles matching) is returned once. The scan is a single linear
* pass over `edges`; it does not build an index, so for repeated lookups over a
* large edge set a caller may still want its own Map.
*
* @example
* ```ts
* const handle = buildPortHandleId("Rear Ports", { label: "1" });
* findEdgesForPortHandle(edges, "device-1", handle); // ["e1", "e2"]
* ```
* @public
*/
declare function findEdgesForPortHandle(edges: NetworkEdge[], nodeId: string, handleId: string): string[];
/**
* For pass-through nodes with two sides, return the handle id of the opposite
* side: splice `left` \<-\> `right`, coupler `left-` \<-\> `right-`.
* Returns `undefined` for every other shape. Directional suffixes and face
* aliases are stripped (neither occurs on splice/coupler handles in practice).
* @public
*/
declare function getOppositeSideHandleId(handleId: string): string | undefined;
/**
* Options for {@link encodeHandle}: the optional suffixes composed onto the
* base, in grammar order ` [--rear|--front][-source|-target]`.
* @public
*/
interface EncodeHandleOptions {
/** Split-face alias to append (`--rear` / `--front`). */
face?: "front" | "rear";
/**
* Directional suffix to append (`-source` / `-target`). Applied structurally
* regardless of power-ness — use {@link applyDirectionalPortHandleId} instead
* when you need power ports left un-suffixed.
*/
direction?: "source" | "target";
}
/**
* The single structured encoder: turn a {@link HandleShape} into its handle-id
* string, dispatching to the individual typed builders. The inverse of
* {@link decodeHandle}/{@link parseHandleId} for the base classification.
*
* @remarks
* Use this when you hold structured handle data (e.g. from a `DiagramEndpoint`)
* and need the React Flow boundary string, instead of remembering which builder
* a kind maps to. A `port` shape REQUIRES string `group` and `label` — it is
* encoded through {@link buildPortHandleId} (so the same escaping/injectivity
* applies). A `verbatim` port has no id to emit and cannot be re-encoded: pass
* an explicit id to {@link buildPortHandleId} yourself for that case.
*
* Round-trip: `decodeHandle(encodeHandle(shape)).shape` deep-equals `shape` for
* every non-verbatim kind.
*
* @throws TypeError when a `port` shape is missing `group` or `label`.
* @example
* ```ts
* encodeHandle({ kind: "fiber", tube: 2, fiber: 7 }); // "fiber-2-7"
* encodeHandle({ kind: "port", group: "Rear Ports", label: "1" }); // "Rear Ports-1"
* encodeHandle({ kind: "port", group: "Ports", label: "1" }, { face: "rear", direction: "source" });
* // "Ports-1--rear-source"
* ```
* @public
*/
declare function encodeHandle(shape: HandleShape, opts?: EncodeHandleOptions): string;
/**
* Decode a handle-id string into its structural parts. A documented alias of
* {@link parseHandleId} — identical behavior, named for symmetry with
* {@link encodeHandle}.
*
* @remarks
* Total (never throws). Use the {@link encodeHandle}/`decodeHandle` pair when
* you think in structured {@link HandleShape}s; use `parseHandleId` directly
* when the "parse a rendered id" framing reads better. See
* {@link ParsedHandleId.alternates} for the ambiguity signal on verbatim ids.
*
* @example
* ```ts
* decodeHandle("fiber-2-7").shape; // { kind: "fiber", tube: 2, fiber: 7 }
* decodeHandle(encodeHandle(x)).shape; // deep-equals x for non-verbatim x
* ```
* @public
*/
declare function decodeHandle(handleId: string | null | undefined): ParsedHandleId;
/**
* Build the top-level node id for a rack: `rack-` — the exact form the
* inventory/netbox converters mint.
*
* @remarks
* Use this instead of hand-writing the `rack-` prefix so consumer code stays in
* lockstep with the converters. Pairs with {@link parseNodeId} and
* {@link buildDeviceNodeId}. This is an id-string helper only; to decide whether
* a node is a rack, use {@link isRackNode} (which reads `node.type`).
*
* @example
* ```ts
* buildRackNodeId("42"); // "rack-42"
* buildRackNodeId(42); // "rack-42"
* ```
* @public
*/
declare function buildRackNodeId(rackId: string | number): string;
/**
* Build the top-level node id for a device: `device-` — the exact
* form the inventory/netbox converters mint.
*
* @remarks
* Consumers that layer a compound scheme on top (e.g.
* `device--`) can pass the already-composed id part as
* `deviceId`; {@link parseNodeId} returns it back verbatim. This is an
* id-string helper only; to decide whether a node is a device/equipment node,
* use {@link isDeviceNode} (which reads `node.type`).
*
* @example
* ```ts
* buildDeviceNodeId("7"); // "device-7"
* buildDeviceNodeId(`${rackId}-${u}`); // "device--"
* ```
* @public
*/
declare function buildDeviceNodeId(deviceId: string | number): string;
/**
* A parsed converter-minted node id.
* @public
*/
interface ParsedNodeId {
/** Which converter prefix produced the id. */
kind: "rack" | "device";
/**
* Everything after the prefix, verbatim. For a compound consumer scheme
* (`device--`) this is the whole `-` tail —
* decomposing it further is the consumer's concern.
*/
id: string;
}
/**
* Parse a converter-minted node id back into `{ kind, id }`, or `null` when the
* id carries neither the `rack-` nor the `device-` prefix.
*
* @remarks
* Total and prefix-only: it never throws and only recognizes the two
* converter-minted prefixes. It deliberately does NOT classify fibre-scene node
* ids (`fiber-3`, `tube-2`, `closure-1`, …) or bare ids — those return `null`.
* `kind` is derived from the id STRING here purely to invert the builders; do
* not use it as a node's semantic kind (that is `node.type`'s job — see
* {@link getNodeKind}).
*
* @example
* ```ts
* parseNodeId("rack-42"); // { kind: "rack", id: "42" }
* parseNodeId("device-r1-u2"); // { kind: "device", id: "r1-u2" }
* parseNodeId("fiber-3"); // null
* ```
* @public
*/
declare function parseNodeId(nodeId: string | null | undefined): ParsedNodeId | null;
/**
* Minimal shape read by the type-driven predicates — only `type` matters.
*
* @remarks
* This is the parameter type of the public {@link getNodeKind},
* {@link isRackNode} and {@link isDeviceNode}, so it is exported for consumers
* that need to name it (wrapper helpers, generic utilities). Any object with a
* `type` satisfies it — a full {@link NetworkNode}, a document node, or a bare
* `{ type: "rack" }` literal.
*
* @public
*/
type NodeTypeCarrier = Pick | {
type?: string | null;
};
/**
* Map a node's `type` to its {@link DiagramNodeKind}, mirroring the document
* adapter's authoritative discrimination (`toDiagramNodeKind`). Returns `null`
* when `type` is missing/empty.
*
* @remarks
* `node.type` — never the id string — is authoritative for what a node is. Use
* this (and {@link isRackNode}/{@link isDeviceNode}) rather than sniffing id
* prefixes. The mapping collapses `server`/`switch`/`router`/`device` into
* `equipment`, `vertical-pdu` into `pdu`, `patch-panel`/`closure`/`splice-tray`
* into `container`, and `fibre-split` into `splitter`; the remaining fibre/rack
* types map 1:1. Subtype detail (the adapter's `subtype`) is intentionally not
* returned — this is a kind-only helper.
*
* @example
* ```ts
* getNodeKind({ type: "switch" }); // "equipment"
* getNodeKind({ type: "rack" }); // "rack"
* getNodeKind({ type: undefined }); // null
* ```
* @public
*/
declare function getNodeKind(node: NodeTypeCarrier): DiagramNodeKind | null;
/**
* True when a node is a rack (`node.type === "rack"`). Type-driven — never sniffs
* the id.
*
* @remarks
* Prefer this over `node.id.startsWith("rack-")`: the id prefix is a converter
* detail, whereas `node.type` is the authoritative discriminator and survives
* the document round-trip.
*
* @example
* ```ts
* isRackNode({ type: "rack" }); // true
* isRackNode({ type: "device" }); // false
* ```
* @public
*/
declare function isRackNode(node: NodeTypeCarrier): boolean;
/**
* True when a node is a device/equipment node — i.e. its {@link getNodeKind} is
* `equipment` (`server`, `switch`, `router` or `device`). Type-driven — never
* sniffs the id.
*
* @remarks
* This is broader than `node.type === "device"`: the document model unifies the
* whole equipment family under one kind, and a rack-mounted switch or server is
* as much a "device node" as one the inventory converter happened to mint with
* `type: "device"`. If you need only the literal `device` type, compare
* `node.type` directly. Note the asymmetry with {@link buildDeviceNodeId}: the
* `device-` id prefix only covers converter-minted device ids, whereas this
* predicate covers the semantic equipment family.
*
* @example
* ```ts
* isDeviceNode({ type: "switch" }); // true
* isDeviceNode({ type: "rack" }); // false
* ```
* @public
*/
declare function isDeviceNode(node: NodeTypeCarrier): boolean;
/**
* Height of each holder position in pixels (one splice per holder)
* Different heights for ribbon mode (taller) vs single mode (compact)
*/
declare const HOLDER_HEIGHT_PX_SINGLE = 20;
declare const HOLDER_HEIGHT_PX_RIBBON = 36;
declare const HOLDER_HEIGHT_PX = 20;
/**
* Tray header height in pixels (smaller than rack header for compact trays)
*/
declare const TRAY_HEADER_HEIGHT = 32;
/**
* Rendered splice width in pixels — the exact width the SpliceNode renderer
* uses.
*
* @remarks
* Exposed so consumers composing a fibre scene by hand (positioning splices to
* line up with rendered tray/holder geometry) use the real value instead of a
* local copy that would silently drift when the renderer changes. Pairs with
* {@link SPLICE_HEIGHT_PX} and {@link calculateSplicePositionFromNumber}.
* @public
*/
declare const SPLICE_WIDTH_PX = 69;
/**
* Rendered splice height in pixels for single-mode (compact) splices.
* @public
*/
declare const SPLICE_HEIGHT_PX_SINGLE = 16;
/**
* Rendered splice height in pixels for ribbon-mode (taller) splices.
* @public
*/
declare const SPLICE_HEIGHT_PX_RIBBON = 32;
/**
* Rendered splice height in pixels. Defaults to single-mode; for ribbon splices
* use {@link SPLICE_HEIGHT_PX_RIBBON}.
*
* @remarks
* The height counterpart to {@link SPLICE_WIDTH_PX}: the exact value the
* SpliceNode renderer uses, exposed so hand-composed fibre scenes don't fork a
* magic number that drifts.
* @public
*/
declare const SPLICE_HEIGHT_PX = 16;
/**
* Tray width in pixels (narrower to fit splice properly)
*/
declare const TRAY_WIDTH_PX = 80;
/**
* Rendered splice-tray height when the tray holds ribbon splices.
*/
declare const TRAY_NODE_HEIGHT_RIBBON: number;
/** Default tray height — compact single-mode layout. */
declare const TRAY_NODE_HEIGHT: number;
/**
* Fixed X position to center splices within tray content
*/
declare const SPLICE_CENTER_X: number;
/**
* Compute the `{ x, y }` offset (relative to the tray node) at which a splice in
* a given 1-based holder position renders, in single (compact) mode.
*
* @remarks
* Pure geometry — no DOM, React or reactflow. Exposed so consumers hand-building
* a fibre closure scene (a tray with splices/tubes wired up and rendered through
* `NetworkDiagram`) position splices with the same formula the renderer uses,
* instead of forking it. This single-mode variant has no access to splice data;
* for ribbon splices use {@link calculateSplicePositionFromNumberWithData} with
* `isRibbonMode: true`. `x` is fixed at {@link SPLICE_CENTER_X}.
*
* @example
* ```ts
* calculateSplicePositionFromNumber(tray, 1); // { x: SPLICE_CENTER_X, y: 34 }
* ```
* @public
*/
declare const calculateSplicePositionFromNumber: (tray: NetworkNode, holderPosition: number) => {
x: number;
y: number;
};
/**
* Compute the `{ x, y }` offset (relative to the tray node) at which a splice in
* a given 1-based holder position renders, honoring the splice mode.
*
* @remarks
* Pure geometry — no DOM, React or reactflow. The mode-aware companion to
* {@link calculateSplicePositionFromNumber}: ribbon splices are taller
* ({@link SPLICE_HEIGHT_PX_RIBBON} in {@link HOLDER_HEIGHT_PX_RIBBON} holders)
* than single splices ({@link SPLICE_HEIGHT_PX_SINGLE} in
* {@link HOLDER_HEIGHT_PX_SINGLE} holders); the splice is centered vertically in
* its holder slot. `x` is fixed at {@link SPLICE_CENTER_X}.
*
* @example
* ```ts
* calculateSplicePositionFromNumberWithData(tray, 1, true); // ribbon: { x, y: 34 }
* ```
* @public
*/
declare const calculateSplicePositionFromNumberWithData: (tray: NetworkNode, holderPosition: number, isRibbonMode?: boolean) => {
x: number;
y: number;
};
/**
* Resolve the effective device face for a flat port input.
* @public
*/
declare function resolveDevicePortFace(port: Pick): DeviceFace;
/**
* Resolve the effective port group name for a flat port input.
* @public
*/
declare function resolveDevicePortGroup(port: Pick): string;
/**
* Normalize unknown **flat** port array input into canonical {@link DevicePort} entries.
*
* This handles a flat `DevicePort[]` only; grouped `PortBlock[]` input returns
* `undefined` (blocks have no `label`). To resolve flat ports from any supported
* form — including grouped `PortBlock[]` — use {@link resolveNodeDevicePorts}.
* @public
*/
declare function normalizeDevicePorts(value: unknown): DevicePort[] | undefined;
/**
* Resolve flat device ports from any supported port input.
*
* Accepts every form the renderer accepts (`getResolvedPortBlocks`): a node-data
* record (`ports` / `portGroups` / `rearPortGroups` / `rearPorts`), a flat
* `DevicePort[]`, or a grouped `PortBlock[]`. Grouped `PortBlock[]` carried on
* `ports` are treated as front groups (taking precedence over `portGroups`, matching
* the renderer); rear groups come from `rearPortGroups`, falling back to `rearPorts`.
* @public
*/
declare function resolveNodeDevicePorts(value: unknown): DevicePort[] | undefined;
/**
* Flatten grouped port blocks into canonical flat {@link DevicePort} entries.
* @public
*/
declare function flattenDevicePortsFromGroups({ portGroups, rearPortGroups, }: {
portGroups?: PortBlock[];
rearPortGroups?: PortBlock[];
}): DevicePort[];
/**
* Build grouped port blocks from flat device port input.
* @public
*/
declare function buildDevicePortGroupsFromPorts(ports: DevicePort[] | undefined): {
portGroups: PortBlock[];
rearPortGroups: PortBlock[];
};
interface ListPossiblePortHandleIdsOptions {
deviceType?: string;
face?: DeviceFace;
}
/**
* List the **canonical** handle ids that may exist on a rendered node for a port.
*
* The returned ids are in canonical form. When a device renders both faces and a
* front/rear handle id collides, the renderer disambiguates the hidden face with a
* `--rear` / `--front` suffix (see `normalizeHandleId`). This function does not emit
* those split-face aliases, so callers reconciling stored/rendered handles should
* compare through {@link normalizeHandleId} (which strips the suffix) rather than by
* raw string equality. {@link validatePortHandleOnNodeData} does this for you.
* @public
*/
declare function listPossiblePortHandleIds(portBlockName: string, port: Pick, options?: ListPossiblePortHandleIdsOptions): string[];
/**
* A rendered/stored handle id resolved back to the port that owns it.
* @public
*/
interface DescribedPortHandle {
/** The port the handle belongs to. */
port: DevicePort;
/** The port block (group) the port renders under. */
blockName: string;
/** The device face the block renders on. */
face: DeviceFace;
}
/**
* Resolve a handle id back to the port that owns it — the inverse of
* {@link buildPortHandleId} / {@link listPossiblePortHandleIds} over a node's
* resolved port blocks. Directional suffixes and split-face aliases are
* normalized before comparison (via {@link normalizeHandleId}), so ids taken
* straight from edges match. Pass `options.sanitizeHandle` to match under an
* additional app-side normalization (e.g. a `+`-insensitive comparison). Prefer
* {@link handlesMatch} / {@link findEdgesForPortHandle} for whole-handle matching.
* Returns `undefined` when no port on the node renders that handle.
* @public
*/
declare function describePortHandle(nodeData: Pick | undefined, handle: string, options?: {
deviceType?: string;
sanitizeHandle?: (value: string) => string;
}): DescribedPortHandle | undefined;
/**
* Validate that a handle id exists on node data after grouping resolution.
* @public
*/
declare function validatePortHandleOnNodeData(nodeData: Pick | undefined, handle: string, options?: {
deviceType?: string;
sanitizeHandle?: (value: string) => string;
}): boolean;
/**
* Convert flat ports to grouped blocks using the same rules as renderers.
* @public
*/
declare function resolveDevicePortBlocksFromFlatPorts(ports: DevicePort[]): {
portGroups: PortBlock[];
rearPortGroups: PortBlock[];
};
/**
* Classify a port/transceiver type string as fibre-bearing.
*
* Matching is case-insensitive and trims surrounding whitespace. Optical
* transceiver families (SFP/SFP+/SFP28/QSFP/QSFP+/QSFP28/QSFP56/QSFP-DD) and
* bare fibre connectors (LC/SC/MPO) return `true`; everything else (RJ45,
* copper, power, unknown) returns `false`.
*
* Server-safe: pure data code with no react/reactflow dependency.
* @public
*/
declare function isFibreConnectorType(portType: string): boolean;
/**
* Coerce a free-form cable descriptor into a canonical {@link PortCableType}.
*
* Accepts common aliases used by inventory sources — e.g. `smf_os2`,
* `single-mode`, `mmf_om4` — and normalizes them to the library's cable-type
* vocabulary. Unrecognized values fall back to `cat6` when they look like a
* copper category (`cat*`) and to `ethernet` otherwise. Nullish/blank input
* resolves to `ethernet`.
*
* Server-safe: pure data code with no react/reactflow dependency.
* @public
*/
declare function toPortCableType(value: string): PortCableType;
declare const parseSemanticPortPosition: (value: string) => PortPosition | undefined;
declare const normalizePortPositionInput: (value: unknown) => PortPosition | undefined;
interface NetworkGraphSnapshot {
nodes: NetworkNode[];
edges: NetworkEdge[];
}
/**
* Lenient input accepted by {@link diagramDocumentToNetworkGraph}. Everything is
* optional so partial / legacy documents degrade gracefully instead of throwing:
* a missing `version` is treated as legacy v0 (identical to `1`), and missing
* `nodes`/`edges` default to empty. A full {@link DiagramDocument} is assignable.
*/
type DiagramDocumentInput = {
version?: number;
nodes?: DiagramNode[];
edges?: DiagramEdge[];
ui?: DiagramUiState;
metadata?: Record;
};
/**
* Convert a runtime {@link NetworkGraphSnapshot} into the canonical, persistable
* {@link DiagramDocument}. The emitted document always carries `version: 1`.
*
* Container kinds (`patch-panel`/`closure`/`splice-tray`) serialize their ports
* as face-tagged `endpoints`, which {@link diagramDocumentToNetworkGraph}
* reconstructs back into `portGroups`/`rearPortGroups`.
*/
declare function networkGraphToDiagramDocument(snapshot: NetworkGraphSnapshot, previousDocument?: DiagramDocument): DiagramDocument;
/**
* Convert a persisted {@link DiagramDocument} (or a lenient
* {@link DiagramDocumentInput}) back into a runtime graph.
*
* Untrusted input never throws: malformed nodes/endpoints/edges are skipped
* with a `console.warn`, and edges whose source or target node is absent from
* the produced graph (unknown kind, malformed, or simply missing) are dropped
* instead of being emitted dangling.
*/
declare function diagramDocumentToNetworkGraph(document: DiagramDocumentInput): NetworkGraphSnapshot;
type PathLayoutDirection = 'horizontal' | 'vertical';
interface PathLayoutStepInput {
id: string;
type?: NetworkNode['type'];
data?: NetworkNode['data'];
}
interface PathLayoutEdgeInput {
id: string;
source: string;
target: string;
type?: NetworkEdge['type'];
data?: NetworkEdge['data'];
}
interface BuildPathLayoutOptions {
direction?: PathLayoutDirection;
spacing?: number;
origin?: {
x: number;
y: number;
};
}
/**
* Lay out a sequential path of nodes and connecting edges with deterministic spacing.
* @public
*/
declare function buildPathLayoutGraph(steps: PathLayoutStepInput[], edges: PathLayoutEdgeInput[], options?: BuildPathLayoutOptions): {
nodes: NetworkNode[];
edges: NetworkEdge[];
};
/**
* Normalize a rendered handle id back to its canonical endpoint id.
* The `node` parameter is retained for call-site ergonomics and future
* node-specific normalization; today all node types share one rule.
*/
declare const normalizeHandle: (_node?: NetworkNode, handle?: string | null) => string;
/** Result shape shared with the store's `ValidationResult`. */
interface ConnectionRuleResult {
valid: boolean;
reason?: string;
}
/**
* Structural classification of a handle that participates in fiber routing.
* This is the single place that maps a (node, handle) pair to its fiber role;
* the connection matrix below is expressed purely in terms of these classes.
*/
type FibreEndpointClass = 'tube-fiber' | 'tube-tube' | 'cable-tube' | 'cable-button' | 'splice-side' | 'coupler-port' | 'splitter-input' | 'splitter-output' | 'other';
declare function classifyFibreEndpoint(node: NetworkNode | undefined, handle: string | null | undefined): FibreEndpointClass;
/**
* Evaluate whether a fiber-domain connection is structurally allowed.
*
* This is the single source of truth for fiber connection rules, consumed by
* both the headless store validator and the interactive `NetworkDiagram`
* validator. It does not consider handle occupancy (capacity) — fiber handles
* support edge replacement, so occupancy is resolved by the caller.
*/
declare function evaluateFibreConnectionRule(sourceNode: NetworkNode | undefined, sourceHandle: string | null | undefined, targetNode: NetworkNode | undefined, targetHandle: string | null | undefined): ConnectionRuleResult;
export { type ApplyDirectionalPortHandleIdOptions, BOARD_INCOMER_HANDLE_ID, type BuildPathLayoutOptions, type ConnectionRuleResult, type DescribedPortHandle, DeviceFace, DevicePort, DiagramDocument, type DiagramDocumentInput, DiagramEdge, DiagramNode, DiagramNodeKind, DiagramUiState, type EncodeHandleOptions, type FibreEndpointClass, HOLDER_HEIGHT_PX, HOLDER_HEIGHT_PX_RIBBON, HOLDER_HEIGHT_PX_SINGLE, type HandleShape, type ListPossiblePortHandleIdsOptions, NetworkEdge, type NetworkGraphSnapshot, NetworkNode, NetworkNodeDataBase, type NodeTypeCarrier, PDU_INLET_HANDLE_ID, type ParsedHandleId, type ParsedNodeId, type PathLayoutDirection, type PathLayoutEdgeInput, type PathLayoutStepInput, Port, PortBlock, PortCableType, PortPosition, SPLICE_CENTER_X, SPLICE_HEIGHT_PX, SPLICE_HEIGHT_PX_RIBBON, SPLICE_HEIGHT_PX_SINGLE, SPLICE_WIDTH_PX, TRAY_HEADER_HEIGHT, TRAY_NODE_HEIGHT, TRAY_NODE_HEIGHT_RIBBON, TRAY_WIDTH_PX, applyDirectionalPortHandleId, applyFaceAliasHandleId, buildBreakerWayHandleId, buildCouplerHandleId, buildDeviceNodeId, buildDevicePortGroupsFromPorts, buildFibreHandleId, buildPathLayoutGraph, buildPortHandleId, buildRackNodeId, buildSpliceHandleId, buildSplitIoHandleId, buildTubeHandleId, calculateSplicePositionFromNumber, calculateSplicePositionFromNumberWithData, classifyFibreEndpoint, decodeHandle, describePortHandle, diagramDocumentToNetworkGraph, encodeHandle, evaluateFibreConnectionRule, findEdgesForPortHandle, flattenDevicePortsFromGroups, getNodeKind, getOppositeSideHandleId, handlesMatch, isDeviceNode, isFibreConnectorType, isPowerInletHandleId, isPowerPortType, isRackNode, listPossiblePortHandleIds, looksLikePowerHandleId, networkGraphToDiagramDocument, normalizeDevicePorts, normalizeDirectionalHandleId, normalizeHandle, normalizeHandleId, normalizePortPositionInput, parseHandleId, parseNodeId, parseSemanticPortPosition, resolveDevicePortBlocksFromFlatPorts, resolveDevicePortFace, resolveDevicePortGroup, resolveNodeDevicePorts, toPortCableType, validatePortHandleOnNodeData };