@mp70/react-networks 0.5.1 → 0.7.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/LICENSE +46 -0
- package/README.md +13 -11
- package/dist/document-adapter-B-6MM_tj.d.mts +1449 -0
- package/dist/document-adapter-B-6MM_tj.d.ts +1449 -0
- package/dist/index.d.mts +49 -1244
- package/dist/index.d.ts +49 -1244
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/model.d.mts +33 -0
- package/dist/model.d.ts +33 -0
- package/dist/model.js +2 -0
- package/dist/model.js.map +1 -0
- package/dist/model.mjs +2 -0
- package/dist/model.mjs.map +1 -0
- package/package.json +46 -5
package/dist/model.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { a as NetworkNode } from './document-adapter-B-6MM_tj.mjs';
|
|
2
|
+
export { A as ApplyDirectionalPortHandleIdOptions, B as BuildPathLayoutOptions, v as DescribedPortHandle, o as DeviceFace, n as DevicePort, j as DiagramDocument, z as DiagramEdge, C as DiagramEdgeEndpoint, E as DiagramEndpoint, H as DiagramNode, J as DiagramNodePlacement, K as DiagramNodeRender, V as HandleShape, W as ListPossiblePortHandleIdsOptions, i as NetworkEdge, a6 as NetworkGraphSnapshot, a9 as NetworkNodeType, aa as ParsedHandleId, ab as PathLayoutDirection, ac as PathLayoutEdgeInput, ad as PathLayoutStepInput, ae as Port, P as PortBlock, t as PortCableType, u as PortPosition, p as PortType, k as RackConfig, al as applyDirectionalPortHandleId, am as applyFaceAliasHandleId, an as buildCouplerHandleId, ao as buildDevicePortGroupsFromPorts, ap as buildFiberHandleId, aq as buildPathLayoutGraph, ar as buildPortHandleId, as as buildReactNetworksHandleId, at as buildReactNetworksPortGroupsFromPorts, au as buildSpliceHandleId, av as buildSplitIoHandleId, aw as buildTubeHandleId, ax as describePortHandle, ay as diagramDocumentToNetworkGraph, aA as flattenReactNetworksPortGroups, aB as getOppositeSideHandleId, aC as isPowerPortType, aD as listPossiblePortHandleIds, aE as looksLikePowerHandleId, aF as networkGraphToDiagramDocument, aG as normalizeDevicePorts, aH as normalizeDirectionalHandleId, aI as normalizeHandleId, aJ as normalizePortPositionInput, aL as parseHandleId, aM as parseSemanticPortPosition, aN as resolveDevicePortBlocksFromFlatPorts, aP as resolveDevicePortGroup, aQ as resolveNodeDevicePorts, aR as resolveReactNetworksNodePorts, aT as resolveReactNetworksPortGroup, aU as sanitizeHandleIdForMatching, aV as validatePortHandleOnNodeData } from './document-adapter-B-6MM_tj.mjs';
|
|
3
|
+
import 'reactflow';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a rendered handle id back to its canonical endpoint id.
|
|
7
|
+
* The `node` parameter is retained for call-site ergonomics and future
|
|
8
|
+
* node-specific normalization; today all node types share one rule.
|
|
9
|
+
*/
|
|
10
|
+
declare const normalizeHandle: (_node?: NetworkNode, handle?: string | null) => string;
|
|
11
|
+
/** Result shape shared with the store's `ValidationResult`. */
|
|
12
|
+
interface ConnectionRuleResult {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Structural classification of a handle that participates in fiber routing.
|
|
18
|
+
* This is the single place that maps a (node, handle) pair to its fiber role;
|
|
19
|
+
* the connection matrix below is expressed purely in terms of these classes.
|
|
20
|
+
*/
|
|
21
|
+
type FiberEndpointClass = 'tube-fiber' | 'tube-tube' | 'cable-tube' | 'cable-button' | 'splice-side' | 'coupler-port' | 'splitter-input' | 'splitter-output' | 'other';
|
|
22
|
+
declare function classifyFiberEndpoint(node: NetworkNode | undefined, handle: string | null | undefined): FiberEndpointClass;
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate whether a fiber-domain connection is structurally allowed.
|
|
25
|
+
*
|
|
26
|
+
* This is the single source of truth for fiber connection rules, consumed by
|
|
27
|
+
* both the headless store validator and the interactive `NetworkDiagram`
|
|
28
|
+
* validator. It does not consider handle occupancy (capacity) — fiber handles
|
|
29
|
+
* support edge replacement, so occupancy is resolved by the caller.
|
|
30
|
+
*/
|
|
31
|
+
declare function evaluateFiberConnectionRule(sourceNode: NetworkNode | undefined, sourceHandle: string | null | undefined, targetNode: NetworkNode | undefined, targetHandle: string | null | undefined): ConnectionRuleResult;
|
|
32
|
+
|
|
33
|
+
export { type FiberEndpointClass, NetworkNode, classifyFiberEndpoint, evaluateFiberConnectionRule, normalizeHandle };
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { a as NetworkNode } from './document-adapter-B-6MM_tj.js';
|
|
2
|
+
export { A as ApplyDirectionalPortHandleIdOptions, B as BuildPathLayoutOptions, v as DescribedPortHandle, o as DeviceFace, n as DevicePort, j as DiagramDocument, z as DiagramEdge, C as DiagramEdgeEndpoint, E as DiagramEndpoint, H as DiagramNode, J as DiagramNodePlacement, K as DiagramNodeRender, V as HandleShape, W as ListPossiblePortHandleIdsOptions, i as NetworkEdge, a6 as NetworkGraphSnapshot, a9 as NetworkNodeType, aa as ParsedHandleId, ab as PathLayoutDirection, ac as PathLayoutEdgeInput, ad as PathLayoutStepInput, ae as Port, P as PortBlock, t as PortCableType, u as PortPosition, p as PortType, k as RackConfig, al as applyDirectionalPortHandleId, am as applyFaceAliasHandleId, an as buildCouplerHandleId, ao as buildDevicePortGroupsFromPorts, ap as buildFiberHandleId, aq as buildPathLayoutGraph, ar as buildPortHandleId, as as buildReactNetworksHandleId, at as buildReactNetworksPortGroupsFromPorts, au as buildSpliceHandleId, av as buildSplitIoHandleId, aw as buildTubeHandleId, ax as describePortHandle, ay as diagramDocumentToNetworkGraph, aA as flattenReactNetworksPortGroups, aB as getOppositeSideHandleId, aC as isPowerPortType, aD as listPossiblePortHandleIds, aE as looksLikePowerHandleId, aF as networkGraphToDiagramDocument, aG as normalizeDevicePorts, aH as normalizeDirectionalHandleId, aI as normalizeHandleId, aJ as normalizePortPositionInput, aL as parseHandleId, aM as parseSemanticPortPosition, aN as resolveDevicePortBlocksFromFlatPorts, aP as resolveDevicePortGroup, aQ as resolveNodeDevicePorts, aR as resolveReactNetworksNodePorts, aT as resolveReactNetworksPortGroup, aU as sanitizeHandleIdForMatching, aV as validatePortHandleOnNodeData } from './document-adapter-B-6MM_tj.js';
|
|
3
|
+
import 'reactflow';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a rendered handle id back to its canonical endpoint id.
|
|
7
|
+
* The `node` parameter is retained for call-site ergonomics and future
|
|
8
|
+
* node-specific normalization; today all node types share one rule.
|
|
9
|
+
*/
|
|
10
|
+
declare const normalizeHandle: (_node?: NetworkNode, handle?: string | null) => string;
|
|
11
|
+
/** Result shape shared with the store's `ValidationResult`. */
|
|
12
|
+
interface ConnectionRuleResult {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Structural classification of a handle that participates in fiber routing.
|
|
18
|
+
* This is the single place that maps a (node, handle) pair to its fiber role;
|
|
19
|
+
* the connection matrix below is expressed purely in terms of these classes.
|
|
20
|
+
*/
|
|
21
|
+
type FiberEndpointClass = 'tube-fiber' | 'tube-tube' | 'cable-tube' | 'cable-button' | 'splice-side' | 'coupler-port' | 'splitter-input' | 'splitter-output' | 'other';
|
|
22
|
+
declare function classifyFiberEndpoint(node: NetworkNode | undefined, handle: string | null | undefined): FiberEndpointClass;
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate whether a fiber-domain connection is structurally allowed.
|
|
25
|
+
*
|
|
26
|
+
* This is the single source of truth for fiber connection rules, consumed by
|
|
27
|
+
* both the headless store validator and the interactive `NetworkDiagram`
|
|
28
|
+
* validator. It does not consider handle occupancy (capacity) — fiber handles
|
|
29
|
+
* support edge replacement, so occupancy is resolved by the caller.
|
|
30
|
+
*/
|
|
31
|
+
declare function evaluateFiberConnectionRule(sourceNode: NetworkNode | undefined, sourceHandle: string | null | undefined, targetNode: NetworkNode | undefined, targetHandle: string | null | undefined): ConnectionRuleResult;
|
|
32
|
+
|
|
33
|
+
export { type FiberEndpointClass, NetworkNode, classifyFiberEndpoint, evaluateFiberConnectionRule, normalizeHandle };
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var U=/-(source|target)$/i,R=/--(rear|front)$/i;function k(e){return e?e.replace(U,""):""}function p(e){return k(e).replace(R,"")}function l(e){return typeof e=="string"&&e.toLowerCase().includes("power")}function D(e,t){return typeof t.id=="string"&&t.id.length>0?t.id:l(t.type)?t.label:`${e}-${t.label}`}function W(e){let t=k(e).toLowerCase();return t.startsWith("pwr")||t.includes("power")}function ae(e,t,n){return !t||(typeof n?.portType=="string"&&n.portType.length>0?l(n.portType):W(e))?e:`${k(e)}-${t}`}var se=/^\d+$/,ce=/^fiber-(\d+)-(\d+)$/,ue=/^fiber-(\d+)$/,de=/^tube-(\d+)$/,pe=/^(left|right)-(\d+)$/,le=/^(input|output)-(\d+)$/;function fe(e){if(se.test(e))return {kind:"outlet",outlet:Number(e)};let t=ce.exec(e);if(t)return {kind:"fiber",tube:Number(t[1]),fiber:Number(t[2])};let n=ue.exec(e);if(n)return {kind:"fiber",fiber:Number(n[1])};let r=de.exec(e);if(r)return {kind:"tube",tube:Number(r[1])};if(e==="left"||e==="right")return {kind:"splice-side",side:e};let i=pe.exec(e);if(i)return {kind:"side-port",side:i[1],index:Number(i[2])};let a=le.exec(e);return a?{kind:"split-io",io:a[1],index:Number(a[2])}:e==="cable-button"?{kind:"cable-button"}:{kind:"port"}}function T(e){let t=e??"",n=U.exec(t),r=n?t.slice(0,t.length-n[0].length):t,i=R.exec(r),a=i?r.slice(0,r.length-i[0].length):r,o={raw:t,base:a,shape:fe(a)};return n&&(o.direction=n[1].toLowerCase()),i&&(o.faceAlias=i[1].toLowerCase()),o}function me(e,t){return t===void 0?`fiber-${e}`:`fiber-${t}-${e}`}function be(e){return `tube-${e}`}function Pe(e){return e}function j(e,t){return `${e}-${t}`}function ye(e,t){return `${e}-${t}`}function ge(e,t){return `${e.replace(R,"")}--${t}`}function he(e){return e.replace(/\+/g,"")}function Ee(e){let{shape:t}=T(e);if(t.kind==="splice-side")return t.side==="left"?"right":"left";if(t.kind==="side-port")return j(t.side==="left"?"right":"left",t.index)}var Ne=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),m=e=>{if(typeof e=="number"&&Number.isFinite(e))return e;if(typeof e=="string"&&e.trim().length>0){let t=Number(e);if(Number.isFinite(t))return t}},ke=e=>e.trim().toLowerCase().split(/[^a-z0-9]+/).filter(t=>t.length>0),G=e=>{if(e==="top"||e==="up")return "top";if(e==="right")return "right";if(e==="bottom"||e==="down")return "bottom";if(e==="left")return "left"},K=e=>e.includes("center")||e.includes("centre")||e.includes("middle")||e.includes("mid"),De=(e,t)=>{let n=t.includes("left"),r=t.includes("right"),i=t.includes("top")||t.includes("up"),a=t.includes("bottom")||t.includes("down"),o=K(t);if(e==="top"||e==="bottom")return n&&o&&!r?34:r&&o&&!n?66:n&&!r?12:r&&!n?88:o?50:void 0;if(i&&o&&!a)return 34;if(a&&o&&!i)return 66;if(i&&!a)return 12;if(a&&!i)return 88;if(o)return 50},Te=e=>{if(e.includes("front")||e.includes("rear")||e.includes("back"))return;let n=e.map(G).find(i=>i!==void 0);if(!n)return;let r=De(n,e);if(r!==void 0)return {side:n,offsetPercent:r}},xe=e=>{if(e.length===0)return;let t=e.includes("left"),n=e.includes("right"),r=K(e),i=e.includes("front")||e.includes("top")||e.includes("up"),a=e.includes("rear")||e.includes("back")||e.includes("bottom")||e.includes("down"),o;t&&r&&!n?o=34:n&&r&&!t?o=66:t&&!n?o=12:n&&!t?o=88:r&&(o=50);let s;if(i&&!a?s=22:a&&!i?s=78:r&&(s=50),!(o===void 0||s===void 0))return {xPercent:o,yPercent:s}},C=e=>{let t=ke(e);if(t.length!==0){if(t.length===1){let n=G(t[0]);return n?{side:n}:void 0}return Te(t)??xe(t)}},b=e=>{if(typeof e=="string")return C(e);if(!Ne(e))return;let t=e.side??e.port_side??e.portSide,n=typeof t=="string"?C(t):void 0,i=(typeof t=="string"?G(t.trim().toLowerCase()):void 0)??n?.side,a=m(e.offsetPercent??e.offset_percent??e.offsetPct??e.offset_pct)??n?.offsetPercent,o=m(e.offsetPx??e.offset_px??e.offsetPixels??e.offset_pixels)??n?.offsetPx,s=m(e.xPercent??e.x_percent??e.xPct??e.x_pct??e.horizontalPercent??e.horizontal_percent??e.x)??n?.xPercent,c=m(e.yPercent??e.y_percent??e.yPct??e.y_pct??e.verticalPercent??e.vertical_percent??e.y)??n?.yPercent,u=m(e.widthPercent??e.width_percent??e.widthPct??e.width_pct??e.wPercent??e.w_percent),f=m(e.heightPercent??e.height_percent??e.heightPct??e.height_pct??e.hPercent??e.h_percent);if(!(i===void 0&&a===void 0&&o===void 0&&s===void 0&&c===void 0&&u===void 0&&f===void 0))return {side:i,offsetPercent:a,offsetPx:o,xPercent:s,yPercent:c,widthPercent:u,heightPercent:f}};var we="Network Ports",_e="Rear Network Ports",Fe="Power Ports",Y=8,Ie=92;function P(e){return Math.max(0,Math.min(100,e))}function A(e){if(e==="top"||e==="right"||e==="bottom"||e==="left")return e}function y(e){return b(e)}function q(e){let t=y(e.position);return typeof t?.xPercent=="number"&&typeof t?.yPercent=="number"}function Re(e){let t=y(e.position);return typeof t?.offsetPercent=="number"||typeof t?.offsetPx=="number"}function Ce(e){return e.face==="rear"||e.face==="back"?"rear":e.face==="front"?"front":l(e.type)?"rear":"front"}function Ge(e,t){return typeof e.group=="string"&&e.group.trim().length>0?e.group.trim():l(e.type)?Fe:t==="rear"?_e:we}function w(e){return typeof e.port.order=="number"&&Number.isFinite(e.port.order)?e.port.order:e.index}function Se(e,t){return t<=1?50:Y+e*(Ie-Y)/(t-1)}function Ae(e){let t=e.filter(i=>!!A(y(i.port.position)?.side)&&!q(i.port)&&!Re(i.port)),n=new Map,r=new Map;return t.forEach(i=>{let a=A(y(i.port.position)?.side);if(!a)return;let o=r.get(a);if(o){o.push(i);return}r.set(a,[i]);}),r.forEach(i=>{let a=[...i].sort((o,s)=>{let c=w(o)-w(s);return c!==0?c:o.index-s.index});a.forEach((o,s)=>{n.set(o.index,Se(s,a.length));});}),n}function He(e,t){let n=y(e.position),r=A(n?.side);if(q(e)){let s={xPercent:P(n?.xPercent),yPercent:P(n?.yPercent)};return typeof n?.widthPercent=="number"&&Number.isFinite(n.widthPercent)&&(s.widthPercent=P(n.widthPercent)),typeof n?.heightPercent=="number"&&Number.isFinite(n.heightPercent)&&(s.heightPercent=P(n.heightPercent)),r&&(s.side=r),s}if(!r)return;let i={side:r};if(typeof n?.offsetPx=="number"&&Number.isFinite(n.offsetPx))return i.offsetPx=n.offsetPx,i;let o=(typeof n?.offsetPercent=="number"&&Number.isFinite(n.offsetPercent)?P(n.offsetPercent):void 0)??(typeof t=="number"?P(t):void 0);return typeof o=="number"&&(i.offsetPercent=o),i}function Oe(e,t){let n=He(e.port,t);return {id:e.port.id,label:e.port.label,sourceTypeLabel:e.port.sourceTypeLabel,connected:e.port.connected??false,type:e.port.type,cableType:e.port.cableType,connectorType:e.port.connectorType,compatibility:Array.isArray(e.port.compatibility)?[...e.port.compatibility]:void 0,...n?{position:n}:{}}}function x(e){return e.map(t=>({...t,ports:t.ports.map(n=>{let r=y(n.position);return {...n,...r?{position:r}:{position:void 0}}})}))}function S(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t!==null&&typeof t=="object"&&typeof t.name=="string"&&Array.isArray(t.ports))}function Be(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t!==null&&typeof t=="object"&&typeof t.label=="string"&&!(typeof t.name=="string"&&Array.isArray(t.ports)))}function H(e){let t=new Map;e.forEach((i,a)=>{if(typeof i.label!="string"||i.label.trim().length===0)return;let o=Ce(i),s=Ge(i,o),c=`${o}:${s}`,u=t.get(c);if(u){u.entries.push({index:a,port:{...i,label:i.label.trim()}});return}t.set(c,{face:o,groupName:s,entries:[{index:a,port:{...i,label:i.label.trim()}}]});});let n=[],r=[];return t.forEach(i=>{let a=[...i.entries].sort((c,u)=>{let f=w(c)-w(u);return f!==0?f:c.index-u.index}),o=Ae(a),s={name:i.groupName,ports:a.map(c=>Oe(c,o.get(c.index)))};if(i.face==="rear"){r.push(s);return}n.push(s);}),{ports:n,rearPorts:r}}function g(e){return S(e?.ports)?{ports:x(e.ports),rearPorts:x(Array.isArray(e?.rearPortGroups)?e.rearPortGroups:S(e?.rearPorts)?e.rearPorts:[])}:Be(e?.ports)?H(e.ports):{ports:x(Array.isArray(e?.portGroups)?e.portGroups:[]),rearPorts:x(Array.isArray(e?.rearPortGroups)?e.rearPortGroups:S(e?.rearPorts)?e.rearPorts:[])}}var ve=D,Le="Network Ports",Me="Rear Network Ports",$e="Power Ports",_=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),d=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},X=e=>{let t=d(e)?.toLowerCase();if(t==="front")return "front";if(t==="rear"||t==="back")return "rear"},F=e=>{let t=d(e)?.toUpperCase();if(t==="C13"||t==="C14"||t==="C19"||t==="C20")return t},ze=e=>l(e)?"rear":"front",Ue=({face:e,type:t})=>l(t)?$e:e==="rear"?Me:Le;function V(e){return X(e.face)??ze(e.type)}function I(e){return d(e.group)??Ue({face:V(e),type:e.type})}var We=I;function O(e){if(!Array.isArray(e))return;let t=e.map(n=>{if(!_(n))return null;let r=d(n.label);if(!r)return null;let i=d(n.type),a=V({face:X(n.face),type:i}),o=I({face:a,group:d(n.group),type:i}),s=b(n.position??n),c=typeof n.order=="number"&&Number.isFinite(n.order)?n.order:void 0;return {...d(n.id)?{id:d(n.id)}:{},label:r,face:a,group:o,connected:typeof n.connected=="boolean"?n.connected:void 0,...i?{type:i}:{},...d(n.cableType)?{cableType:d(n.cableType)}:{},...F(n.connectorType)?{connectorType:F(n.connectorType)}:{},...typeof c=="number"?{order:c}:{},...s?{position:s}:{}}}).filter(n=>n!==null);return t.length>0?t:void 0}function h(e,t){return Array.isArray(e)?e.flatMap(n=>!_(n)||!Array.isArray(n.ports)?[]:n.ports.map((r,i)=>{if(!_(r))return null;let a=d(r.label);if(!a)return null;let o=d(r.type),s=I({face:t,group:d(n.name),type:o}),c=b(r.position??r),u=typeof r.order=="number"&&Number.isFinite(r.order)?r.order:i;return {...d(r.id)?{id:d(r.id)}:{},label:a,face:t,group:s,connected:typeof r.connected=="boolean"?r.connected:void 0,...o?{type:o}:{},...d(r.cableType)?{cableType:d(r.cableType)}:{},...F(r.connectorType)?{connectorType:F(r.connectorType)}:{},order:u,...c?{position:c}:{}}}).filter(r=>r!==null)):[]}function J(e){if(_(e)){let r=O(e.ports);if(r)return r;let i=h(e.ports,"front"),a=i.length>0?i:h(e.portGroups,"front"),o=h(e.rearPortGroups,"rear"),s=o.length>0?o:h(e.rearPorts,"rear"),c=[...a,...s];return c.length>0?c:void 0}let t=O(e);if(t)return t;let n=h(e,"front");return n.length>0?n:void 0}var je=J;function Ke({portGroups:e,rearPortGroups:t}){let n=[],r=(i,a)=>{i?.forEach(o=>{o.ports.forEach((s,c)=>{n.push({...typeof s.id=="string"&&s.id.trim().length>0?{id:s.id}:{},label:s.label,face:a,group:o.name,connected:s.connected,...s.type?{type:s.type}:{},...s.cableType?{cableType:s.cableType}:{},...s.connectorType?{connectorType:s.connectorType}:{},order:c,...s.position?{position:s.position}:{}});});});};return r(e,"front"),r(t,"rear"),n}var Ye=Ke;function B(e){if(!Array.isArray(e)||e.length===0)return {portGroups:[],rearPortGroups:[]};let{ports:t,rearPorts:n}=H(e);return {portGroups:t,rearPortGroups:n}}var qe=B;function Q(e,t,n={}){let r=D(e,t);if(l(t.type))return [r];let i=String(n.deviceType??"").toLowerCase(),a=n.face??"front";return i==="switch"||i==="router"?[r,`${r}-target`]:i==="patch-panel"||i==="patch_panel"?a==="rear"?[r,`${r}-target`]:[r,`${r}-source`]:[r]}function Z(e,t,n={}){let r=n.sanitizeHandle??(c=>p(c)),i=r(t),{ports:a,rearPorts:o}=g(e),s=(c,u)=>{for(let f of c)for(let z of f.ports)if(Q(f.name,z,{deviceType:n.deviceType,face:u}).some(ie=>r(ie)===i))return {port:z,blockName:f.name,face:u}};return s(a,"front")??s(o,"rear")}function Xe(e,t,n={}){return Z(e,t,n)!==void 0}function Ve(e){return B(e)}function ee(e){return e%10<8?"C13":"C19"}var Je=new Set(["patch-panel","closure","splice-tray"]),v=["frontImageUrl","rearImageUrl","frontImageAttribution","rearImageAttribution","imageFit","imagePosition","imageRepeat","stretchToFit"],L=["panelWidth","panelHeight","closureWidth","closureHeight","headerHeight"],M=["status","width","widthPx","height","header","color","colorMode","showUMarkers","customHeaderText","powerSide","rotation","rackWidthPx","rackFaceGapPx"],Qe=["face","handleSideMap"],Ze=new Set(["label","ports","portGroups","rearPortGroups","pduPortGroups","connections","netboxId","netboxType","inventoryId","inventoryType","uPosition","mountMode","rackLayout","slotPosition","holderPosition",...v,...L,...M,...Qe]);function re(e){return {x:typeof e?.x=="number"?e.x:0,y:typeof e?.y=="number"?e.y:0}}function N(e){if(e)return {...e}}function E(e){return {...e,ports:e.ports.map(t=>({...t,compatibility:Array.isArray(t.compatibility)?[...t.compatibility]:void 0,position:N(t.position)}))}}function et(e){if(typeof e.parentId=="string")return e.parentId;let t=e.parentNode;return typeof t=="string"?t:void 0}function tt(e){return e.type==="vertical-pdu"?{kind:"pdu"}:e.type==="server"||e.type==="switch"||e.type==="router"||e.type==="device"?{kind:"equipment",subtype:typeof e.data?.deviceType=="string"&&e.data.deviceType.trim().length>0?e.data.deviceType:e.type}:e.type==="patch-panel"||e.type==="closure"||e.type==="splice-tray"?{kind:"container",subtype:e.type}:e.type==="fibre-split"?{kind:"splitter"}:e.type==="fibre-flow-cable"||e.type==="fibre-flow-closure"?{kind:e.type}:{kind:e.type}}function rt(e){if(e.kind==="equipment"){let t=(e.subtype??"device").toLowerCase().replace(/_/g,"-");return t==="server"||t==="switch"||t==="router"?t:"device"}if(e.kind==="pdu")return "vertical-pdu";if(e.kind==="container"){let t=e.subtype??"patch-panel";return Je.has(t)?t:"patch-panel"}return e.kind==="splitter"?"fibre-split":e.kind}function nt(e){let t={position:re(e.position)},n=et(e);n&&(t.parentId=n),e.extent==="parent"&&(t.extent="parent");let r=e.data??{};return (typeof r.uPosition=="number"||r.mountMode==="front"||r.mountMode==="rear"||r.mountMode==="both"||r.rackLayout==="single-face"||r.rackLayout==="split-face")&&(t.rack={...typeof r.uPosition=="number"?{uPosition:r.uPosition}:{},...r.mountMode?{mountMode:r.mountMode}:{},...r.rackLayout?{rackLayout:r.rackLayout}:{},...typeof r.rackWidthPx=="number"?{rackWidthPx:r.rackWidthPx}:{},...typeof r.rackFaceGapPx=="number"?{rackFaceGapPx:r.rackFaceGapPx}:{}}),(typeof r.slotPosition=="number"||typeof r.holderPosition=="number")&&(t.container={...typeof r.slotPosition=="number"?{slotPosition:r.slotPosition}:{},...typeof r.holderPosition=="number"?{holderPosition:r.holderPosition}:{}}),t}function ot(e){let t=e.data??{},n={};M.forEach(a=>{let o=t[a];o!==void 0&&(n[a]=o);});let r=v.reduce((a,o)=>{let s=t[o];return s!==void 0&&(a[o]=s),a},{});Object.keys(r).length>0&&(n.images=r);let i=L.reduce((a,o)=>{let s=t[o];return typeof s=="number"&&(a[o]=s),a},{});return Object.keys(i).length>0&&(n.dimensions=i),Object.keys(n).length>0?n:void 0}function it(e){let t=[],n=e.data??{};return n.netboxId!==void 0&&t.push({source:"netbox",id:n.netboxId,type:n.netboxType}),n.inventoryId!==void 0&&t.push({source:"inventory",id:n.inventoryId,type:n.inventoryType}),t.length>0?t:void 0}function at(e){let t=e.data??{},n={};return Object.entries(t).forEach(([r,i])=>{Ze.has(r)||(r==="rackWidthPx"||r==="rackFaceGapPx")&&e.type!=="rack"||(n[r]=i);}),Object.keys(n).length>0?n:void 0}function st(e,t){let n={};if(e.forEach(r=>{let i=r.data??{},a={},o=i.face==="front"||i.face==="rear"?i.face:void 0;o&&(a.face=o),i.handleSideMap&&typeof i.handleSideMap=="object"&&(a.handleSideMap={...i.handleSideMap}),Object.keys(a).length>0&&(n[r.id]=a);}),!(Object.keys(n).length===0&&!t?.metadata))return {...Object.keys(n).length>0?{nodeState:n}:{},...t?.metadata?{metadata:t.metadata}:{}}}function ct(e){let t=String(e??"").toLowerCase();return t.includes("power")?"power":t.includes("fiber")?"fiber":t.includes("console")||t.includes("mgmt")||t.includes("usb")?"generic":t.includes("ethernet")||t.includes("rj45")?"ethernet":"generic"}function ut(e){let t=e.data??{};if(Array.isArray(t.pduPortGroups)&&t.pduPortGroups.length>0)return t.pduPortGroups.map(E);let{ports:n,rearPorts:r}=g(t);if(r.length>0)return r.map(E);if(n.length>0)return n.map(E);let i=typeof t.powerPorts=="number"&&Number.isFinite(t.powerPorts)&&t.powerPorts>0?t.powerPorts:36;return [{name:"Power",ports:Array.from({length:i},(a,o)=>{let s=ee(o);return {id:`${o+1}`,label:`${o+1}`,connected:false,type:s==="C19"?"power_c19":"power_c13",connectorType:s}})}]}function dt(e){return (e.type==="vertical-pdu"?[{face:"rear",blocks:ut(e)}]:(()=>{let{ports:n,rearPorts:r}=g(e.data);return [{face:"front",blocks:n.map(E)},{face:"rear",blocks:r.map(E)}]})()).flatMap(({face:n,blocks:r})=>r.flatMap(i=>i.ports.map(a=>({id:a.id??`${i.name}-${a.label}`,label:a.label,face:n,group:i.name,role:l(a.type)?"power":"network",medium:ct(a.type),type:a.type,cableType:a.cableType,connector:a.connectorType,compatibility:Array.isArray(a.compatibility)?[...a.compatibility]:void 0,layout:N(a.position)}))))}function pt(e){let t=e.data??{};if(e.type==="tube"){let n=Math.max(1,Math.min(24,Number(t.fibersPerTube)||12)),r=Math.max(1,Number(t.tubeIndex)||1);return [...Array.from({length:n},(i,a)=>({id:`fiber-${a+1}`,label:`${a+1}`,role:"fiber",medium:"fiber"})),{id:`tube-${r}`,label:`Tube ${r}`,role:"tube",medium:"tube"}]}if(e.type==="cable"){let n=Math.max(1,Math.min(24,Number(t.fiberCount)||12));return [...Array.from({length:n},(r,i)=>({id:`fiber-${i+1}`,label:`${i+1}`,role:"fiber",medium:"fiber"})),{id:"cable-button",label:"Cable",role:"cable"}]}if(e.type==="multi-tube-cable"){let n=Math.max(1,Math.min(24,Number(t.tubeCount)||12)),r=Math.max(1,Number(t.tubeStartIndex)||1);return [...Array.from({length:n},(i,a)=>({id:`tube-${r+a}`,label:`${r+a}`,role:"tube",medium:"tube"})),{id:"cable-button",label:"Cable",role:"cable"}]}if(e.type==="splice")return [{id:"left",label:"Left",role:"splice",medium:"fiber"},{id:"right",label:"Right",role:"splice",medium:"fiber"}];if(e.type==="coupler"){let n=typeof t.couplerFiberCount=="number"&&[1,2,4].includes(t.couplerFiberCount)?t.couplerFiberCount:2;return [...Array.from({length:n},(r,i)=>({id:`left-${i+1}`,label:`${i+1}`,role:"coupler",medium:"fiber"})),...Array.from({length:n},(r,i)=>({id:`right-${i+1}`,label:`${i+1}`,role:"coupler",medium:"fiber"}))]}if(e.type==="fibre-split"){let n=Math.max(1,Math.min(2,Number(t.inputCount)||1)),r=Math.max(1,Math.min(64,Number(t.outputCount)||8));return [...Array.from({length:n},(i,a)=>({id:`input-${a+1}`,label:`Input ${a+1}`,role:"splitter",medium:"fiber"})),...Array.from({length:r},(i,a)=>({id:`output-${a+1}`,label:`Output ${a+1}`,role:"splitter",medium:"fiber"}))]}return []}function lt(e){let t=e.type==="vertical-pdu"||Array.isArray(e.data?.ports)||Array.isArray(e.data?.portGroups)||Array.isArray(e.data?.rearPortGroups)||Array.isArray(e.data?.pduPortGroups)?dt(e):pt(e);return t.length>0?t:void 0}function ft(e,t){if(!t||t.length===0)return t;let n=new Map((e??[]).map(r=>[r.id,r]));return t.map(r=>{let i=n.get(r.id);return i?{...i,...r,compatibility:r.compatibility??i.compatibility,layout:r.layout??i.layout}:r})}function mt(e,t){return e?{...e,...t,placement:t.placement,render:t.render,config:t.config,externalRefs:t.externalRefs,metadata:t.metadata??e.metadata,endpoints:ft(e.endpoints,t.endpoints)}:t}function bt(e,t){let n=new Map((t?.nodes??[]).map(o=>[o.id,o])),r=e.nodes.map(o=>mt(n.get(o.id),{id:o.id,...tt(o),label:o.data?.label??o.id,placement:nt(o),render:ot(o),config:at(o),externalRefs:it(o),metadata:n.get(o.id)?.metadata,endpoints:lt(o)})),i=new Map((t?.edges??[]).map(o=>[o.id,o])),a=e.edges.map(o=>{let s={id:o.id,source:{nodeId:o.source,endpointId:o.sourceHandle?p(o.sourceHandle):void 0},target:{nodeId:o.target,endpointId:o.targetHandle?p(o.targetHandle):void 0},type:o.type,hidden:o.hidden===true?true:void 0,data:o.data?{...o.data}:void 0,metadata:i.get(o.id)?.metadata},c=i.get(o.id);return c?{...c,...s}:s});return {nodes:r,edges:a,ui:st(e.nodes,t?.ui),metadata:t?.metadata}}function Pt(e){let t=new Map,n=r=>{if(!r.endpointId)return;let i=`${r.nodeId}:${p(r.endpointId)}`;t.set(i,(t.get(i)??0)+1);};return e.forEach(r=>{n(r.source),n(r.target);}),t}function ne(e){if(e.type)return e.type;if(e.role==="power"||e.medium==="power"){let t=String(e.connector??"").toLowerCase();return t.includes("c19")||t.includes("c20")?"power_c19":t.includes("c13")||t.includes("c14")?"power_c13":"power_ac"}return e.medium==="fiber"?"fiber":(e.medium==="ethernet","ethernet")}function yt(e,t,n){return e.map(r=>({id:r.id,label:r.label,face:r.face==="rear"?"rear":r.face==="front"?"front":void 0,group:r.group,connected:(t.get(`${n}:${p(r.id)}`)??0)>0,type:ne(r),cableType:r.cableType,connectorType:r.connector,compatibility:r.compatibility&&r.compatibility.length>0?[...r.compatibility]:void 0,position:N(r.layout)}))}function gt(e,t,n){let r=new Map;return e.forEach(i=>{let a=i.group??"Ports",o=r.get(a),s={id:i.id,label:i.label,connected:(t.get(`${n}:${p(i.id)}`)??0)>0,type:ne(i),cableType:i.cableType,connectorType:i.connector,compatibility:i.compatibility&&i.compatibility.length>0?[...i.compatibility]:void 0,position:N(i.layout)};if(o){o.push(s);return}r.set(a,[s]);}),[...r.entries()].map(([i,a])=>({name:i,ports:a.map(o=>({id:o.id,label:o.label,connected:o.connected??false,type:o.type,cableType:o.cableType,connectorType:o.connectorType,compatibility:o.compatibility,position:N(o.position)}))}))}function ht(e,t,n){if(!t&&!n)return e;let r={...e};t&&(M.forEach(a=>{let o=t[a];o!==void 0&&(r[a]=o);}),t.images&&v.forEach(a=>{let o=t.images?.[a];o!==void 0&&(r[a]=o);}),t.dimensions&&L.forEach(a=>{let o=t.dimensions?.[a];typeof o=="number"&&(r[a]=o);}));let i=n?.face;return i&&(r.face=i),n?.handleSideMap&&(r.handleSideMap={...n.handleSideMap}),r}function Et(e,t){if(!t||t.length===0)return e;let n={...e};return t.forEach(r=>{r.source==="netbox"&&(n.netboxId=r.id,r.type&&(n.netboxType=r.type)),r.source==="inventory"&&(n.inventoryId=r.id,r.type&&(n.inventoryType=r.type));}),n}function te(e,t,n){if(!t)return;let r=p(t);if(!e)return r;if(e.kind==="equipment"){let i=(e.subtype??"").toLowerCase().replace(/_/g,"-"),a=e.endpoints?.find(s=>s.id===r),o=a?.role==="power"||a?.medium==="power";if((i==="switch"||i==="router")&&n==="target"&&!o)return `${r}-target`;if(i==="patch-panel"){let s=a?.face==="rear"?"rear":"front";if(n==="source"&&s==="front")return `${r}-source`;if(n==="target"&&s==="rear")return `${r}-target`}return r}return e.kind==="splitter",r}function Nt(e){let t=Pt(e.edges),n=e.ui?.nodeState??{},r=e.nodes.map(o=>{let s=rt(o),c={label:o.label,...o.config??{}};if(c=ht(c,o.render,n[o.id]),c=Et(c,o.externalRefs),s==="device"||s==="server"||s==="switch"||s==="router")c.deviceType=o.subtype??(s==="device"?"device":s),o.endpoints&&o.endpoints.length>0&&(c.ports=yt(o.endpoints,t,o.id));else if(s==="vertical-pdu"){let u=(o.endpoints??[]).filter(f=>f.face!=="front");u.length>0&&(c.pduPortGroups=gt(u,t,o.id),c.powerPorts=u.length);}return o.placement.rack&&(o.placement.rack.rackLayout&&(c.rackLayout=o.placement.rack.rackLayout),typeof o.placement.rack.rackWidthPx=="number"&&(c.rackWidthPx=o.placement.rack.rackWidthPx),typeof o.placement.rack.rackFaceGapPx=="number"&&(c.rackFaceGapPx=o.placement.rack.rackFaceGapPx)),s!=="rack"&&o.placement.rack&&(c.isRackMounted=true,typeof o.placement.rack.uPosition=="number"&&(c.uPosition=o.placement.rack.uPosition),o.placement.rack.mountMode&&(c.mountMode=o.placement.rack.mountMode)),o.placement.container&&(typeof o.placement.container.slotPosition=="number"&&(c.slotPosition=o.placement.container.slotPosition),typeof o.placement.container.holderPosition=="number"&&(c.holderPosition=o.placement.container.holderPosition)),{id:o.id,type:s,position:re(o.placement.position),...o.placement.parentId?{parentId:o.placement.parentId}:{},...o.placement.extent?{extent:o.placement.extent}:{},data:c}}),i=new Map(e.nodes.map(o=>[o.id,o])),a=e.edges.map(o=>({id:o.id,source:o.source.nodeId,target:o.target.nodeId,...o.source.endpointId?{sourceHandle:te(i.get(o.source.nodeId),o.source.endpointId,"source")}:{},...o.target.endpointId?{targetHandle:te(i.get(o.target.nodeId),o.target.endpointId,"target")}:{},...o.type?{type:o.type}:{},...o.hidden===true?{hidden:true}:{},...o.data?{data:{...o.data}}:{}}));return {nodes:r,edges:a}}function kt(e,t,n={}){let r=n.direction??"horizontal",i=n.spacing??220,a=n.origin??{x:0,y:0},o=e.map((c,u)=>({id:c.id,type:c.type??"fiber",position:r==="vertical"?{x:a.x,y:a.y+u*i}:{x:a.x+u*i,y:a.y},data:c.data??{label:c.id}})),s=t.map(c=>({id:c.id,source:c.source,target:c.target,type:c.type??"fiber",data:c.data}));return {nodes:o,edges:s}}var Dt=(e,t)=>t?p(t):"";function $(e,t){if(!e||!t)return "other";let{shape:n}=T(t);switch(e.type){case "tube":return n.kind==="fiber"?"tube-fiber":n.kind==="tube"?"tube-tube":"other";case "cable":return n.kind==="fiber"?"tube-fiber":n.kind==="cable-button"?"cable-button":"other";case "multi-tube-cable":return n.kind==="tube"?"cable-tube":n.kind==="cable-button"?"cable-button":"other";case "splice":return n.kind==="splice-side"?"splice-side":"other";case "coupler":return n.kind==="side-port"?"coupler-port":"other";case "fibre-split":return n.kind==="split-io"?n.io==="input"?"splitter-input":"splitter-output":"other";default:return "other"}}var Tt=new Set(["tube-fiber>splice-side","splice-side>tube-fiber","splice-side>coupler-port","coupler-port>splice-side","splice-side>splitter-input","splice-side>splitter-output","splitter-output>splice-side","coupler-port>coupler-port","coupler-port>splitter-input","splitter-input>coupler-port","coupler-port>splitter-output","splitter-output>coupler-port","splitter-output>tube-fiber","tube-fiber>splitter-output","cable-button>cable-button","cable-tube>tube-tube","tube-tube>cable-tube"]);function oe(e){return e==="tube-tube"||e==="cable-tube"}function xt(e,t,n,r){if(e?.type==="splice"&&n?.type==="splice"){let o=e.data?.mode==="ribbon",s=n.data?.mode==="ribbon";if(o!==s)return {valid:false,reason:"Ribbon splices can only connect to other ribbon splices"}}let i=$(e,t),a=$(n,r);return Tt.has(`${i}>${a}`)?{valid:true}:oe(i)&&oe(a)?{valid:false,reason:"Tube handles can only connect between cable and tube nodes"}:{valid:false,reason:"Connection not allowed by fiber rules"}}exports.applyDirectionalPortHandleId=ae;exports.applyFaceAliasHandleId=ge;exports.buildCouplerHandleId=j;exports.buildDevicePortGroupsFromPorts=B;exports.buildFiberHandleId=me;exports.buildPathLayoutGraph=kt;exports.buildPortHandleId=D;exports.buildReactNetworksHandleId=ve;exports.buildReactNetworksPortGroupsFromPorts=qe;exports.buildSpliceHandleId=Pe;exports.buildSplitIoHandleId=ye;exports.buildTubeHandleId=be;exports.classifyFiberEndpoint=$;exports.describePortHandle=Z;exports.diagramDocumentToNetworkGraph=Nt;exports.evaluateFiberConnectionRule=xt;exports.flattenReactNetworksPortGroups=Ye;exports.getOppositeSideHandleId=Ee;exports.isPowerPortType=l;exports.listPossiblePortHandleIds=Q;exports.looksLikePowerHandleId=W;exports.networkGraphToDiagramDocument=bt;exports.normalizeDevicePorts=O;exports.normalizeDirectionalHandleId=k;exports.normalizeHandle=Dt;exports.normalizeHandleId=p;exports.normalizePortPositionInput=b;exports.parseHandleId=T;exports.parseSemanticPortPosition=C;exports.resolveDevicePortBlocksFromFlatPorts=Ve;exports.resolveDevicePortGroup=I;exports.resolveNodeDevicePorts=J;exports.resolveReactNetworksNodePorts=je;exports.resolveReactNetworksPortGroup=We;exports.sanitizeHandleIdForMatching=he;exports.validatePortHandleOnNodeData=Xe;//# sourceMappingURL=model.js.map
|
|
2
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/handle-ids.ts","../src/utils/port-position.ts","../src/utils/render-ports.ts","../src/utils/port-contract.ts","../src/utils/power-connectors.ts","../src/utils/document-adapter.ts","../src/utils/path-layout.ts","../src/utils/connection-rules.ts"],"names":["DIRECTIONAL_HANDLE_SUFFIX","FACE_ALIAS_HANDLE_SUFFIX","normalizeDirectionalHandleId","handleId","normalizeHandleId","isPowerPortType","portType","buildPortHandleId","portBlockName","port","looksLikePowerHandleId","normalized","applyDirectionalPortHandleId","direction","options","OUTLET_BASE","FIBER_TUBE_BASE","FIBER_BASE","TUBE_BASE","SIDE_PORT_BASE","SPLIT_IO_BASE","classifyHandleBase","base","fiberTube","fiber","tube","sidePort","splitIo","parseHandleId","raw","directionMatch","afterDirection","faceMatch","parsed","buildFiberHandleId","buildTubeHandleId","buildSpliceHandleId","side","buildCouplerHandleId","index","buildSplitIoHandleId","io","applyFaceAliasHandleId","face","sanitizeHandleIdForMatching","getOppositeSideHandleId","shape","isRecord","value","toNumber","normalizeSemanticPositionTokens","token","canonicalizeSideToken","hasCenterToken","tokens","resolveAxisOffsetPercent","primarySide","hasLeft","hasRight","hasTop","hasBottom","hasCenter","resolveSemanticEdgePortPosition","offsetPercent","resolveSemanticCompositePortPosition","hasFront","hasRear","xPercent","yPercent","parseSemanticPortPosition","normalizePortPositionInput","sideRaw","semanticPosition","offsetPx","widthPercent","heightPercent","DEFAULT_FRONT_GROUP_NAME","DEFAULT_REAR_GROUP_NAME","DEFAULT_POWER_GROUP_NAME","DISTRIBUTED_OFFSET_MIN_PERCENT","DISTRIBUTED_OFFSET_MAX_PERCENT","clampPercent","normalizePortAnchor","getNormalizedPortPosition","position","hasExplicitAbsoluteCoordinates","hasExplicitOffset","resolveRenderPortFace","resolveRenderPortGroupName","getEntrySortValue","entry","toDistributedOffsetPercent","count","buildDistributedOffsetMap","entries","distributedEntries","offsetPercentByIndex","entriesByAnchor","anchor","group","anchorEntries","sortedEntries","left","right","orderDifference","toPortPosition","distributedOffsetPercent","normalizedPosition","resolvedOffsetPercent","toPort","normalizePortBlockCollection","portBlocks","block","isGroupedPortBlockArray","isFlatDevicePortArray","buildPortBlocksFromPorts","devicePorts","bucketsByKey","devicePort","groupName","key","existingBucket","ports","rearPorts","bucket","distributedOffsetByIndex","getResolvedPortBlocks","data","buildReactNetworksHandleId","toText","trimmed","normalizeFaceInput","normalizeConnectorType","resolveDefaultFace","resolveDefaultGroup","type","resolveDevicePortFace","resolveDevicePortGroup","resolveReactNetworksPortGroup","normalizeDevicePorts","label","order","normalizePortBlocks","rawPort","resolveNodeDevicePorts","flatPorts","frontFromPorts","frontPorts","rearFromGroups","blockPorts","resolveReactNetworksNodePorts","flattenDevicePortsFromGroups","portGroups","rearPortGroups","appendFace","blocks","flattenReactNetworksPortGroups","buildDevicePortGroupsFromPorts","buildReactNetworksPortGroupsFromPorts","listPossiblePortHandleIds","baseHandle","normalizedDeviceType","describePortHandle","nodeData","handle","sanitize","normalizedRequestedHandle","findInBlocks","portBlock","candidate","validatePortHandleOnNodeData","resolveDevicePortBlocksFromFlatPorts","getPDUPortType","portIndex","CONTAINER_SUBTYPES","IMAGE_RENDER_KEYS","DIMENSION_RENDER_KEYS","GENERAL_RENDER_KEYS","UI_NODE_STATE_KEYS","EXCLUDED_CONFIG_KEYS","clonePosition","clonePortPosition","clonePortBlock","getNodeParentId","node","legacyParent","toDiagramNodeKind","toLegacyNodeType","normalizedSubtype","subtype","toDiagramPlacement","placement","parentId","toDiagramRender","render","images","acc","dimensions","toDiagramExternalRefs","refs","toDiagramConfig","config","extractUiState","nodes","previousUi","nodeState","resolvedFace","inferEndpointMedium","normalizedType","buildPduPortBlocks","powerPorts","_","connectorType","toPortBasedEndpoints","createSyntheticEndpoints","fibersPerTube","tubeIndex","fiberCount","tubeCount","tubeStartIndex","portCount","inputCount","outputCount","toDiagramEndpoints","endpoints","mergeEndpoints","previousEndpoints","nextEndpoints","previousById","endpoint","previous","mergeDocumentNode","previousNode","nextNode","networkGraphToDiagramDocument","snapshot","previousDocument","previousNodesById","previousEdgesById","edge","edges","nextEdge","buildEndpointConnectionMap","counts","increment","ref","endpointToPortType","connector","endpointsToDevicePorts","edgeConnectionMap","nodeId","endpointsToPortBlocks","grouped","existing","name","applyRenderToLegacyData","uiEntry","nextData","applyExternalRefsToLegacyData","resolveLegacyHandleId","endpointId","role","normalizedEndpointId","isPowerEndpoint","diagramDocumentToNetworkGraph","document","uiNodeState","legacyType","rearEndpoints","nodesById","buildPathLayoutGraph","steps","spacing","origin","step","layoutEdges","normalizeHandle","_node","classifyFiberEndpoint","ALLOWED_FIBER_PAIRS","isTubeHandleClass","evaluateFiberConnectionRule","sourceNode","sourceHandle","targetNode","targetHandle","sourceIsRibbon","targetIsRibbon","sourceClass","targetClass"],"mappings":"aAEA,IAAMA,EAA4B,oBAAA,CAC5BC,CAAAA,CAA2B,mBAM1B,SAASC,CAAAA,CACdC,EACQ,CACR,OAAKA,EACEA,CAAAA,CAAS,OAAA,CAAQH,EAA2B,EAAE,CAAA,CAD/B,EAExB,CASO,SAASI,EAAkBD,CAAAA,CAA6C,CAC7E,OAAOD,CAAAA,CAA6BC,CAAQ,EAAE,OAAA,CAC5CF,CAAAA,CACA,EACF,CACF,CAKO,SAASI,CAAAA,CAAgBC,CAAAA,CAAuC,CACrE,OACE,OAAOA,GAAa,QAAA,EAAYA,CAAAA,CAAS,aAAY,CAAE,QAAA,CAAS,OAAO,CAE3E,CAMO,SAASC,CAAAA,CACdC,EACAC,CAAAA,CACQ,CACR,OAAI,OAAOA,CAAAA,CAAK,IAAO,QAAA,EAAYA,CAAAA,CAAK,GAAG,MAAA,CAAS,CAAA,CAC3CA,EAAK,EAAA,CAEPJ,CAAAA,CAAgBI,EAAK,IAAI,CAAA,CAC5BA,EAAK,KAAA,CACL,CAAA,EAAGD,CAAa,CAAA,CAAA,EAAIC,CAAAA,CAAK,KAAK,CAAA,CACpC,CAKO,SAASC,CAAAA,CAAuBP,CAAAA,CAA2B,CAChE,IAAMQ,CAAAA,CAAaT,EAA6BC,CAAQ,CAAA,CAAE,aAAY,CACtE,OAAOQ,EAAW,UAAA,CAAW,KAAK,GAAKA,CAAAA,CAAW,QAAA,CAAS,OAAO,CACpE,CA8BO,SAASC,EAAAA,CACdT,EACAU,CAAAA,CACAC,CAAAA,CACQ,CAOR,OANI,CAACD,IAEH,OAAOC,CAAAA,EAAS,UAAa,QAAA,EAAYA,CAAAA,CAAQ,SAAS,MAAA,CAAS,CAAA,CAEjET,EAAgBS,CAAAA,CAAQ,QAAQ,EAChCJ,CAAAA,CAAuBP,CAAQ,GACfA,CAAAA,CACb,CAAA,EAAGD,EAA6BC,CAAQ,CAAC,IAAIU,CAAS,CAAA,CAC/D,CAmEA,IAAME,EAAAA,CAAc,QACdC,EAAAA,CAAkB,qBAAA,CAClBC,GAAa,eAAA,CACbC,EAAAA,CAAY,eACZC,EAAAA,CAAiB,sBAAA,CACjBC,EAAAA,CAAgB,wBAAA,CAEtB,SAASC,EAAAA,CAAmBC,CAAAA,CAA2B,CACrD,GAAIP,EAAAA,CAAY,KAAKO,CAAI,CAAA,CACvB,OAAO,CAAE,IAAA,CAAM,SAAU,MAAA,CAAQ,MAAA,CAAOA,CAAI,CAAE,CAAA,CAEhD,IAAMC,CAAAA,CAAYP,EAAAA,CAAgB,KAAKM,CAAI,CAAA,CAC3C,GAAIC,CAAAA,CACF,OAAO,CAAE,IAAA,CAAM,OAAA,CAAS,KAAM,MAAA,CAAOA,CAAAA,CAAU,CAAC,CAAC,CAAA,CAAG,MAAO,MAAA,CAAOA,CAAAA,CAAU,CAAC,CAAC,CAAE,EAElF,IAAMC,CAAAA,CAAQP,EAAAA,CAAW,IAAA,CAAKK,CAAI,CAAA,CAClC,GAAIE,EACF,OAAO,CAAE,KAAM,OAAA,CAAS,KAAA,CAAO,OAAOA,CAAAA,CAAM,CAAC,CAAC,CAAE,CAAA,CAElD,IAAMC,CAAAA,CAAOP,EAAAA,CAAU,KAAKI,CAAI,CAAA,CAChC,GAAIG,CAAAA,CACF,OAAO,CAAE,IAAA,CAAM,MAAA,CAAQ,KAAM,MAAA,CAAOA,CAAAA,CAAK,CAAC,CAAC,CAAE,EAE/C,GAAIH,CAAAA,GAAS,QAAUA,CAAAA,GAAS,OAAA,CAC9B,OAAO,CAAE,IAAA,CAAM,cAAe,IAAA,CAAMA,CAAK,CAAA,CAE3C,IAAMI,EAAWP,EAAAA,CAAe,IAAA,CAAKG,CAAI,CAAA,CACzC,GAAII,EACF,OAAO,CACL,KAAM,WAAA,CACN,IAAA,CAAMA,EAAS,CAAC,CAAA,CAChB,MAAO,MAAA,CAAOA,CAAAA,CAAS,CAAC,CAAC,CAC3B,EAEF,IAAMC,CAAAA,CAAUP,GAAc,IAAA,CAAKE,CAAI,EACvC,OAAIK,CAAAA,CACK,CACL,IAAA,CAAM,UAAA,CACN,GAAIA,CAAAA,CAAQ,CAAC,EACb,KAAA,CAAO,MAAA,CAAOA,EAAQ,CAAC,CAAC,CAC1B,CAAA,CAEEL,CAAAA,GAAS,cAAA,CACJ,CAAE,KAAM,cAAe,CAAA,CAEzB,CAAE,IAAA,CAAM,MAAO,CACxB,CAQO,SAASM,EAAczB,CAAAA,CAAqD,CACjF,IAAM0B,CAAAA,CAAM1B,CAAAA,EAAY,GAClB2B,CAAAA,CAAiB9B,CAAAA,CAA0B,KAAK6B,CAAG,CAAA,CACnDE,EAAiBD,CAAAA,CACnBD,CAAAA,CAAI,MAAM,CAAA,CAAGA,CAAAA,CAAI,OAASC,CAAAA,CAAe,CAAC,EAAE,MAAM,CAAA,CAClDD,EACEG,CAAAA,CAAY/B,CAAAA,CAAyB,KAAK8B,CAAc,CAAA,CACxDT,EAAOU,CAAAA,CACTD,CAAAA,CAAe,MAAM,CAAA,CAAGA,CAAAA,CAAe,MAAA,CAASC,CAAAA,CAAU,CAAC,CAAA,CAAE,MAAM,EACnED,CAAAA,CACEE,CAAAA,CAAyB,CAAE,GAAA,CAAAJ,CAAAA,CAAK,KAAAP,CAAAA,CAAM,KAAA,CAAOD,GAAmBC,CAAI,CAAE,EAC5E,OAAIQ,CAAAA,GACFG,EAAO,SAAA,CAAYH,CAAAA,CAAe,CAAC,CAAA,CAAE,WAAA,IAEnCE,CAAAA,GACFC,CAAAA,CAAO,UAAYD,CAAAA,CAAU,CAAC,EAAE,WAAA,EAAY,CAAA,CAEvCC,CACT,CAOO,SAASC,GAAmBV,CAAAA,CAAeC,CAAAA,CAAuB,CACvE,OAAOA,CAAAA,GAAS,OAAY,CAAA,MAAA,EAASD,CAAK,CAAA,CAAA,CAAK,CAAA,MAAA,EAASC,CAAI,CAAA,CAAA,EAAID,CAAK,EACvE,CAMO,SAASW,GAAkBV,CAAAA,CAAsB,CACtD,OAAO,CAAA,KAAA,EAAQA,CAAI,EACrB,CAMO,SAASW,GAAoBC,CAAAA,CAAgC,CAClE,OAAOA,CACT,CAMO,SAASC,CAAAA,CAAqBD,CAAAA,CAAwBE,EAAuB,CAClF,OAAO,GAAGF,CAAI,CAAA,CAAA,EAAIE,CAAK,CAAA,CACzB,CAMO,SAASC,EAAAA,CAAqBC,CAAAA,CAAwBF,EAAuB,CAClF,OAAO,GAAGE,CAAE,CAAA,CAAA,EAAIF,CAAK,CAAA,CACvB,CAQO,SAASG,EAAAA,CACdvC,EACAwC,CAAAA,CACQ,CACR,OAAO,CAAA,EAAGxC,CAAAA,CAAS,QAAQF,CAAAA,CAA0B,EAAE,CAAC,CAAA,EAAA,EAAK0C,CAAI,EACnE,CAWO,SAASC,GAA4BzC,CAAAA,CAA0B,CACpE,OAAOA,CAAAA,CAAS,OAAA,CAAQ,MAAO,EAAE,CACnC,CASO,SAAS0C,EAAAA,CAAwB1C,EAAsC,CAC5E,GAAM,CAAE,KAAA,CAAA2C,CAAM,EAAIlB,CAAAA,CAAczB,CAAQ,EACxC,GAAI2C,CAAAA,CAAM,OAAS,aAAA,CACjB,OAAOA,EAAM,IAAA,GAAS,MAAA,CAAS,OAAA,CAAU,MAAA,CAE3C,GAAIA,CAAAA,CAAM,IAAA,GAAS,YACjB,OAAOR,CAAAA,CAAqBQ,EAAM,IAAA,GAAS,MAAA,CAAS,QAAU,MAAA,CAAQA,CAAAA,CAAM,KAAK,CAGrF,KC9TMC,EAAAA,CAAYC,CAAAA,EAChB,OAAOA,CAAAA,EAAU,QAAA,EAAYA,IAAU,IAAA,EAAQ,CAAC,MAAM,OAAA,CAAQA,CAAK,EAE/DC,CAAAA,CAAYD,CAAAA,EAAuC,CACvD,GAAI,OAAOA,GAAU,QAAA,EAAY,MAAA,CAAO,SAASA,CAAK,CAAA,CACpD,OAAOA,CAAAA,CAET,GAAI,OAAOA,CAAAA,EAAU,QAAA,EAAYA,CAAAA,CAAM,IAAA,GAAO,MAAA,CAAS,CAAA,CAAG,CACxD,IAAMf,CAAAA,CAAS,OAAOe,CAAK,CAAA,CAC3B,GAAI,MAAA,CAAO,QAAA,CAASf,CAAM,CAAA,CACxB,OAAOA,CAEX,CAEF,CAAA,CAEMiB,GAAmCF,CAAAA,EACvCA,CAAAA,CACG,MAAK,CACL,WAAA,GACA,KAAA,CAAM,YAAY,EAClB,MAAA,CAAQG,CAAAA,EAAUA,EAAM,MAAA,CAAS,CAAC,EAEjCC,CAAAA,CAAyBD,CAAAA,EAAiE,CAC9F,GAAIA,CAAAA,GAAU,OAASA,CAAAA,GAAU,IAAA,CAC/B,OAAO,KAAA,CAET,GAAIA,CAAAA,GAAU,OAAA,CACZ,OAAO,OAAA,CAET,GAAIA,IAAU,QAAA,EAAYA,CAAAA,GAAU,OAClC,OAAO,QAAA,CAET,GAAIA,CAAAA,GAAU,MAAA,CACZ,OAAO,MAGX,CAAA,CAEME,EAAkBC,CAAAA,EACtBA,CAAAA,CAAO,SAAS,QAAQ,CAAA,EAAKA,EAAO,QAAA,CAAS,QAAQ,GAAKA,CAAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,EAAKA,CAAAA,CAAO,SAAS,KAAK,CAAA,CAExGC,GAA2B,CAC/BC,CAAAA,CACAF,IACuB,CACvB,IAAMG,EAAUH,CAAAA,CAAO,QAAA,CAAS,MAAM,CAAA,CAChCI,CAAAA,CAAWJ,CAAAA,CAAO,QAAA,CAAS,OAAO,CAAA,CAClCK,CAAAA,CAASL,EAAO,QAAA,CAAS,KAAK,GAAKA,CAAAA,CAAO,QAAA,CAAS,IAAI,CAAA,CACvDM,CAAAA,CAAYN,EAAO,QAAA,CAAS,QAAQ,GAAKA,CAAAA,CAAO,QAAA,CAAS,MAAM,CAAA,CAC/DO,CAAAA,CAAYR,EAAeC,CAAM,CAAA,CAEvC,GAAIE,CAAAA,GAAgB,KAAA,EAASA,IAAgB,QAAA,CAC3C,OAAIC,GAAWI,CAAAA,EAAa,CAACH,EAAiB,EAAA,CAC1CA,CAAAA,EAAYG,GAAa,CAACJ,CAAAA,CAAgB,GAC1CA,CAAAA,EAAW,CAACC,EAAiB,EAAA,CAC7BA,CAAAA,EAAY,CAACD,CAAAA,CAAgB,GAC7BI,CAAAA,CAAkB,EAAA,CACtB,OAGF,GAAIF,CAAAA,EAAUE,GAAa,CAACD,CAAAA,CAAW,OAAO,EAAA,CAC9C,GAAIA,GAAaC,CAAAA,EAAa,CAACF,EAAQ,OAAO,EAAA,CAC9C,GAAIA,CAAAA,EAAU,CAACC,EAAW,OAAO,EAAA,CACjC,GAAIA,CAAAA,EAAa,CAACD,EAAQ,OAAO,EAAA,CACjC,GAAIE,CAAAA,CAAW,SAEjB,CAAA,CAEMC,EAAAA,CAAmCR,GAA+C,CAEtF,GADsBA,EAAO,QAAA,CAAS,OAAO,GAAKA,CAAAA,CAAO,QAAA,CAAS,MAAM,CAAA,EAAKA,CAAAA,CAAO,SAAS,MAAM,CAAA,CAEjG,OAGF,IAAME,CAAAA,CAAcF,EACjB,GAAA,CAAIF,CAAqB,EACzB,IAAA,CAAMf,CAAAA,EAAoDA,IAAS,MAAS,CAAA,CAE/E,GAAI,CAACmB,CAAAA,CACH,OAGF,IAAMO,CAAAA,CAAgBR,GAAyBC,CAAAA,CAAaF,CAAM,EAClE,GAAIS,CAAAA,GAAkB,OAItB,OAAO,CACL,KAAMP,CAAAA,CACN,aAAA,CAAAO,CACF,CACF,CAAA,CAEMC,GAAwCV,CAAAA,EAA+C,CAC3F,GAAIA,CAAAA,CAAO,MAAA,GAAW,EACpB,OAGF,IAAMG,CAAAA,CAAUH,CAAAA,CAAO,SAAS,MAAM,CAAA,CAChCI,EAAWJ,CAAAA,CAAO,QAAA,CAAS,OAAO,CAAA,CAClCO,CAAAA,CAAYR,EAAeC,CAAM,CAAA,CACjCW,EAAWX,CAAAA,CAAO,QAAA,CAAS,OAAO,CAAA,EAAKA,CAAAA,CAAO,SAAS,KAAK,CAAA,EAAKA,EAAO,QAAA,CAAS,IAAI,EACrFY,CAAAA,CAAUZ,CAAAA,CAAO,SAAS,MAAM,CAAA,EAAKA,EAAO,QAAA,CAAS,MAAM,GAAKA,CAAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,EAAKA,CAAAA,CAAO,SAAS,MAAM,CAAA,CAErHa,EACAV,CAAAA,EAAWI,CAAAA,EAAa,CAACH,CAAAA,CAC3BS,EAAW,EAAA,CACFT,CAAAA,EAAYG,GAAa,CAACJ,CAAAA,CACnCU,EAAW,EAAA,CACFV,CAAAA,EAAW,CAACC,CAAAA,CACrBS,CAAAA,CAAW,GACFT,CAAAA,EAAY,CAACD,EACtBU,CAAAA,CAAW,EAAA,CACFN,IACTM,CAAAA,CAAW,EAAA,CAAA,CAGb,IAAIC,CAAAA,CASJ,GARIH,GAAY,CAACC,CAAAA,CACfE,EAAW,EAAA,CACFF,CAAAA,EAAW,CAACD,CAAAA,CACrBG,CAAAA,CAAW,GACFP,CAAAA,GACTO,CAAAA,CAAW,IAGT,EAAAD,CAAAA,GAAa,QAAaC,CAAAA,GAAa,MAAA,CAAA,CAI3C,OAAO,CAAE,QAAA,CAAAD,CAAAA,CAAU,QAAA,CAAAC,CAAS,CAC9B,CAAA,CAEaC,EAA6BrB,CAAAA,EAA4C,CACpF,IAAMM,CAAAA,CAASJ,EAAAA,CAAgCF,CAAK,CAAA,CACpD,GAAIM,EAAO,MAAA,GAAW,CAAA,CAItB,IAAIA,CAAAA,CAAO,MAAA,GAAW,EAAG,CACvB,IAAMjB,EAAOe,CAAAA,CAAsBE,CAAAA,CAAO,CAAC,CAAC,CAAA,CAC5C,OAAOjB,CAAAA,CAAO,CAAE,KAAAA,CAAK,CAAA,CAAI,MAC3B,CAEA,OAAOyB,GAAgCR,CAAM,CAAA,EAAKU,GAAqCV,CAAM,CAAA,CAC/F,EAEagB,CAAAA,CAA8BtB,CAAAA,EAA6C,CACtF,GAAI,OAAOA,CAAAA,EAAU,QAAA,CACnB,OAAOqB,CAAAA,CAA0BrB,CAAK,EAGxC,GAAI,CAACD,GAASC,CAAK,CAAA,CACjB,OAGF,IAAMuB,CAAAA,CAAUvB,EAAM,IAAA,EAAQA,CAAAA,CAAM,WAAaA,CAAAA,CAAM,QAAA,CACjDwB,EAAmB,OAAOD,CAAAA,EAAY,SAAWF,CAAAA,CAA0BE,CAAO,EAAI,MAAA,CAGtFlC,CAAAA,CAAAA,CAFa,OAAOkC,CAAAA,EAAY,QAAA,CAAWnB,EAAsBmB,CAAAA,CAAQ,IAAA,GAAO,WAAA,EAAa,EAAI,MAAA,GAE5EC,CAAAA,EAAkB,KACvCT,CAAAA,CAAgBd,CAAAA,CACpBD,CAAAA,CAAM,aAAA,EACNA,EAAM,cAAA,EACNA,CAAAA,CAAM,WACNA,CAAAA,CAAM,UACR,GAAKwB,CAAAA,EAAkB,aAAA,CACjBC,EAAWxB,CAAAA,CACfD,CAAAA,CAAM,UACNA,CAAAA,CAAM,SAAA,EACNA,EAAM,YAAA,EACNA,CAAAA,CAAM,aACR,CAAA,EAAKwB,CAAAA,EAAkB,SACjBL,CAAAA,CAAWlB,CAAAA,CACfD,EAAM,QAAA,EACNA,CAAAA,CAAM,WACNA,CAAAA,CAAM,IAAA,EACNA,EAAM,KAAA,EACNA,CAAAA,CAAM,mBACNA,CAAAA,CAAM,kBAAA,EACNA,EAAM,CACR,CAAA,EAAKwB,GAAkB,QAAA,CACjBJ,CAAAA,CAAWnB,EACfD,CAAAA,CAAM,QAAA,EACNA,CAAAA,CAAM,SAAA,EACNA,EAAM,IAAA,EACNA,CAAAA,CAAM,OACNA,CAAAA,CAAM,eAAA,EACNA,EAAM,gBAAA,EACNA,CAAAA,CAAM,CACR,CAAA,EAAKwB,CAAAA,EAAkB,SACjBE,CAAAA,CAAezB,CAAAA,CACnBD,EAAM,YAAA,EACNA,CAAAA,CAAM,eACNA,CAAAA,CAAM,QAAA,EACNA,EAAM,SAAA,EACNA,CAAAA,CAAM,UACNA,CAAAA,CAAM,SACR,EACM2B,CAAAA,CAAgB1B,CAAAA,CACpBD,EAAM,aAAA,EACNA,CAAAA,CAAM,gBACNA,CAAAA,CAAM,SAAA,EACNA,EAAM,UAAA,EACNA,CAAAA,CAAM,UACNA,CAAAA,CAAM,SACR,EAEA,GACE,EAAAX,CAAAA,GAAS,MAAA,EACT0B,IAAkB,MAAA,EAClBU,CAAAA,GAAa,QACbN,CAAAA,GAAa,MAAA,EACbC,IAAa,MAAA,EACbM,CAAAA,GAAiB,QACjBC,CAAAA,GAAkB,MAAA,CAAA,CAKpB,OAAO,CACL,IAAA,CAAAtC,EACA,aAAA,CAAA0B,CAAAA,CACA,SAAAU,CAAAA,CACA,QAAA,CAAAN,EACA,QAAA,CAAAC,CAAAA,CACA,aAAAM,CAAAA,CACA,aAAA,CAAAC,CACF,CACF,EC1NA,IAAMC,EAAAA,CAA2B,eAAA,CAC3BC,GAA0B,oBAAA,CAC1BC,EAAAA,CAA2B,cAC3BC,CAAAA,CAAiC,CAAA,CACjCC,GAAiC,EAAA,CAEvC,SAASC,EAAajC,CAAAA,CAAuB,CAC3C,OAAO,IAAA,CAAK,IAAI,CAAA,CAAG,IAAA,CAAK,IAAI,GAAA,CAAKA,CAAK,CAAC,CACzC,CAEA,SAASkC,CAAAA,CAAoBlC,CAAAA,CAA2E,CACtG,GAAIA,CAAAA,GAAU,OAASA,CAAAA,GAAU,OAAA,EAAWA,IAAU,QAAA,EAAYA,CAAAA,GAAU,OAC1E,OAAOA,CAGX,CAEA,SAASmC,CAAAA,CAA0BC,EAA6C,CAC9E,OAAOd,EAA2Bc,CAAQ,CAC5C,CAEA,SAASC,CAAAA,CAA+B5E,EAA2B,CACjE,IAAM2E,EAAWD,CAAAA,CAA0B1E,CAAAA,CAAK,QAAQ,CAAA,CACxD,OACE,OAAO2E,CAAAA,EAAU,UAAa,QAAA,EAC9B,OAAOA,GAAU,QAAA,EAAa,QAElC,CAEA,SAASE,EAAAA,CAAkB7E,EAA2B,CACpD,IAAM2E,EAAWD,CAAAA,CAA0B1E,CAAAA,CAAK,QAAQ,CAAA,CACxD,OACE,OAAO2E,CAAAA,EAAU,aAAA,EAAkB,UACnC,OAAOA,CAAAA,EAAU,UAAa,QAElC,CAEA,SAASG,EAAAA,CAAsB9E,CAAAA,CAA8B,CAC3D,OAAIA,CAAAA,CAAK,OAAS,MAAA,EAAUA,CAAAA,CAAK,OAAS,MAAA,CACjC,MAAA,CAELA,EAAK,IAAA,GAAS,OAAA,CACT,QAEFJ,CAAAA,CAAgBI,CAAAA,CAAK,IAAI,CAAA,CAAI,OAAS,OAC/C,CAEA,SAAS+E,EAAAA,CAA2B/E,CAAAA,CAAkBkC,EAA0B,CAC9E,OAAI,OAAOlC,CAAAA,CAAK,KAAA,EAAU,UAAYA,CAAAA,CAAK,KAAA,CAAM,MAAK,CAAE,MAAA,CAAS,EACxDA,CAAAA,CAAK,KAAA,CAAM,MAAK,CAErBJ,CAAAA,CAAgBI,EAAK,IAAI,CAAA,CACpBqE,GAEFnC,CAAAA,GAAS,MAAA,CAASkC,GAA0BD,EACrD,CAEA,SAASa,CAAAA,CAAkBC,CAAAA,CAA4B,CACrD,OAAO,OAAOA,EAAM,IAAA,CAAK,KAAA,EAAU,UAAY,MAAA,CAAO,QAAA,CAASA,CAAAA,CAAM,IAAA,CAAK,KAAK,CAAA,CAC3EA,CAAAA,CAAM,KAAK,KAAA,CACXA,CAAAA,CAAM,KACZ,CAEA,SAASC,GAA2BpD,CAAAA,CAAeqD,CAAAA,CAAuB,CACxE,OAAIA,CAAAA,EAAS,EACJ,EAAA,CAGPb,CAAAA,CACCxC,GAASyC,EAAAA,CAAiCD,CAAAA,CAAAA,EAAoCa,EAAQ,CAAA,CAE3F,CAEA,SAASC,EAAAA,CAA0BC,CAAAA,CAA6C,CAC9E,IAAMC,CAAAA,CAAqBD,EAAQ,MAAA,CAAQJ,CAAAA,EAElC,CAAC,CADOR,CAAAA,CAAoBC,EAA0BO,CAAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,EAAG,IAAI,GACpE,CAACL,CAAAA,CAA+BK,CAAAA,CAAM,IAAI,GAAK,CAACJ,EAAAA,CAAkBI,EAAM,IAAI,CAChG,EACKM,CAAAA,CAAuB,IAAI,IAC3BC,CAAAA,CAAkB,IAAI,IAE5B,OAAAF,CAAAA,CAAmB,QAASL,CAAAA,EAAU,CACpC,IAAMQ,CAAAA,CAAShB,CAAAA,CAAoBC,EAA0BO,CAAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,EAAG,IAAI,EACvF,GAAI,CAACQ,EACH,OAEF,IAAMC,EAAQF,CAAAA,CAAgB,GAAA,CAAIC,CAAM,CAAA,CACxC,GAAIC,EAAO,CACTA,CAAAA,CAAM,KAAKT,CAAK,CAAA,CAChB,MACF,CACAO,EAAgB,GAAA,CAAIC,CAAAA,CAAQ,CAACR,CAAK,CAAC,EACrC,CAAC,CAAA,CAEDO,EAAgB,OAAA,CAASG,CAAAA,EAAkB,CACzC,IAAMC,CAAAA,CAAgB,CAAC,GAAGD,CAAa,EAAE,IAAA,CAAK,CAACE,EAAMC,CAAAA,GAAU,CAC7D,IAAMC,CAAAA,CAAkBf,CAAAA,CAAkBa,CAAI,CAAA,CAAIb,CAAAA,CAAkBc,CAAK,CAAA,CACzE,OAAIC,IAAoB,CAAA,CACfA,CAAAA,CAEFF,EAAK,KAAA,CAAQC,CAAAA,CAAM,KAC5B,CAAC,CAAA,CACDF,EAAc,OAAA,CAAQ,CAACX,CAAAA,CAAOnD,CAAAA,GAAU,CACtCyD,CAAAA,CAAqB,GAAA,CAAIN,EAAM,KAAA,CAAOC,EAAAA,CAA2BpD,EAAO8D,CAAAA,CAAc,MAAM,CAAC,EAC/F,CAAC,EACH,CAAC,CAAA,CAEML,CACT,CAEA,SAASS,GACPhG,CAAAA,CACAiG,CAAAA,CAC0B,CAC1B,IAAMC,CAAAA,CAAqBxB,EAA0B1E,CAAAA,CAAK,QAAQ,EAC5DyF,CAAAA,CAAShB,CAAAA,CAAoByB,GAAoB,IAAI,CAAA,CAC3D,GAAItB,CAAAA,CAA+B5E,CAAI,EAAG,CACxC,IAAM2E,EAAyB,CAC7B,QAAA,CAAUH,EAAa0B,CAAAA,EAAoB,QAAkB,CAAA,CAC7D,QAAA,CAAU1B,EAAa0B,CAAAA,EAAoB,QAAkB,CAC/D,CAAA,CACA,OAAI,OAAOA,CAAAA,EAAoB,YAAA,EAAiB,UAAY,MAAA,CAAO,QAAA,CAASA,EAAmB,YAAY,CAAA,GACzGvB,EAAS,YAAA,CAAeH,CAAAA,CAAa0B,EAAmB,YAAY,CAAA,CAAA,CAElE,OAAOA,CAAAA,EAAoB,aAAA,EAAkB,UAAY,MAAA,CAAO,QAAA,CAASA,EAAmB,aAAa,CAAA,GAC3GvB,EAAS,aAAA,CAAgBH,CAAAA,CAAa0B,EAAmB,aAAa,CAAA,CAAA,CAEpET,IACFd,CAAAA,CAAS,IAAA,CAAOc,GAEXd,CACT,CAEA,GAAI,CAACc,CAAAA,CACH,OAGF,IAAMd,CAAAA,CAAyB,CAAE,IAAA,CAAMc,CAAO,EAC9C,GAAI,OAAOS,GAAoB,QAAA,EAAa,QAAA,EAAY,OAAO,QAAA,CAASA,CAAAA,CAAmB,QAAQ,CAAA,CACjG,OAAAvB,EAAS,QAAA,CAAWuB,CAAAA,CAAmB,SAChCvB,CAAAA,CAOT,IAAMwB,GAHJ,OAAOD,CAAAA,EAAoB,eAAkB,QAAA,EAAY,MAAA,CAAO,SAASA,CAAAA,CAAmB,aAAa,EACrG1B,CAAAA,CAAa0B,CAAAA,CAAmB,aAAa,CAAA,CAC7C,MAAA,IAEsB,OAAOD,CAAAA,EAA6B,QAAA,CAC1DzB,EAAayB,CAAwB,CAAA,CACrC,QAEN,OAAI,OAAOE,CAAAA,EAA0B,QAAA,GACnCxB,EAAS,aAAA,CAAgBwB,CAAAA,CAAAA,CAGpBxB,CACT,CAEA,SAASyB,GAAOnB,CAAAA,CAAoBgB,CAAAA,CAAyC,CAC3E,IAAMtB,CAAAA,CAAWqB,GAAef,CAAAA,CAAM,IAAA,CAAMgB,CAAwB,CAAA,CACpE,OAAO,CACL,EAAA,CAAIhB,CAAAA,CAAM,KAAK,EAAA,CACf,KAAA,CAAOA,EAAM,IAAA,CAAK,KAAA,CAClB,gBAAiBA,CAAAA,CAAM,IAAA,CAAK,gBAC5B,SAAA,CAAWA,CAAAA,CAAM,KAAK,SAAA,EAAa,KAAA,CACnC,KAAMA,CAAAA,CAAM,IAAA,CAAK,KACjB,SAAA,CAAWA,CAAAA,CAAM,KAAK,SAAA,CACtB,aAAA,CAAeA,CAAAA,CAAM,IAAA,CAAK,cAC1B,aAAA,CAAe,KAAA,CAAM,QAAQA,CAAAA,CAAM,IAAA,CAAK,aAAa,CAAA,CAAI,CAAC,GAAGA,CAAAA,CAAM,IAAA,CAAK,aAAa,CAAA,CAAI,MAAA,CACzF,GAAIN,CAAAA,CAAW,CAAE,SAAAA,CAAS,CAAA,CAAI,EAChC,CACF,CAEA,SAAS0B,CAAAA,CAA6BC,EAAsC,CAC1E,OAAOA,EAAW,GAAA,CAAKC,CAAAA,GAAW,CAChC,GAAGA,CAAAA,CACH,MAAOA,CAAAA,CAAM,KAAA,CAAM,IAAKvG,CAAAA,EAAS,CAC/B,IAAM2E,CAAAA,CAAWD,CAAAA,CAA0B1E,CAAAA,CAAK,QAAQ,EACxD,OAAO,CACL,GAAGA,CAAAA,CACH,GAAI2E,EAAW,CAAE,QAAA,CAAAA,CAAS,CAAA,CAAI,CAAE,SAAU,MAAU,CACtD,CACF,CAAC,CACH,EAAE,CACJ,CAEA,SAAS6B,CAAAA,CAAwBjE,CAAAA,CAAsC,CACrE,OACE,KAAA,CAAM,QAAQA,CAAK,CAAA,EACnBA,EAAM,MAAA,CAAS,CAAA,EACfA,EAAM,KAAA,CACH0C,CAAAA,EACCA,IAAU,IAAA,EACV,OAAOA,GAAU,QAAA,EACjB,OAAQA,EAAoB,IAAA,EAAS,QAAA,EACrC,KAAA,CAAM,OAAA,CAASA,EAAoB,KAAK,CAC5C,CAEJ,CAEA,SAASwB,GAAsBlE,CAAAA,CAAuC,CACpE,OACE,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,EACnBA,CAAAA,CAAM,OAAS,CAAA,EACfA,CAAAA,CAAM,MACH0C,CAAAA,EACCA,CAAAA,GAAU,MACV,OAAOA,CAAAA,EAAU,UACjB,OAAQA,CAAAA,CAAqB,OAAU,QAAA,EACvC,EACE,OAAQA,CAAAA,CAA6B,IAAA,EAAS,UAC9C,KAAA,CAAM,OAAA,CAASA,EAA8B,KAAK,CAAA,CAExD,CAEJ,CAQO,SAASyB,EACdC,CAAAA,CACgD,CAChD,IAAMC,CAAAA,CAAe,IAAI,GAAA,CAEzBD,CAAAA,CAAY,QAAQ,CAACE,CAAAA,CAAY/E,IAAU,CACzC,GAAI,OAAO+E,CAAAA,CAAW,KAAA,EAAU,UAAYA,CAAAA,CAAW,KAAA,CAAM,MAAK,CAAE,MAAA,GAAW,EAC7E,OAGF,IAAM3E,EAAO4C,EAAAA,CAAsB+B,CAAU,EACvCC,CAAAA,CAAY/B,EAAAA,CAA2B8B,EAAY3E,CAAI,CAAA,CACvD6E,EAAM,CAAA,EAAG7E,CAAI,IAAI4E,CAAS,CAAA,CAAA,CAC1BE,EAAiBJ,CAAAA,CAAa,GAAA,CAAIG,CAAG,CAAA,CAC3C,GAAIC,EAAgB,CAClBA,CAAAA,CAAe,OAAA,CAAQ,IAAA,CAAK,CAC1B,KAAA,CAAAlF,CAAAA,CACA,KAAM,CACJ,GAAG+E,EACH,KAAA,CAAOA,CAAAA,CAAW,MAAM,IAAA,EAC1B,CACF,CAAC,CAAA,CACD,MACF,CAEAD,CAAAA,CAAa,IAAIG,CAAAA,CAAK,CACpB,KAAA7E,CAAAA,CACA,SAAA,CAAA4E,EACA,OAAA,CAAS,CACP,CACE,KAAA,CAAAhF,CAAAA,CACA,KAAM,CACJ,GAAG+E,EACH,KAAA,CAAOA,CAAAA,CAAW,MAAM,IAAA,EAC1B,CACF,CACF,CACF,CAAC,EACH,CAAC,CAAA,CAED,IAAMI,EAAqB,EAAC,CACtBC,EAAyB,EAAC,CAEhC,OAAAN,CAAAA,CAAa,OAAA,CAASO,GAAW,CAC/B,IAAMvB,EAAgB,CAAC,GAAGuB,EAAO,OAAO,CAAA,CAAE,KAAK,CAACtB,CAAAA,CAAMC,IAAU,CAC9D,IAAMC,EAAkBf,CAAAA,CAAkBa,CAAI,EAAIb,CAAAA,CAAkBc,CAAK,EACzE,OAAIC,CAAAA,GAAoB,EACfA,CAAAA,CAEFF,CAAAA,CAAK,MAAQC,CAAAA,CAAM,KAC5B,CAAC,CAAA,CACKsB,CAAAA,CAA2BhC,GAA0BQ,CAAa,CAAA,CAClEW,CAAAA,CAAmB,CACvB,KAAMY,CAAAA,CAAO,SAAA,CACb,MAAOvB,CAAAA,CAAc,GAAA,CAAKX,GACxBmB,EAAAA,CAAOnB,CAAAA,CAAOmC,EAAyB,GAAA,CAAInC,CAAAA,CAAM,KAAK,CAAC,CACzD,CACF,CAAA,CACA,GAAIkC,EAAO,IAAA,GAAS,MAAA,CAAQ,CAC1BD,CAAAA,CAAU,IAAA,CAAKX,CAAK,CAAA,CACpB,MACF,CACAU,CAAAA,CAAM,IAAA,CAAKV,CAAK,EAClB,CAAC,EAEM,CAAE,KAAA,CAAAU,EAAO,SAAA,CAAAC,CAAU,CAC5B,CAqBO,SAASG,EACdC,CAAAA,CACgD,CAChD,OAAId,CAAAA,CAAwBc,GAAM,KAAK,CAAA,CAC9B,CACL,KAAA,CAAOjB,CAAAA,CAA6BiB,EAAK,KAAK,CAAA,CAC9C,UAAWjB,CAAAA,CACT,KAAA,CAAM,QAAQiB,CAAAA,EAAM,cAAc,EAC9BA,CAAAA,CAAK,cAAA,CACLd,EAAwBc,CAAAA,EAAM,SAAS,EACrCA,CAAAA,CAAK,SAAA,CACL,EACR,CACF,EAGEb,EAAAA,CAAsBa,CAAAA,EAAM,KAAK,CAAA,CAC5BZ,CAAAA,CAAyBY,EAAK,KAAK,CAAA,CAGrC,CACL,KAAA,CAAOjB,CAAAA,CAA6B,MAAM,OAAA,CAAQiB,CAAAA,EAAM,UAAU,CAAA,CAAIA,CAAAA,CAAK,UAAA,CAAa,EAAE,CAAA,CAC1F,SAAA,CAAWjB,EACT,KAAA,CAAM,OAAA,CAAQiB,GAAM,cAAc,CAAA,CAC9BA,EAAK,cAAA,CACLd,CAAAA,CAAwBc,GAAM,SAAS,CAAA,CACrCA,EAAK,SAAA,CACL,EACR,CACF,CACF,CCrWO,IAAMC,EAAAA,CAA6BzH,EAEpCqE,EAAAA,CAA2B,eAAA,CAC3BC,GAA0B,oBAAA,CAC1BC,EAAAA,CAA2B,cAE3B/B,CAAAA,CAAYC,CAAAA,EAChB,OAAOA,CAAAA,EAAU,QAAA,EAAYA,IAAU,IAAA,EAAQ,CAAC,MAAM,OAAA,CAAQA,CAAK,EAE/DiF,CAAAA,CAAUjF,CAAAA,EAAuC,CACrD,GAAI,OAAOA,CAAAA,EAAU,QAAA,CACnB,OAEF,IAAMkF,CAAAA,CAAUlF,EAAM,IAAA,EAAK,CAC3B,OAAOkF,CAAAA,CAAQ,MAAA,CAAS,EAAIA,CAAAA,CAAU,MACxC,EAEMC,CAAAA,CAAsBnF,CAAAA,EAA2C,CACrE,IAAMrC,CAAAA,CAAasH,EAAOjF,CAAK,CAAA,EAAG,aAAY,CAC9C,GAAIrC,IAAe,OAAA,CACjB,OAAO,QAET,GAAIA,CAAAA,GAAe,QAAUA,CAAAA,GAAe,MAAA,CAC1C,OAAO,MAGX,CAAA,CAEMyH,EACJpF,CAAAA,EACyD,CACzD,IAAMrC,CAAAA,CAAasH,CAAAA,CAAOjF,CAAK,CAAA,EAAG,aAAY,CAC9C,GACErC,IAAe,KAAA,EACfA,CAAAA,GAAe,OACfA,CAAAA,GAAe,KAAA,EACfA,IAAe,KAAA,CAEf,OAAOA,CAGX,CAAA,CAEM0H,EAAAA,CAAsB/H,GAC1BD,CAAAA,CAAgBC,CAAQ,EAAI,MAAA,CAAS,OAAA,CAEjCgI,GAAsB,CAAC,CAC3B,KAAA3F,CAAAA,CACA,IAAA,CAAA4F,CACF,CAAA,GAIMlI,CAAAA,CAAgBkI,CAAI,CAAA,CACfzD,EAAAA,CAEFnC,IAAS,MAAA,CAASkC,EAAAA,CAA0BD,GAO9C,SAAS4D,CAAAA,CACd/H,EACY,CACZ,OAAO0H,EAAmB1H,CAAAA,CAAK,IAAI,CAAA,EAAK4H,EAAAA,CAAmB5H,EAAK,IAAI,CACtE,CASO,SAASgI,CAAAA,CACdhI,EACQ,CACR,OACEwH,EAAOxH,CAAAA,CAAK,KAAK,GACjB6H,EAAAA,CAAoB,CAClB,KAAME,CAAAA,CAAsB/H,CAAI,EAChC,IAAA,CAAMA,CAAAA,CAAK,IACb,CAAC,CAEL,CAGO,IAAMiI,EAAAA,CAAgCD,EAUtC,SAASE,CAAAA,CAAqB3F,EAA0C,CAC7E,GAAI,CAAC,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,CACtB,OAGF,IAAM0E,CAAAA,CAAQ1E,CAAAA,CACX,IAAK0C,CAAAA,EAA6B,CACjC,GAAI,CAAC3C,EAAS2C,CAAK,CAAA,CACjB,OAAO,IAAA,CAGT,IAAMkD,EAAQX,CAAAA,CAAOvC,CAAAA,CAAM,KAAK,CAAA,CAChC,GAAI,CAACkD,CAAAA,CACH,OAAO,KAGT,IAAML,CAAAA,CAAON,EAAOvC,CAAAA,CAAM,IAAI,EACxB/C,CAAAA,CAAO6F,CAAAA,CAAsB,CACjC,IAAA,CAAML,CAAAA,CAAmBzC,EAAM,IAAI,CAAA,CACnC,KAAA6C,CACF,CAAC,EACKpC,CAAAA,CAAQsC,CAAAA,CAAuB,CACnC,IAAA,CAAA9F,CAAAA,CACA,MAAOsF,CAAAA,CAAOvC,CAAAA,CAAM,KAAK,CAAA,CACzB,IAAA,CAAA6C,CACF,CAAC,EACKnD,CAAAA,CAAWd,CAAAA,CAA2BoB,EAAM,QAAA,EAAYA,CAAK,EAC7DmD,CAAAA,CACJ,OAAOnD,EAAM,KAAA,EAAU,QAAA,EAAY,OAAO,QAAA,CAASA,CAAAA,CAAM,KAAK,CAAA,CAAIA,CAAAA,CAAM,MAAQ,MAAA,CAiBlF,OAfyB,CACvB,GAAIuC,CAAAA,CAAOvC,EAAM,EAAE,CAAA,CAAI,CAAE,EAAA,CAAIuC,CAAAA,CAAOvC,EAAM,EAAE,CAAE,EAAI,EAAC,CACnD,MAAAkD,CAAAA,CACA,IAAA,CAAAjG,EACA,KAAA,CAAAwD,CAAAA,CACA,UAAW,OAAOT,CAAAA,CAAM,WAAc,SAAA,CAAYA,CAAAA,CAAM,UAAY,MAAA,CACpE,GAAI6C,EAAO,CAAE,IAAA,CAAAA,CAAK,CAAA,CAAI,GACtB,GAAIN,CAAAA,CAAOvC,EAAM,SAAS,CAAA,CAAI,CAAE,SAAA,CAAWuC,CAAAA,CAAOvC,EAAM,SAAS,CAAmB,EAAI,EAAC,CACzF,GAAI0C,CAAAA,CAAuB1C,CAAAA,CAAM,aAAa,CAAA,CAC1C,CAAE,cAAe0C,CAAAA,CAAuB1C,CAAAA,CAAM,aAAa,CAAE,CAAA,CAC7D,EAAC,CACL,GAAI,OAAOmD,CAAAA,EAAU,QAAA,CAAW,CAAE,KAAA,CAAAA,CAAM,CAAA,CAAI,GAC5C,GAAIzD,CAAAA,CAAW,CAAE,QAAA,CAAAA,CAAS,EAAI,EAChC,CAGF,CAAC,CAAA,CACA,OAAQM,CAAAA,EAA+BA,CAAAA,GAAU,IAAI,CAAA,CAExD,OAAOgC,EAAM,MAAA,CAAS,CAAA,CAAIA,EAAQ,MACpC,CAKA,SAASoB,CAAAA,CAAoB9F,CAAAA,CAAgBL,EAAgC,CAC3E,OAAK,MAAM,OAAA,CAAQK,CAAK,EAIjBA,CAAAA,CAAM,OAAA,CAAS0C,GAChB,CAAC3C,CAAAA,CAAS2C,CAAK,CAAA,EAAK,CAAC,MAAM,OAAA,CAAQA,CAAAA,CAAM,KAAK,CAAA,CACzC,EAAC,CAGHA,CAAAA,CAAM,MACV,GAAA,CAAI,CAACqD,EAASxG,CAAAA,GAA6B,CAC1C,GAAI,CAACQ,CAAAA,CAASgG,CAAO,CAAA,CACnB,OAAO,KAGT,IAAMH,CAAAA,CAAQX,EAAOc,CAAAA,CAAQ,KAAK,EAClC,GAAI,CAACH,EACH,OAAO,IAAA,CAGT,IAAML,CAAAA,CAAON,CAAAA,CAAOc,EAAQ,IAAI,CAAA,CAC1B5C,EAAQsC,CAAAA,CAAuB,CACnC,KAAA9F,CAAAA,CACA,KAAA,CAAOsF,EAAOvC,CAAAA,CAAM,IAAI,EACxB,IAAA,CAAA6C,CACF,CAAC,CAAA,CACKnD,EAAWd,CAAAA,CAA2ByE,CAAAA,CAAQ,UAAYA,CAAO,CAAA,CACjEF,EACJ,OAAOE,CAAAA,CAAQ,OAAU,QAAA,EAAY,MAAA,CAAO,SAASA,CAAAA,CAAQ,KAAK,EAC9DA,CAAAA,CAAQ,KAAA,CACRxG,EAEN,OAAO,CACL,GAAI0F,CAAAA,CAAOc,CAAAA,CAAQ,EAAE,CAAA,CAAI,CAAE,GAAId,CAAAA,CAAOc,CAAAA,CAAQ,EAAE,CAAE,CAAA,CAAI,EAAC,CACvD,KAAA,CAAAH,EACA,IAAA,CAAAjG,CAAAA,CACA,MAAAwD,CAAAA,CACA,SAAA,CAAW,OAAO4C,CAAAA,CAAQ,SAAA,EAAc,SAAA,CAAYA,CAAAA,CAAQ,UAAY,MAAA,CACxE,GAAIR,EAAO,CAAE,IAAA,CAAAA,CAAK,CAAA,CAAI,GACtB,GAAIN,CAAAA,CAAOc,EAAQ,SAAS,CAAA,CACxB,CAAE,SAAA,CAAWd,CAAAA,CAAOc,EAAQ,SAAS,CAAmB,EACxD,EAAC,CACL,GAAIX,CAAAA,CAAuBW,CAAAA,CAAQ,aAAa,CAAA,CAC5C,CAAE,cAAeX,CAAAA,CAAuBW,CAAAA,CAAQ,aAAa,CAAE,CAAA,CAC/D,EAAC,CACL,KAAA,CAAAF,EACA,GAAIzD,CAAAA,CAAW,CAAE,QAAA,CAAAA,CAAS,CAAA,CAAI,EAChC,CACF,CAAC,EACA,MAAA,CAAQ3E,CAAAA,EAA6BA,IAAS,IAAI,CACtD,EAjDQ,EAkDX,CAYO,SAASuI,CAAAA,CAAuBhG,EAA0C,CAC/E,GAAID,EAASC,CAAK,CAAA,CAAG,CACnB,IAAMiG,CAAAA,CAAYN,EAAqB3F,CAAAA,CAAM,KAAK,EAClD,GAAIiG,CAAAA,CACF,OAAOA,CAAAA,CAKT,IAAMC,EAAiBJ,CAAAA,CAAoB9F,CAAAA,CAAM,MAAO,OAAO,CAAA,CACzDmG,EACJD,CAAAA,CAAe,MAAA,CAAS,EACpBA,CAAAA,CACAJ,CAAAA,CAAoB9F,CAAAA,CAAM,UAAA,CAAY,OAAO,CAAA,CAC7CoG,CAAAA,CAAiBN,EAAoB9F,CAAAA,CAAM,cAAA,CAAgB,MAAM,CAAA,CACjE2E,CAAAA,CACJyB,EAAe,MAAA,CAAS,CAAA,CACpBA,EACAN,CAAAA,CAAoB9F,CAAAA,CAAM,UAAW,MAAM,CAAA,CAC3C0E,EAAQ,CAAC,GAAGyB,EAAY,GAAGxB,CAAS,EAC1C,OAAOD,CAAAA,CAAM,OAAS,CAAA,CAAIA,CAAAA,CAAQ,MACpC,CAGA,IAAMuB,EAAYN,CAAAA,CAAqB3F,CAAK,EAC5C,GAAIiG,CAAAA,CACF,OAAOA,CAAAA,CAET,IAAMI,EAAaP,CAAAA,CAAoB9F,CAAAA,CAAO,OAAO,CAAA,CACrD,OAAOqG,CAAAA,CAAW,MAAA,CAAS,EAAIA,CAAAA,CAAa,MAC9C,CAGO,IAAMC,EAAAA,CAAgCN,EAMtC,SAASO,EAAAA,CAA6B,CAC3C,UAAA,CAAAC,CAAAA,CACA,eAAAC,CACF,CAAA,CAGiB,CACf,IAAM/B,CAAAA,CAAsB,EAAC,CAEvBgC,CAAAA,CAAa,CAACC,CAAAA,CAAiChH,CAAAA,GAAqB,CACxEgH,CAAAA,EAAQ,OAAA,CAAS3C,GAAU,CACzBA,CAAAA,CAAM,MAAM,OAAA,CAAQ,CAACvG,EAAM8B,CAAAA,GAAU,CACnCmF,EAAM,IAAA,CAAK,CACT,GAAI,OAAOjH,CAAAA,CAAK,IAAO,QAAA,EAAYA,CAAAA,CAAK,GAAG,IAAA,EAAK,CAAE,OAAS,CAAA,CAAI,CAAE,GAAIA,CAAAA,CAAK,EAAG,EAAI,EAAC,CAClF,MAAOA,CAAAA,CAAK,KAAA,CACZ,KAAAkC,CAAAA,CACA,KAAA,CAAOqE,EAAM,IAAA,CACb,SAAA,CAAWvG,EAAK,SAAA,CAChB,GAAIA,EAAK,IAAA,CAAO,CAAE,KAAMA,CAAAA,CAAK,IAAK,EAAI,EAAC,CACvC,GAAIA,CAAAA,CAAK,SAAA,CAAY,CAAE,SAAA,CAAWA,CAAAA,CAAK,SAAU,CAAA,CAAI,GACrD,GAAIA,CAAAA,CAAK,aAAA,CAAgB,CAAE,cAAeA,CAAAA,CAAK,aAAc,EAAI,EAAC,CAClE,MAAO8B,CAAAA,CACP,GAAI9B,EAAK,QAAA,CAAW,CAAE,SAAUA,CAAAA,CAAK,QAAS,EAAI,EACpD,CAAC,EACH,CAAC,EACH,CAAC,EACH,EAEA,OAAAiJ,CAAAA,CAAWF,EAAY,OAAO,CAAA,CAC9BE,EAAWD,CAAAA,CAAgB,MAAM,EAC1B/B,CACT,KAGakC,EAAAA,CAAiCL,GAMvC,SAASM,CAAAA,CACdnC,CAAAA,CAIA,CACA,GAAI,CAAC,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,EAAKA,CAAAA,CAAM,SAAW,CAAA,CAC5C,OAAO,CAAE,UAAA,CAAY,GAAI,cAAA,CAAgB,EAAG,CAAA,CAG9C,GAAM,CAAE,KAAA,CAAO8B,CAAAA,CAAY,UAAWC,CAAe,CAAA,CAAItC,EAAyBO,CAAK,CAAA,CACvF,OAAO,CACL,UAAA,CAAA8B,EACA,cAAA,CAAAC,CACF,CACF,CAGO,IAAMK,GAAwCD,EAkB9C,SAASE,EACdvJ,CAAAA,CACAC,CAAAA,CACAK,EAA4C,EAAC,CACnC,CACV,IAAMkJ,CAAAA,CAAazJ,CAAAA,CAAkBC,CAAAA,CAAeC,CAAI,CAAA,CAKxD,GAAIJ,EAAgBI,CAAAA,CAAK,IAAI,EAC3B,OAAO,CAACuJ,CAAU,CAAA,CAGpB,IAAMC,EAAuB,MAAA,CAAOnJ,CAAAA,CAAQ,YAAc,EAAE,CAAA,CAAE,aAAY,CACpE6B,CAAAA,CAAO7B,EAAQ,IAAA,EAAQ,OAAA,CAE7B,OAAImJ,CAAAA,GAAyB,QAAA,EAAYA,IAAyB,QAAA,CACzD,CAACD,EAAY,CAAA,EAAGA,CAAU,SAAS,CAAA,CAGxCC,CAAAA,GAAyB,eAAiBA,CAAAA,GAAyB,aAAA,CAC9DtH,IAAS,MAAA,CACZ,CAACqH,EAAY,CAAA,EAAGA,CAAU,CAAA,OAAA,CAAS,CAAA,CACnC,CAACA,CAAAA,CAAY,CAAA,EAAGA,CAAU,CAAA,OAAA,CAAS,CAAA,CAGlC,CAACA,CAAU,CACpB,CA0BO,SAASE,CAAAA,CACdC,EACAC,CAAAA,CACAtJ,CAAAA,CAGI,EAAC,CAC4B,CACjC,IAAMuJ,CAAAA,CAAWvJ,CAAAA,CAAQ,iBAAoBkC,CAAAA,EAAkB5C,CAAAA,CAAkB4C,CAAK,CAAA,CAAA,CAChFsH,CAAAA,CAA4BD,EAASD,CAAM,CAAA,CAC3C,CAAE,KAAA,CAAA1C,CAAAA,CAAO,UAAAC,CAAU,CAAA,CAAIG,EAAsBqC,CAAQ,CAAA,CAErDI,EAAe,CAACZ,CAAAA,CAAqBhH,IAAsD,CAC/F,IAAA,IAAW6H,CAAAA,IAAab,CAAAA,CACtB,QAAWlJ,CAAAA,IAAQ+J,CAAAA,CAAU,MAK3B,GAJwBT,CAAAA,CAA0BS,EAAU,IAAA,CAAM/J,CAAAA,CAAM,CACtE,UAAA,CAAYK,CAAAA,CAAQ,WACpB,IAAA,CAAA6B,CACF,CAAC,CAAA,CAEiB,IAAA,CACb8H,IAAcJ,CAAAA,CAASI,EAAS,IAAMH,CACzC,CAAA,CAEA,OAAO,CAAE,IAAA,CAAA7J,EAAM,SAAA,CAAW+J,CAAAA,CAAU,KAAM,IAAA,CAAA7H,CAAK,CAKvD,CAAA,CAEA,OAAO4H,EAAa7C,CAAAA,CAAO,OAAO,GAAK6C,CAAAA,CAAa5C,CAAAA,CAAW,MAAM,CACvE,CAMO,SAAS+C,EAAAA,CACdP,EACAC,CAAAA,CACAtJ,CAAAA,CAGI,EAAC,CACI,CACT,OAAOoJ,CAAAA,CAAmBC,CAAAA,CAAUC,EAAQtJ,CAAO,CAAA,GAAM,MAC3D,CAMO,SAAS6J,GACdjD,CAAAA,CAC0D,CAC1D,OAAOmC,CAAAA,CAA+BnC,CAAK,CAC7C,CCvUO,SAASkD,GAAeC,CAAAA,CAA2B,CAExD,OADsBA,CAAAA,CAAY,EAAA,CACX,EAAI,KAAA,CAAQ,KACrC,CCjGA,IAAMC,EAAAA,CAAqB,IAAI,GAAA,CAAI,CAAC,cAAe,SAAA,CAAW,aAAa,CAAC,CAAA,CACtEC,CAAAA,CAAoB,CACxB,eAAA,CACA,eACA,uBAAA,CACA,sBAAA,CACA,WACA,eAAA,CACA,aAAA,CACA,cACF,CAAA,CACMC,CAAAA,CAAwB,CAC5B,YAAA,CACA,aAAA,CACA,eACA,eAAA,CACA,cACF,EACMC,CAAAA,CAAsB,CAC1B,SACA,OAAA,CACA,SAAA,CACA,SACA,QAAA,CACA,OAAA,CACA,YACA,cAAA,CACA,kBAAA,CACA,YACA,UAAA,CACA,aAAA,CACA,eACF,CAAA,CACMC,EAAAA,CAAqB,CAAC,MAAA,CAAQ,eAAe,EAC7CC,EAAAA,CAAuB,IAAI,IAAY,CAC3C,OAAA,CACA,QACA,YAAA,CACA,gBAAA,CACA,gBACA,aAAA,CACA,UAAA,CACA,aACA,aAAA,CACA,eAAA,CACA,YACA,WAAA,CACA,YAAA,CACA,eACA,gBAAA,CACA,GAAGJ,EACH,GAAGC,CAAAA,CACH,GAAGC,CAAAA,CACH,GAAGC,EACL,CAAC,CAAA,CAED,SAASE,EAAAA,CAAchG,CAAAA,CAA4E,CACjG,OAAO,CACL,EAAG,OAAOA,CAAAA,EAAU,GAAM,QAAA,CAAWA,CAAAA,CAAS,EAAI,CAAA,CAClD,CAAA,CAAG,OAAOA,CAAAA,EAAU,CAAA,EAAM,SAAWA,CAAAA,CAAS,CAAA,CAAI,CACpD,CACF,CAEA,SAASiG,CAAAA,CAAkBjG,CAAAA,CAA8D,CACvF,GAAKA,EAIL,OAAO,CAAE,GAAGA,CAAS,CACvB,CAEA,SAASkG,CAAAA,CAAed,EAAiC,CACvD,OAAO,CACL,GAAGA,CAAAA,CACH,MAAOA,CAAAA,CAAU,KAAA,CAAM,IAAK/J,CAAAA,GAAU,CACpC,GAAGA,CAAAA,CACH,aAAA,CAAe,MAAM,OAAA,CAAQA,CAAAA,CAAK,aAAa,CAAA,CAAI,CAAC,GAAGA,CAAAA,CAAK,aAAa,EAAI,MAAA,CAC7E,QAAA,CAAU4K,EAAkB5K,CAAAA,CAAK,QAAQ,CAC3C,CAAA,CAAE,CACJ,CACF,CAEA,SAAS8K,EAAAA,CAAgBC,CAAAA,CAAuC,CAC9D,GAAI,OAAOA,EAAK,QAAA,EAAa,QAAA,CAC3B,OAAOA,CAAAA,CAAK,QAAA,CAGd,IAAMC,CAAAA,CAAgBD,CAAAA,CAA+C,WACrE,OAAO,OAAOC,GAAiB,QAAA,CAAWA,CAAAA,CAAe,MAC3D,CAEA,SAASC,GAAkBF,CAAAA,CAAgE,CACzF,OAAIA,CAAAA,CAAK,IAAA,GAAS,eACT,CAAE,IAAA,CAAM,KAAM,CAAA,CAGnBA,CAAAA,CAAK,OAAS,QAAA,EAAYA,CAAAA,CAAK,OAAS,QAAA,EAAYA,CAAAA,CAAK,OAAS,QAAA,EAAYA,CAAAA,CAAK,OAAS,QAAA,CAKvF,CAAE,IAAA,CAAM,WAAA,CAAa,QAH1B,OAAOA,CAAAA,CAAK,MAAM,UAAA,EAAe,QAAA,EAAYA,EAAK,IAAA,CAAK,UAAA,CAAW,MAAK,CAAE,MAAA,CAAS,EAC9EA,CAAAA,CAAK,IAAA,CAAK,WACVA,CAAAA,CAAK,IACyB,EAGlCA,CAAAA,CAAK,IAAA,GAAS,eAAiBA,CAAAA,CAAK,IAAA,GAAS,WAAaA,CAAAA,CAAK,IAAA,GAAS,cACnE,CAAE,IAAA,CAAM,YAAa,OAAA,CAASA,CAAAA,CAAK,IAAK,CAAA,CAG7CA,CAAAA,CAAK,OAAS,aAAA,CACT,CAAE,KAAM,UAAW,CAAA,CAGxBA,EAAK,IAAA,GAAS,kBAAA,EAAsBA,CAAAA,CAAK,IAAA,GAAS,qBAC7C,CAAE,IAAA,CAAMA,EAAK,IAAK,CAAA,CAGpB,CAAE,IAAA,CAAMA,CAAAA,CAAK,IAAwB,CAC9C,CAEA,SAASG,EAAAA,CAAiBH,CAAAA,CAAoC,CAC5D,GAAIA,CAAAA,CAAK,OAAS,WAAA,CAAa,CAC7B,IAAMI,CAAAA,CAAAA,CAAqBJ,CAAAA,CAAK,SAAW,QAAA,EAAU,WAAA,GAAc,OAAA,CAAQ,IAAA,CAAM,GAAG,CAAA,CACpF,OAAII,IAAsB,QAAA,EAAYA,CAAAA,GAAsB,UAAYA,CAAAA,GAAsB,QAAA,CACrFA,EAEF,QACT,CAEA,GAAIJ,CAAAA,CAAK,IAAA,GAAS,KAAA,CAChB,OAAO,eAGT,GAAIA,CAAAA,CAAK,OAAS,WAAA,CAAa,CAC7B,IAAMK,CAAAA,CAAUL,CAAAA,CAAK,SAAW,aAAA,CAChC,OAAIV,GAAmB,GAAA,CAAIe,CAAO,EACzBA,CAAAA,CAEF,aACT,CAEA,OAAIL,CAAAA,CAAK,OAAS,UAAA,CACT,aAAA,CAGFA,EAAK,IACd,CAEA,SAASM,EAAAA,CAAmBN,CAAAA,CAAyC,CACnE,IAAMO,CAAAA,CAAkC,CACtC,QAAA,CAAUX,EAAAA,CAAcI,EAAK,QAAQ,CACvC,EAEMQ,CAAAA,CAAWT,EAAAA,CAAgBC,CAAI,CAAA,CACjCQ,CAAAA,GACFD,CAAAA,CAAU,QAAA,CAAWC,GAEnBR,CAAAA,CAAK,MAAA,GAAW,WAClBO,CAAAA,CAAU,MAAA,CAAS,UAGrB,IAAMhE,CAAAA,CAAOyD,EAAK,IAAA,EAAQ,GAC1B,OAAA,CACE,OAAOzD,EAAK,SAAA,EAAc,QAAA,EAC1BA,EAAK,SAAA,GAAc,OAAA,EACnBA,EAAK,SAAA,GAAc,MAAA,EACnBA,EAAK,SAAA,GAAc,MAAA,EACnBA,EAAK,UAAA,GAAe,aAAA,EACpBA,EAAK,UAAA,GAAe,YAAA,IAEpBgE,EAAU,IAAA,CAAO,CACf,GAAI,OAAOhE,CAAAA,CAAK,WAAc,QAAA,CAAW,CAAE,UAAWA,CAAAA,CAAK,SAAU,CAAA,CAAI,GACzE,GAAIA,CAAAA,CAAK,UAAY,CAAE,SAAA,CAAWA,EAAK,SAAU,CAAA,CAAI,EAAC,CACtD,GAAIA,EAAK,UAAA,CAAa,CAAE,WAAYA,CAAAA,CAAK,UAAW,EAAI,EAAC,CACzD,GAAI,OAAOA,CAAAA,CAAK,aAAgB,QAAA,CAAW,CAAE,YAAaA,CAAAA,CAAK,WAAY,EAAI,EAAC,CAChF,GAAI,OAAOA,CAAAA,CAAK,eAAkB,QAAA,CAAW,CAAE,cAAeA,CAAAA,CAAK,aAAc,EAAI,EACvF,CAAA,CAAA,CAAA,CAGE,OAAOA,EAAK,YAAA,EAAiB,QAAA,EAAY,OAAOA,CAAAA,CAAK,cAAA,EAAmB,YAC1EgE,CAAAA,CAAU,SAAA,CAAY,CACpB,GAAI,OAAOhE,EAAK,YAAA,EAAiB,QAAA,CAAW,CAAE,YAAA,CAAcA,CAAAA,CAAK,YAAa,CAAA,CAAI,GAClF,GAAI,OAAOA,EAAK,cAAA,EAAmB,QAAA,CAAW,CAAE,cAAA,CAAgBA,CAAAA,CAAK,cAAe,CAAA,CAAI,EAC1F,CAAA,CAAA,CAGKgE,CACT,CAEA,SAASE,EAAAA,CAAgBT,EAAkD,CACzE,IAAMzD,EAAOyD,CAAAA,CAAK,IAAA,EAAQ,EAAC,CACrBU,EAA4B,EAAC,CAEnCjB,EAAoB,OAAA,CAASzD,CAAAA,EAAQ,CACnC,IAAMxE,CAAAA,CAAQ+E,EAAKP,CAAG,CAAA,CAClBxE,IAAU,MAAA,GACXkJ,CAAAA,CAAmC1E,CAAG,CAAA,CAAIxE,CAAAA,EAE/C,CAAC,CAAA,CAED,IAAMmJ,EAASpB,CAAAA,CAAkB,MAAA,CAAgC,CAACqB,CAAAA,CAAK5E,CAAAA,GAAQ,CAC7E,IAAMxE,CAAAA,CAAQ+E,EAAKP,CAAG,CAAA,CACtB,OAAIxE,CAAAA,GAAU,MAAA,GACZoJ,EAAI5E,CAAG,CAAA,CAAIxE,GAENoJ,CACT,CAAA,CAAG,EAAE,CAAA,CACD,MAAA,CAAO,IAAA,CAAKD,CAAM,CAAA,CAAE,MAAA,CAAS,IAC/BD,CAAAA,CAAO,MAAA,CAASC,GAGlB,IAAME,CAAAA,CAAarB,EAAsB,MAAA,CAA2C,CAACoB,EAAK5E,CAAAA,GAAQ,CAChG,IAAMxE,CAAAA,CAAQ+E,CAAAA,CAAKP,CAAG,CAAA,CACtB,OAAI,OAAOxE,CAAAA,EAAU,QAAA,GACnBoJ,EAAI5E,CAAG,CAAA,CAAIxE,GAENoJ,CACT,CAAA,CAAG,EAAE,CAAA,CACL,OAAI,MAAA,CAAO,IAAA,CAAKC,CAAU,CAAA,CAAE,MAAA,CAAS,IACnCH,CAAAA,CAAO,UAAA,CAAaG,GAGf,MAAA,CAAO,IAAA,CAAKH,CAAM,CAAA,CAAE,OAAS,CAAA,CAAIA,CAAAA,CAAS,MACnD,CAEA,SAASI,GAAsBd,CAAAA,CAAqD,CAClF,IAAMe,CAAAA,CAA6B,GAC7BxE,CAAAA,CAAOyD,CAAAA,CAAK,MAAQ,EAAC,CAE3B,OAAIzD,CAAAA,CAAK,QAAA,GAAa,QACpBwE,CAAAA,CAAK,IAAA,CAAK,CACR,MAAA,CAAQ,QAAA,CACR,GAAIxE,CAAAA,CAAK,QAAA,CACT,KAAMA,CAAAA,CAAK,UACb,CAAC,CAAA,CAGCA,CAAAA,CAAK,cAAgB,MAAA,EACvBwE,CAAAA,CAAK,KAAK,CACR,MAAA,CAAQ,YACR,EAAA,CAAIxE,CAAAA,CAAK,WAAA,CACT,IAAA,CAAMA,EAAK,aACb,CAAC,EAGIwE,CAAAA,CAAK,MAAA,CAAS,EAAIA,CAAAA,CAAO,MAClC,CAEA,SAASC,EAAAA,CAAgBhB,EAAwD,CAC/E,IAAMzD,EAAOyD,CAAAA,CAAK,IAAA,EAAQ,EAAC,CACrBiB,CAAAA,CAAkC,EAAC,CAEzC,OAAA,MAAA,CAAO,QAAQ1E,CAAI,CAAA,CAAE,QAAQ,CAAC,CAACP,EAAKxE,CAAK,CAAA,GAAM,CACzCmI,EAAAA,CAAqB,GAAA,CAAI3D,CAAG,CAAA,EAAA,CAG3BA,CAAAA,GAAQ,eAAiBA,CAAAA,GAAQ,eAAA,GAAoBgE,EAAK,IAAA,GAAS,MAAA,GAGxEiB,CAAAA,CAAOjF,CAAG,EAAIxE,CAAAA,EAChB,CAAC,EAEM,MAAA,CAAO,IAAA,CAAKyJ,CAAM,CAAA,CAAE,MAAA,CAAS,EAAIA,CAAAA,CAAS,MACnD,CAEA,SAASC,EAAAA,CAAeC,EAAsBC,CAAAA,CAAyD,CACrG,IAAMC,CAAAA,CAAsD,GAsB5D,GApBAF,CAAAA,CAAM,QAASnB,CAAAA,EAAS,CACtB,IAAMzD,CAAAA,CAAOyD,CAAAA,CAAK,MAAQ,EAAC,CACrB9F,EAA0D,EAAC,CAC3DoH,EACJ/E,CAAAA,CAAK,IAAA,GAAS,SAAWA,CAAAA,CAAK,IAAA,GAAS,OACnCA,CAAAA,CAAK,IAAA,CACL,MAAA,CAEF+E,CAAAA,GACFpH,EAAM,IAAA,CAAOoH,CAAAA,CAAAA,CAEX/E,EAAK,aAAA,EAAiB,OAAOA,EAAK,aAAA,EAAkB,QAAA,GACtDrC,EAAM,aAAA,CAAgB,CAAE,GAAGqC,CAAAA,CAAK,aAAc,GAG5C,MAAA,CAAO,IAAA,CAAKrC,CAAK,CAAA,CAAE,MAAA,CAAS,IAC9BmH,CAAAA,CAAUrB,CAAAA,CAAK,EAAE,CAAA,CAAI9F,CAAAA,EAEzB,CAAC,CAAA,CAEG,EAAA,MAAA,CAAO,KAAKmH,CAAS,CAAA,CAAE,SAAW,CAAA,EAAK,CAACD,GAAY,QAAA,CAAA,CAIxD,OAAO,CACL,GAAI,MAAA,CAAO,KAAKC,CAAS,CAAA,CAAE,MAAA,CAAS,CAAA,CAAI,CAAE,SAAA,CAAAA,CAAU,EAAI,EAAC,CACzD,GAAID,CAAAA,EAAY,QAAA,CAAW,CAAE,QAAA,CAAUA,CAAAA,CAAW,QAAS,CAAA,CAAI,EACjE,CACF,CAEA,SAASG,EAAAA,CAAoBzM,CAAAA,CAAyD,CACpF,IAAM0M,CAAAA,CAAiB,OAAO1M,CAAAA,EAAY,EAAE,EAAE,WAAA,EAAY,CAC1D,OAAI0M,CAAAA,CAAe,QAAA,CAAS,OAAO,CAAA,CAAU,OAAA,CACzCA,EAAe,QAAA,CAAS,OAAO,EAAU,OAAA,CACzCA,CAAAA,CAAe,SAAS,SAAS,CAAA,EAAKA,EAAe,QAAA,CAAS,MAAM,GAAKA,CAAAA,CAAe,QAAA,CAAS,KAAK,CAAA,CACjG,SAAA,CAELA,EAAe,QAAA,CAAS,UAAU,GAAKA,CAAAA,CAAe,QAAA,CAAS,MAAM,CAAA,CAAU,UAAA,CAC5E,SACT,CAEA,SAASC,GAAmBzB,CAAAA,CAAgC,CAC1D,IAAMzD,CAAAA,CAAOyD,CAAAA,CAAK,MAAQ,EAAC,CAC3B,GAAI,KAAA,CAAM,OAAA,CAAQzD,EAAK,aAAa,CAAA,EAAKA,EAAK,aAAA,CAAc,MAAA,CAAS,EACnE,OAAOA,CAAAA,CAAK,cAAc,GAAA,CAAIuD,CAAc,EAG9C,GAAM,CAAE,KAAA,CAAA5D,CAAAA,CAAO,UAAAC,CAAU,CAAA,CAAIG,EAAsBC,CAAI,CAAA,CACvD,GAAIJ,CAAAA,CAAU,MAAA,CAAS,EACrB,OAAOA,CAAAA,CAAU,IAAI2D,CAAc,CAAA,CAErC,GAAI5D,CAAAA,CAAM,MAAA,CAAS,EACjB,OAAOA,CAAAA,CAAM,IAAI4D,CAAc,CAAA,CAGjC,IAAM4B,CAAAA,CACJ,OAAOnF,EAAK,UAAA,EAAe,QAAA,EAAY,OAAO,QAAA,CAASA,CAAAA,CAAK,UAAU,CAAA,EAAKA,CAAAA,CAAK,WAAa,CAAA,CACzFA,CAAAA,CAAK,WACL,EAAA,CAEN,OAAO,CACL,CACE,IAAA,CAAM,OAAA,CACN,KAAA,CAAO,MAAM,IAAA,CAAK,CAAE,OAAQmF,CAAW,CAAA,CAAG,CAACC,CAAAA,CAAG5K,CAAAA,GAAU,CACtD,IAAM6K,CAAAA,CAAgBxC,GAAerI,CAAK,CAAA,CAC1C,OAAO,CACL,EAAA,CAAI,GAAGA,CAAAA,CAAQ,CAAC,GAChB,KAAA,CAAO,CAAA,EAAGA,EAAQ,CAAC,CAAA,CAAA,CACnB,UAAW,KAAA,CACX,IAAA,CAAM6K,IAAkB,KAAA,CAAQ,WAAA,CAAc,YAC9C,aAAA,CAAeA,CACjB,CACF,CAAC,CACH,CACF,CACF,CAEA,SAASC,EAAAA,CAAqB7B,CAAAA,CAAsC,CAYlE,OAAA,CAVEA,EAAK,IAAA,GAAS,cAAA,CACV,CAAC,CAAE,IAAA,CAAM,OAAQ,MAAA,CAAQyB,EAAAA,CAAmBzB,CAAI,CAAE,CAAC,GAClD,IAAM,CACL,GAAM,CAAE,KAAA,CAAA9D,EAAO,SAAA,CAAAC,CAAU,EAAIG,CAAAA,CAAsB0D,CAAAA,CAAK,IAAI,CAAA,CAC5D,OAAO,CACL,CAAE,IAAA,CAAM,QAAkB,MAAA,CAAQ9D,CAAAA,CAAM,IAAI4D,CAAc,CAAE,EAC5D,CAAE,IAAA,CAAM,OAAiB,MAAA,CAAQ3D,CAAAA,CAAU,IAAI2D,CAAc,CAAE,CACjE,CACF,IAAG,EAEW,OAAA,CAAQ,CAAC,CAAE,IAAA,CAAA3I,EAAM,MAAA,CAAAgH,CAAO,IAC1CA,CAAAA,CAAO,OAAA,CAAS3C,GACdA,CAAAA,CAAM,KAAA,CAAM,IAAKvG,CAAAA,GAAU,CACzB,GAAIA,CAAAA,CAAK,EAAA,EAAM,GAAGuG,CAAAA,CAAM,IAAI,IAAIvG,CAAAA,CAAK,KAAK,GAC1C,KAAA,CAAOA,CAAAA,CAAK,MACZ,IAAA,CAAAkC,CAAAA,CACA,MAAOqE,CAAAA,CAAM,IAAA,CACb,KAAM3G,CAAAA,CAAgBI,CAAAA,CAAK,IAAI,CAAA,CAAI,OAAA,CAAU,UAC7C,MAAA,CAAQsM,EAAAA,CAAoBtM,CAAAA,CAAK,IAAI,EACrC,IAAA,CAAMA,CAAAA,CAAK,KACX,SAAA,CAAWA,CAAAA,CAAK,UAChB,SAAA,CAAWA,CAAAA,CAAK,cAChB,aAAA,CAAe,KAAA,CAAM,QAAQA,CAAAA,CAAK,aAAa,EAAI,CAAC,GAAGA,EAAK,aAAa,CAAA,CAAI,OAC7E,MAAA,CAAQ4K,CAAAA,CAAkB5K,EAAK,QAAQ,CACzC,EAAE,CACJ,CACF,CACF,CAEA,SAAS6M,GAAyB9B,CAAAA,CAAsC,CACtE,IAAMzD,CAAAA,CAAOyD,CAAAA,CAAK,MAAQ,EAAC,CAE3B,GAAIA,CAAAA,CAAK,IAAA,GAAS,MAAA,CAAQ,CACxB,IAAM+B,CAAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,CAAG,IAAA,CAAK,IAAI,EAAA,CAAI,MAAA,CAAOxF,EAAK,aAAa,CAAA,EAAK,EAAE,CAAC,CAAA,CAC1EyF,EAAY,IAAA,CAAK,GAAA,CAAI,EAAG,MAAA,CAAOzF,CAAAA,CAAK,SAAS,CAAA,EAAK,CAAC,EACzD,OAAO,CACL,GAAG,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQwF,CAAc,EAAG,CAACJ,CAAAA,CAAG5K,KAAW,CACtD,EAAA,CAAI,SAASA,CAAAA,CAAQ,CAAC,GACtB,KAAA,CAAO,CAAA,EAAGA,CAAAA,CAAQ,CAAC,GACnB,IAAA,CAAM,OAAA,CACN,OAAQ,OACV,CAAA,CAAE,EACF,CACE,EAAA,CAAI,QAAQiL,CAAS,CAAA,CAAA,CACrB,MAAO,CAAA,KAAA,EAAQA,CAAS,GACxB,IAAA,CAAM,MAAA,CACN,OAAQ,MACV,CACF,CACF,CAEA,GAAIhC,EAAK,IAAA,GAAS,OAAA,CAAS,CACzB,IAAMiC,CAAAA,CAAa,KAAK,GAAA,CAAI,CAAA,CAAG,KAAK,GAAA,CAAI,EAAA,CAAI,OAAO1F,CAAAA,CAAK,UAAU,GAAK,EAAE,CAAC,EAC1E,OAAO,CACL,GAAG,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQ0F,CAAW,EAAG,CAACN,CAAAA,CAAG5K,KAAW,CACnD,EAAA,CAAI,SAASA,CAAAA,CAAQ,CAAC,GACtB,KAAA,CAAO,CAAA,EAAGA,EAAQ,CAAC,CAAA,CAAA,CACnB,KAAM,OAAA,CACN,MAAA,CAAQ,OACV,CAAA,CAAE,CAAA,CACF,CACE,EAAA,CAAI,cAAA,CACJ,MAAO,OAAA,CACP,IAAA,CAAM,OACR,CACF,CACF,CAEA,GAAIiJ,CAAAA,CAAK,OAAS,kBAAA,CAAoB,CACpC,IAAMkC,CAAAA,CAAY,IAAA,CAAK,IAAI,CAAA,CAAG,IAAA,CAAK,GAAA,CAAI,EAAA,CAAI,OAAO3F,CAAAA,CAAK,SAAS,GAAK,EAAE,CAAC,EAClE4F,CAAAA,CAAiB,IAAA,CAAK,IAAI,CAAA,CAAG,MAAA,CAAO5F,EAAK,cAAc,CAAA,EAAK,CAAC,CAAA,CACnE,OAAO,CACL,GAAG,KAAA,CAAM,KAAK,CAAE,MAAA,CAAQ2F,CAAU,CAAA,CAAG,CAACP,EAAG5K,CAAAA,IAAW,CAClD,GAAI,CAAA,KAAA,EAAQoL,CAAAA,CAAiBpL,CAAK,CAAA,CAAA,CAClC,KAAA,CAAO,GAAGoL,CAAAA,CAAiBpL,CAAK,GAChC,IAAA,CAAM,MAAA,CACN,OAAQ,MACV,CAAA,CAAE,CAAA,CACF,CACE,GAAI,cAAA,CACJ,KAAA,CAAO,QACP,IAAA,CAAM,OACR,CACF,CACF,CAEA,GAAIiJ,CAAAA,CAAK,IAAA,GAAS,SAChB,OAAO,CACL,CAAE,EAAA,CAAI,MAAA,CAAQ,MAAO,MAAA,CAAQ,IAAA,CAAM,SAAU,MAAA,CAAQ,OAAQ,EAC7D,CAAE,EAAA,CAAI,QAAS,KAAA,CAAO,OAAA,CAAS,KAAM,QAAA,CAAU,MAAA,CAAQ,OAAQ,CACjE,CAAA,CAGF,GAAIA,CAAAA,CAAK,IAAA,GAAS,UAAW,CAC3B,IAAMoC,EAAY,OAAO7F,CAAAA,CAAK,iBAAA,EAAsB,QAAA,EAAY,CAAC,CAAA,CAAG,CAAA,CAAG,CAAC,CAAA,CAAE,QAAA,CAASA,EAAK,iBAAiB,CAAA,CACrGA,EAAK,iBAAA,CACL,CAAA,CACJ,OAAO,CACL,GAAG,MAAM,IAAA,CAAK,CAAE,OAAQ6F,CAAU,CAAA,CAAG,CAACT,CAAAA,CAAG5K,CAAAA,IAAW,CAClD,EAAA,CAAI,CAAA,KAAA,EAAQA,EAAQ,CAAC,CAAA,CAAA,CACrB,MAAO,CAAA,EAAGA,CAAAA,CAAQ,CAAC,CAAA,CAAA,CACnB,IAAA,CAAM,UACN,MAAA,CAAQ,OACV,EAAE,CAAA,CACF,GAAG,MAAM,IAAA,CAAK,CAAE,MAAA,CAAQqL,CAAU,EAAG,CAACT,CAAAA,CAAG5K,KAAW,CAClD,EAAA,CAAI,SAASA,CAAAA,CAAQ,CAAC,GACtB,KAAA,CAAO,CAAA,EAAGA,EAAQ,CAAC,CAAA,CAAA,CACnB,KAAM,SAAA,CACN,MAAA,CAAQ,OACV,CAAA,CAAE,CACJ,CACF,CAEA,GAAIiJ,EAAK,IAAA,GAAS,aAAA,CAAe,CAC/B,IAAMqC,CAAAA,CAAa,KAAK,GAAA,CAAI,CAAA,CAAG,KAAK,GAAA,CAAI,CAAA,CAAG,OAAO9F,CAAAA,CAAK,UAAU,GAAK,CAAC,CAAC,EAClE+F,CAAAA,CAAc,IAAA,CAAK,GAAA,CAAI,CAAA,CAAG,KAAK,GAAA,CAAI,EAAA,CAAI,OAAO/F,CAAAA,CAAK,WAAW,GAAK,CAAC,CAAC,EAC3E,OAAO,CACL,GAAG,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQ8F,CAAW,EAAG,CAACV,CAAAA,CAAG5K,KAAW,CACnD,EAAA,CAAI,SAASA,CAAAA,CAAQ,CAAC,GACtB,KAAA,CAAO,CAAA,MAAA,EAASA,EAAQ,CAAC,CAAA,CAAA,CACzB,KAAM,UAAA,CACN,MAAA,CAAQ,OACV,CAAA,CAAE,CAAA,CACF,GAAG,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQuL,CAAY,CAAA,CAAG,CAACX,EAAG5K,CAAAA,IAAW,CACpD,GAAI,CAAA,OAAA,EAAUA,CAAAA,CAAQ,CAAC,CAAA,CAAA,CACvB,KAAA,CAAO,UAAUA,CAAAA,CAAQ,CAAC,GAC1B,IAAA,CAAM,UAAA,CACN,OAAQ,OACV,CAAA,CAAE,CACJ,CACF,CAEA,OAAO,EACT,CAEA,SAASwL,EAAAA,CAAmBvC,EAAkD,CAC5E,IAAMwC,EAAYxC,CAAAA,CAAK,IAAA,GAAS,gBAAkB,KAAA,CAAM,OAAA,CAAQA,EAAK,IAAA,EAAM,KAAK,GAAK,KAAA,CAAM,OAAA,CAAQA,EAAK,IAAA,EAAM,UAAU,CAAA,EAAK,KAAA,CAAM,QAAQA,CAAAA,CAAK,IAAA,EAAM,cAAc,CAAA,EAAK,KAAA,CAAM,QAAQA,CAAAA,CAAK,IAAA,EAAM,aAAa,CAAA,CAC3M6B,EAAAA,CAAqB7B,CAAI,CAAA,CACzB8B,EAAAA,CAAyB9B,CAAI,CAAA,CAEjC,OAAOwC,EAAU,MAAA,CAAS,CAAA,CAAIA,EAAY,MAC5C,CAEA,SAASC,EAAAA,CACPC,CAAAA,CACAC,EAC+B,CAC/B,GAAI,CAACA,CAAAA,EAAiBA,CAAAA,CAAc,SAAW,CAAA,CAC7C,OAAOA,EAGT,IAAMC,CAAAA,CAAe,IAAI,GAAA,CAAA,CAAKF,CAAAA,EAAqB,EAAC,EAAG,GAAA,CAAKG,GAAa,CAACA,CAAAA,CAAS,GAAIA,CAAQ,CAAC,CAAC,CAAA,CACjG,OAAOF,EAAc,GAAA,CAAKE,CAAAA,EAAa,CACrC,IAAMC,CAAAA,CAAWF,EAAa,GAAA,CAAIC,CAAAA,CAAS,EAAE,CAAA,CAC7C,OAAKC,EAIE,CACL,GAAGA,EACH,GAAGD,CAAAA,CACH,cAAeA,CAAAA,CAAS,aAAA,EAAiBC,EAAS,aAAA,CAClD,MAAA,CAAQD,EAAS,MAAA,EAAUC,CAAAA,CAAS,MACtC,CAAA,CARSD,CASX,CAAC,CACH,CAEA,SAASE,EAAAA,CAAkBC,CAAAA,CAAuCC,EAAoC,CACpG,OAAKD,CAAAA,CAIE,CACL,GAAGA,CAAAA,CACH,GAAGC,EACH,SAAA,CAAWA,CAAAA,CAAS,UACpB,MAAA,CAAQA,CAAAA,CAAS,OACjB,MAAA,CAAQA,CAAAA,CAAS,OACjB,YAAA,CAAcA,CAAAA,CAAS,aACvB,QAAA,CAAUA,CAAAA,CAAS,UAAYD,CAAAA,CAAa,QAAA,CAC5C,UAAWP,EAAAA,CAAeO,CAAAA,CAAa,UAAWC,CAAAA,CAAS,SAAS,CACtE,CAAA,CAZSA,CAaX,CAEO,SAASC,EAAAA,CACdC,EACAC,CAAAA,CACiB,CACjB,IAAMC,CAAAA,CAAoB,IAAI,KAAKD,CAAAA,EAAkB,KAAA,EAAS,EAAC,EAAG,GAAA,CAAKpD,CAAAA,EAAS,CAACA,EAAK,EAAA,CAAIA,CAAI,CAAC,CAAC,CAAA,CAE1FmB,EAAQgC,CAAAA,CAAS,KAAA,CAAM,IAAKnD,CAAAA,EAChC+C,EAAAA,CAAkBM,EAAkB,GAAA,CAAIrD,CAAAA,CAAK,EAAE,CAAA,CAAG,CAChD,GAAIA,CAAAA,CAAK,EAAA,CACT,GAAGE,EAAAA,CAAkBF,CAAI,EACzB,KAAA,CAAOA,CAAAA,CAAK,MAAM,KAAA,EAASA,CAAAA,CAAK,GAChC,SAAA,CAAWM,EAAAA,CAAmBN,CAAI,CAAA,CAClC,MAAA,CAAQS,GAAgBT,CAAI,CAAA,CAC5B,OAAQgB,EAAAA,CAAgBhB,CAAI,EAC5B,YAAA,CAAcc,EAAAA,CAAsBd,CAAI,CAAA,CACxC,SAAUqD,CAAAA,CAAkB,GAAA,CAAIrD,EAAK,EAAE,CAAA,EAAG,SAC1C,SAAA,CAAWuC,EAAAA,CAAmBvC,CAAI,CACpC,CAAC,CACH,CAAA,CAEMsD,CAAAA,CAAoB,IAAI,GAAA,CAAA,CAAKF,CAAAA,EAAkB,OAAS,EAAC,EAAG,IAAKG,CAAAA,EAAS,CAACA,EAAK,EAAA,CAAIA,CAAI,CAAC,CAAC,CAAA,CAC1FC,EAAQL,CAAAA,CAAS,KAAA,CAAM,IAAKI,CAAAA,EAAS,CACzC,IAAME,CAAAA,CAAwB,CAC5B,GAAIF,CAAAA,CAAK,EAAA,CACT,OAAQ,CACN,MAAA,CAAQA,CAAAA,CAAK,MAAA,CACb,WAAYA,CAAAA,CAAK,YAAA,CAAe3O,EAAkB2O,CAAAA,CAAK,YAAY,EAAI,MACzE,CAAA,CACA,OAAQ,CACN,MAAA,CAAQA,EAAK,MAAA,CACb,UAAA,CAAYA,EAAK,YAAA,CAAe3O,CAAAA,CAAkB2O,EAAK,YAAY,CAAA,CAAI,MACzE,CAAA,CACA,IAAA,CAAMA,EAAK,IAAA,CACX,MAAA,CAAQA,EAAK,MAAA,GAAW,IAAA,CAAO,KAAO,MAAA,CACtC,IAAA,CAAMA,EAAK,IAAA,CAAO,CAAE,GAAGA,CAAAA,CAAK,IAAK,EAAI,MAAA,CACrC,QAAA,CAAUD,EAAkB,GAAA,CAAIC,CAAAA,CAAK,EAAE,CAAA,EAAG,QAC5C,CAAA,CAEMT,CAAAA,CAAWQ,EAAkB,GAAA,CAAIC,CAAAA,CAAK,EAAE,CAAA,CAC9C,OAAOT,EAAW,CAAE,GAAGA,EAAU,GAAGW,CAAS,EAAIA,CACnD,CAAC,EAED,OAAO,CACL,MAAAtC,CAAAA,CACA,KAAA,CAAAqC,EACA,EAAA,CAAItC,EAAAA,CAAeiC,EAAS,KAAA,CAAOC,CAAAA,EAAkB,EAAE,CAAA,CACvD,QAAA,CAAUA,GAAkB,QAC9B,CACF,CAEA,SAASM,EAAAA,CAA2BF,EAA2C,CAC7E,IAAMG,EAAS,IAAI,GAAA,CAEbC,CAAAA,CAAaC,CAAAA,EAA6B,CAC9C,GAAI,CAACA,EAAI,UAAA,CACP,OAEF,IAAM7H,CAAAA,CAAM,CAAA,EAAG6H,EAAI,MAAM,CAAA,CAAA,EAAIjP,EAAkBiP,CAAAA,CAAI,UAAU,CAAC,CAAA,CAAA,CAC9DF,CAAAA,CAAO,IAAI3H,CAAAA,CAAAA,CAAM2H,CAAAA,CAAO,IAAI3H,CAAG,CAAA,EAAK,GAAK,CAAC,EAC5C,EAEA,OAAAwH,CAAAA,CAAM,QAASD,CAAAA,EAAS,CACtBK,EAAUL,CAAAA,CAAK,MAAM,EACrBK,CAAAA,CAAUL,CAAAA,CAAK,MAAM,EACvB,CAAC,EAEMI,CACT,CAEA,SAASG,EAAAA,CAAmBjB,EAAqC,CAC/D,GAAIA,EAAS,IAAA,CACX,OAAOA,EAAS,IAAA,CAGlB,GAAIA,EAAS,IAAA,GAAS,OAAA,EAAWA,EAAS,MAAA,GAAW,OAAA,CAAS,CAC5D,IAAMkB,CAAAA,CAAY,OAAOlB,CAAAA,CAAS,SAAA,EAAa,EAAE,CAAA,CAAE,WAAA,GACnD,OAAIkB,CAAAA,CAAU,SAAS,KAAK,CAAA,EAAKA,EAAU,QAAA,CAAS,KAAK,EAAU,WAAA,CAC/DA,CAAAA,CAAU,SAAS,KAAK,CAAA,EAAKA,EAAU,QAAA,CAAS,KAAK,EAAU,WAAA,CAC5D,UACT,CAEA,OAAIlB,EAAS,MAAA,GAAW,OAAA,CAAgB,SACpCA,CAAAA,CAAS,MAAA,GAAW,WAAmB,UAAA,CAE7C,CAEA,SAASmB,EAAAA,CAAuBxB,CAAAA,CAA8ByB,EAAwCC,CAAAA,CAA8B,CAClI,OAAO1B,CAAAA,CAAU,GAAA,CAAKK,IAAc,CAClC,EAAA,CAAIA,EAAS,EAAA,CACb,KAAA,CAAOA,EAAS,KAAA,CAChB,IAAA,CAAMA,EAAS,IAAA,GAAS,MAAA,CAAS,OAASA,CAAAA,CAAS,IAAA,GAAS,QAAU,OAAA,CAAU,MAAA,CAChF,MAAOA,CAAAA,CAAS,KAAA,CAChB,WAAYoB,CAAAA,CAAkB,GAAA,CAAI,GAAGC,CAAM,CAAA,CAAA,EAAItP,CAAAA,CAAkBiO,CAAAA,CAAS,EAAE,CAAC,CAAA,CAAE,GAAK,CAAA,EAAK,CAAA,CACzF,KAAMiB,EAAAA,CAAmBjB,CAAQ,EACjC,SAAA,CAAWA,CAAAA,CAAS,UACpB,aAAA,CAAeA,CAAAA,CAAS,UACxB,aAAA,CACEA,CAAAA,CAAS,eAAiBA,CAAAA,CAAS,aAAA,CAAc,OAAS,CAAA,CACtD,CAAC,GAAGA,CAAAA,CAAS,aAAa,EAC1B,MAAA,CACN,QAAA,CAAUhD,EAAkBgD,CAAAA,CAAS,MAAM,CAC7C,CAAA,CAAE,CACJ,CAEA,SAASsB,EAAAA,CAAsB3B,EAA8ByB,CAAAA,CAAwCC,CAAAA,CAA6B,CAChI,IAAME,CAAAA,CAAU,IAAI,GAAA,CAEpB,OAAA5B,CAAAA,CAAU,OAAA,CAASK,GAAa,CAC9B,IAAM9G,EAAY8G,CAAAA,CAAS,KAAA,EAAS,QAC9BwB,CAAAA,CAAWD,CAAAA,CAAQ,IAAIrI,CAAS,CAAA,CAChC9G,EAAmB,CACvB,EAAA,CAAI4N,EAAS,EAAA,CACb,KAAA,CAAOA,EAAS,KAAA,CAChB,SAAA,CAAA,CAAYoB,EAAkB,GAAA,CAAI,CAAA,EAAGC,CAAM,CAAA,CAAA,EAAItP,CAAAA,CAAkBiO,EAAS,EAAE,CAAC,EAAE,CAAA,EAAK,CAAA,EAAK,EACzF,IAAA,CAAMiB,EAAAA,CAAmBjB,CAAQ,CAAA,CACjC,SAAA,CAAWA,EAAS,SAAA,CACpB,aAAA,CAAeA,CAAAA,CAAS,SAAA,CACxB,cACEA,CAAAA,CAAS,aAAA,EAAiBA,EAAS,aAAA,CAAc,MAAA,CAAS,EACtD,CAAC,GAAGA,EAAS,aAAa,CAAA,CAC1B,OACN,QAAA,CAAUhD,CAAAA,CAAkBgD,EAAS,MAAM,CAC7C,EAEA,GAAIwB,CAAAA,CAAU,CACZA,CAAAA,CAAS,IAAA,CAAKpP,CAAI,CAAA,CAClB,MACF,CAEAmP,CAAAA,CAAQ,GAAA,CAAIrI,EAAW,CAAC9G,CAAI,CAAC,EAC/B,CAAC,EAEM,CAAC,GAAGmP,EAAQ,OAAA,EAAS,EAAE,GAAA,CAAI,CAAC,CAACE,CAAAA,CAAMpI,CAAK,CAAA,IAAO,CACpD,KAAAoI,CAAAA,CACA,KAAA,CAAOpI,EAAM,GAAA,CAAKjH,CAAAA,GAAU,CAC1B,EAAA,CAAIA,CAAAA,CAAK,GACT,KAAA,CAAOA,CAAAA,CAAK,MACZ,SAAA,CAAWA,CAAAA,CAAK,WAAa,KAAA,CAC7B,IAAA,CAAMA,EAAK,IAAA,CACX,SAAA,CAAWA,EAAK,SAAA,CAChB,aAAA,CAAeA,EAAK,aAAA,CACpB,aAAA,CAAeA,EAAK,aAAA,CACpB,QAAA,CAAU4K,EAAkB5K,CAAAA,CAAK,QAAQ,CAC3C,CAAA,CAAE,CACJ,EAAE,CACJ,CAEA,SAASsP,EAAAA,CACPhI,CAAAA,CACAmE,CAAAA,CACA8D,CAAAA,CACqB,CACrB,GAAI,CAAC9D,GAAU,CAAC8D,CAAAA,CACd,OAAOjI,CAAAA,CAGT,IAAMkI,EAAW,CAAE,GAAGlI,CAAK,CAAA,CACvBmE,CAAAA,GACFjB,EAAoB,OAAA,CAASzD,CAAAA,EAAQ,CACnC,IAAMxE,CAAAA,CAAQkJ,EAAO1E,CAAG,CAAA,CACpBxE,IAAU,MAAA,GACXiN,CAAAA,CAAqCzI,CAAG,CAAA,CAAIxE,CAAAA,EAEjD,CAAC,CAAA,CAEGkJ,CAAAA,CAAO,QACTnB,CAAAA,CAAkB,OAAA,CAASvD,GAAQ,CACjC,IAAMxE,EAAQkJ,CAAAA,CAAO,MAAA,GAAS1E,CAAG,CAAA,CAC7BxE,CAAAA,GAAU,MAAA,GACXiN,CAAAA,CAAqCzI,CAAG,CAAA,CAAIxE,CAAAA,EAEjD,CAAC,CAAA,CAGCkJ,CAAAA,CAAO,YACTlB,CAAAA,CAAsB,OAAA,CAASxD,GAAQ,CACrC,IAAMxE,EAAQkJ,CAAAA,CAAO,UAAA,GAAa1E,CAAG,CAAA,CACjC,OAAOxE,GAAU,QAAA,GAClBiN,CAAAA,CAAqCzI,CAAG,CAAA,CAAIxE,CAAAA,EAEjD,CAAC,CAAA,CAAA,CAIL,IAAM8J,EAAekD,CAAAA,EAAS,IAAA,CAC9B,OAAIlD,CAAAA,GACFmD,CAAAA,CAAS,KAAOnD,CAAAA,CAAAA,CAEdkD,CAAAA,EAAS,gBACXC,CAAAA,CAAS,aAAA,CAAgB,CAAE,GAAGD,CAAAA,CAAQ,aAAc,CAAA,CAAA,CAG/CC,CACT,CAEA,SAASC,GAA8BnI,CAAAA,CAA2BwE,CAAAA,CAA6D,CAC7H,GAAI,CAACA,GAAQA,CAAAA,CAAK,MAAA,GAAW,EAC3B,OAAOxE,CAAAA,CAGT,IAAMkI,CAAAA,CAAW,CAAE,GAAGlI,CAAK,CAAA,CAC3B,OAAAwE,CAAAA,CAAK,OAAA,CAAS8C,GAAQ,CAChBA,CAAAA,CAAI,SAAW,QAAA,GACjBY,CAAAA,CAAS,SAAWZ,CAAAA,CAAI,EAAA,CACpBA,EAAI,IAAA,GACNY,CAAAA,CAAS,WAAaZ,CAAAA,CAAI,IAAA,CAAA,CAAA,CAG1BA,EAAI,MAAA,GAAW,WAAA,GACjBY,EAAS,WAAA,CAAcZ,CAAAA,CAAI,GACvBA,CAAAA,CAAI,IAAA,GACNY,EAAS,aAAA,CAAgBZ,CAAAA,CAAI,OAGnC,CAAC,CAAA,CAEMY,CACT,CAEA,SAASE,GACP3E,CAAAA,CACA4E,CAAAA,CACAC,EACoB,CACpB,GAAI,CAACD,CAAAA,CACH,OAGF,IAAME,CAAAA,CAAuBlQ,CAAAA,CAAkBgQ,CAAU,CAAA,CACzD,GAAI,CAAC5E,CAAAA,CACH,OAAO8E,EAGT,GAAI9E,CAAAA,CAAK,OAAS,WAAA,CAAa,CAC7B,IAAMK,CAAAA,CAAAA,CAAWL,CAAAA,CAAK,SAAW,EAAA,EAAI,WAAA,GAAc,OAAA,CAAQ,IAAA,CAAM,GAAG,CAAA,CAC9D6C,CAAAA,CAAW7C,EAAK,SAAA,EAAW,IAAA,CAAMf,CAAAA,EAAcA,CAAAA,CAAU,KAAO6F,CAAoB,CAAA,CACpFC,EAAkBlC,CAAAA,EAAU,IAAA,GAAS,SAAWA,CAAAA,EAAU,MAAA,GAAW,QAE3E,GAAA,CAAKxC,CAAAA,GAAY,UAAYA,CAAAA,GAAY,QAAA,GAAawE,IAAS,QAAA,EAAY,CAACE,EAC1E,OAAO,CAAA,EAAGD,CAAoB,CAAA,OAAA,CAAA,CAGhC,GAAIzE,IAAY,aAAA,CAAe,CAC7B,IAAMlJ,CAAAA,CAAO0L,CAAAA,EAAU,OAAS,MAAA,CAAS,MAAA,CAAS,QAClD,GAAIgC,CAAAA,GAAS,UAAY1N,CAAAA,GAAS,OAAA,CAChC,OAAO,CAAA,EAAG2N,CAAoB,UAEhC,GAAID,CAAAA,GAAS,QAAA,EAAY1N,CAAAA,GAAS,OAChC,OAAO,CAAA,EAAG2N,CAAoB,CAAA,OAAA,CAElC,CAEA,OAAOA,CACT,CAEA,OAAI9E,CAAAA,CAAK,IAAA,GAAS,WACT8E,CAIX,CAEO,SAASE,EAAAA,CAA8BC,CAAAA,CAAiD,CAC7F,IAAMhB,CAAAA,CAAoBP,GAA2BuB,CAAAA,CAAS,KAAK,EAC7DC,CAAAA,CAAcD,CAAAA,CAAS,IAAI,SAAA,EAAa,GAExC9D,CAAAA,CAAuB8D,CAAAA,CAAS,MAAM,GAAA,CAAKjF,CAAAA,EAAS,CACxD,IAAMmF,CAAAA,CAAahF,GAAiBH,CAAI,CAAA,CACpCzD,EAA4B,CAC9B,KAAA,CAAOyD,CAAAA,CAAK,KAAA,CACZ,GAAIA,CAAAA,CAAK,MAAA,EAAU,EACrB,CAAA,CAKA,GAHAzD,CAAAA,CAAOgI,EAAAA,CAAwBhI,EAAMyD,CAAAA,CAAK,MAAA,CAAQkF,EAAYlF,CAAAA,CAAK,EAAE,CAAC,CAAA,CACtEzD,CAAAA,CAAOmI,GAA8BnI,CAAAA,CAAMyD,CAAAA,CAAK,YAAY,CAAA,CAExDmF,CAAAA,GAAe,UAAYA,CAAAA,GAAe,QAAA,EAAYA,IAAe,QAAA,EAAYA,CAAAA,GAAe,SAClG5I,CAAAA,CAAK,UAAA,CAAayD,EAAK,OAAA,GAAYmF,CAAAA,GAAe,SAAW,QAAA,CAAWA,CAAAA,CAAAA,CACpEnF,EAAK,SAAA,EAAaA,CAAAA,CAAK,UAAU,MAAA,CAAS,CAAA,GAC5CzD,CAAAA,CAAK,KAAA,CAAQyH,GAAuBhE,CAAAA,CAAK,SAAA,CAAWiE,EAAmBjE,CAAAA,CAAK,EAAE,WAEvEmF,CAAAA,GAAe,cAAA,CAAgB,CACxC,IAAMC,CAAAA,CAAAA,CAAiBpF,EAAK,SAAA,EAAa,IAAI,MAAA,CAAQ6C,CAAAA,EAAaA,EAAS,IAAA,GAAS,OAAO,EACvFuC,CAAAA,CAAc,MAAA,CAAS,IACzB7I,CAAAA,CAAK,aAAA,CAAgB4H,GAAsBiB,CAAAA,CAAenB,CAAAA,CAAmBjE,EAAK,EAAE,CAAA,CACpFzD,EAAK,UAAA,CAAa6I,CAAAA,CAAc,QAEpC,CAEA,OAAIpF,EAAK,SAAA,CAAU,IAAA,GACbA,EAAK,SAAA,CAAU,IAAA,CAAK,UAAA,GACtBzD,CAAAA,CAAK,WAAayD,CAAAA,CAAK,SAAA,CAAU,KAAK,UAAA,CAAA,CAEpC,OAAOA,EAAK,SAAA,CAAU,IAAA,CAAK,aAAgB,QAAA,GAC7CzD,CAAAA,CAAK,YAAcyD,CAAAA,CAAK,SAAA,CAAU,KAAK,WAAA,CAAA,CAErC,OAAOA,EAAK,SAAA,CAAU,IAAA,CAAK,eAAkB,QAAA,GAC/CzD,CAAAA,CAAK,cAAgByD,CAAAA,CAAK,SAAA,CAAU,KAAK,aAAA,CAAA,CAAA,CAIzCmF,CAAAA,GAAe,QAAUnF,CAAAA,CAAK,SAAA,CAAU,OAC1CzD,CAAAA,CAAK,aAAA,CAAgB,KACjB,OAAOyD,CAAAA,CAAK,UAAU,IAAA,CAAK,SAAA,EAAc,WAC3CzD,CAAAA,CAAK,SAAA,CAAYyD,CAAAA,CAAK,SAAA,CAAU,KAAK,SAAA,CAAA,CAEnCA,CAAAA,CAAK,UAAU,IAAA,CAAK,SAAA,GACtBzD,EAAK,SAAA,CAAYyD,CAAAA,CAAK,UAAU,IAAA,CAAK,SAAA,CAAA,CAAA,CAIrCA,EAAK,SAAA,CAAU,SAAA,GACb,OAAOA,CAAAA,CAAK,SAAA,CAAU,UAAU,YAAA,EAAiB,QAAA,GACnDzD,EAAK,YAAA,CAAeyD,CAAAA,CAAK,UAAU,SAAA,CAAU,YAAA,CAAA,CAE3C,OAAOA,CAAAA,CAAK,SAAA,CAAU,UAAU,cAAA,EAAmB,QAAA,GACrDzD,EAAK,cAAA,CAAiByD,CAAAA,CAAK,UAAU,SAAA,CAAU,cAAA,CAAA,CAAA,CAI5C,CACL,EAAA,CAAIA,CAAAA,CAAK,GACT,IAAA,CAAMmF,CAAAA,CACN,QAAA,CAAUvF,EAAAA,CAAcI,EAAK,SAAA,CAAU,QAAQ,EAC/C,GAAIA,CAAAA,CAAK,UAAU,QAAA,CAAW,CAAE,SAAUA,CAAAA,CAAK,SAAA,CAAU,QAAS,CAAA,CAAI,GACtE,GAAIA,CAAAA,CAAK,UAAU,MAAA,CAAS,CAAE,OAAQA,CAAAA,CAAK,SAAA,CAAU,MAAO,CAAA,CAAI,GAChE,IAAA,CAAAzD,CACF,CACF,CAAC,CAAA,CAEK8I,EAAY,IAAI,GAAA,CAAIJ,EAAS,KAAA,CAAM,GAAA,CAAKjF,GAAS,CAACA,CAAAA,CAAK,GAAIA,CAAI,CAAC,CAAC,CAAA,CACjEwD,CAAAA,CAAuByB,EAAS,KAAA,CAAM,GAAA,CAAK1B,IAAU,CACzD,EAAA,CAAIA,EAAK,EAAA,CACT,MAAA,CAAQA,EAAK,MAAA,CAAO,MAAA,CACpB,OAAQA,CAAAA,CAAK,MAAA,CAAO,OACpB,GAAIA,CAAAA,CAAK,OAAO,UAAA,CAAa,CAAE,aAAcoB,EAAAA,CAAsBU,CAAAA,CAAU,IAAI9B,CAAAA,CAAK,MAAA,CAAO,MAAM,CAAA,CAAGA,CAAAA,CAAK,OAAO,UAAA,CAAY,QAAQ,CAAE,CAAA,CAAI,GAC5I,GAAIA,CAAAA,CAAK,OAAO,UAAA,CAAa,CAAE,aAAcoB,EAAAA,CAAsBU,CAAAA,CAAU,GAAA,CAAI9B,CAAAA,CAAK,OAAO,MAAM,CAAA,CAAGA,EAAK,MAAA,CAAO,UAAA,CAAY,QAAQ,CAAE,CAAA,CAAI,EAAC,CAC7I,GAAIA,EAAK,IAAA,CAAO,CAAE,KAAMA,CAAAA,CAAK,IAAK,EAAI,EAAC,CACvC,GAAIA,CAAAA,CAAK,MAAA,GAAW,KAAO,CAAE,MAAA,CAAQ,IAAK,CAAA,CAAI,GAC9C,GAAIA,CAAAA,CAAK,KAAO,CAAE,IAAA,CAAM,CAAE,GAAGA,CAAAA,CAAK,IAAK,CAAE,CAAA,CAAI,EAC/C,CAAA,CAAE,CAAA,CAEF,OAAO,CAAE,KAAA,CAAApC,CAAAA,CAAO,MAAAqC,CAAM,CACxB,CCx2BO,SAAS8B,EAAAA,CACdC,EACA/B,CAAAA,CACAlO,CAAAA,CAAkC,EAAC,CACa,CAChD,IAAMD,CAAAA,CAAYC,CAAAA,CAAQ,WAAa,YAAA,CACjCkQ,CAAAA,CAAUlQ,EAAQ,OAAA,EAAW,GAAA,CAC7BmQ,EAASnQ,CAAAA,CAAQ,MAAA,EAAU,CAAE,CAAA,CAAG,CAAA,CAAG,EAAG,CAAE,CAAA,CAExC6L,EAAQoE,CAAAA,CAAM,GAAA,CAAI,CAACG,CAAAA,CAAM3O,CAAAA,IAAW,CACxC,EAAA,CAAI2O,CAAAA,CAAK,GACT,IAAA,CAAMA,CAAAA,CAAK,IAAA,EAAQ,OAAA,CACnB,SACErQ,CAAAA,GAAc,UAAA,CACV,CAAE,CAAA,CAAGoQ,CAAAA,CAAO,EAAG,CAAA,CAAGA,CAAAA,CAAO,EAAI1O,CAAAA,CAAQyO,CAAQ,EAC7C,CAAE,CAAA,CAAGC,EAAO,CAAA,CAAI1O,CAAAA,CAAQyO,EAAS,CAAA,CAAGC,CAAAA,CAAO,CAAE,CAAA,CACnD,IAAA,CAAMC,EAAK,IAAA,EAAQ,CAAE,MAAOA,CAAAA,CAAK,EAAG,CACtC,CAAA,CAAE,CAAA,CAEIC,EAA6BnC,CAAAA,CAAM,GAAA,CAAKD,IAAU,CACtD,EAAA,CAAIA,EAAK,EAAA,CACT,MAAA,CAAQA,EAAK,MAAA,CACb,MAAA,CAAQA,CAAAA,CAAK,MAAA,CACb,KAAMA,CAAAA,CAAK,IAAA,EAAQ,QACnB,IAAA,CAAMA,CAAAA,CAAK,IACb,CAAA,CAAE,CAAA,CAEF,OAAO,CAAE,KAAA,CAAApC,EAAO,KAAA,CAAOwE,CAAY,CACrC,CCvCO,IAAMC,GAAkB,CAACC,CAAAA,CAAqBjH,IACnDA,CAAAA,CAAShK,CAAAA,CAAkBgK,CAAM,CAAA,CAAI,GAwBhC,SAASkH,CAAAA,CACd9F,CAAAA,CACApB,EACoB,CACpB,GAAI,CAACoB,CAAAA,EAAQ,CAACpB,EACZ,OAAO,OAAA,CAMT,GAAM,CAAE,KAAA,CAAAtH,CAAM,CAAA,CAAIlB,CAAAA,CAAcwI,CAAM,CAAA,CACtC,OAAQoB,CAAAA,CAAK,IAAA,EACX,KAAK,MAAA,CACH,OAAI1I,CAAAA,CAAM,IAAA,GAAS,QAAgB,YAAA,CAC/BA,CAAAA,CAAM,OAAS,MAAA,CAAe,WAAA,CAC3B,QACT,KAAK,OAAA,CACH,OAAIA,CAAAA,CAAM,IAAA,GAAS,QAAgB,YAAA,CAC/BA,CAAAA,CAAM,OAAS,cAAA,CAAuB,cAAA,CACnC,QACT,KAAK,kBAAA,CACH,OAAIA,CAAAA,CAAM,IAAA,GAAS,OAAe,YAAA,CAC9BA,CAAAA,CAAM,OAAS,cAAA,CAAuB,cAAA,CACnC,QACT,KAAK,QAAA,CACH,OAAOA,CAAAA,CAAM,IAAA,GAAS,aAAA,CAAgB,aAAA,CAAgB,QACxD,KAAK,SAAA,CACH,OAAOA,CAAAA,CAAM,IAAA,GAAS,YAAc,cAAA,CAAiB,OAAA,CACvD,KAAK,aAAA,CACH,OAAIA,EAAM,IAAA,GAAS,UAAA,CACVA,EAAM,EAAA,GAAO,OAAA,CAAU,iBAAmB,iBAAA,CAE5C,OAAA,CACT,QACE,OAAO,OACX,CACF,CAUA,IAAMyO,GAA2C,IAAI,GAAA,CAAI,CAEvD,wBAAA,CACA,wBAAA,CAEA,2BACA,0BAAA,CAEA,4BAAA,CAEA,8BACA,6BAAA,CAEA,2BAAA,CAEA,8BACA,6BAAA,CACA,8BAAA,CACA,+BAEA,4BAAA,CACA,4BAAA,CAEA,2BAAA,CAEA,sBAAA,CACA,sBACF,CAAC,CAAA,CAED,SAASC,EAAAA,CAAkBxO,CAAAA,CAAoC,CAC7D,OAAOA,CAAAA,GAAU,aAAeA,CAAAA,GAAU,YAC5C,CAUO,SAASyO,EAAAA,CACdC,EACAC,CAAAA,CACAC,CAAAA,CACAC,EACsB,CACtB,GAAIH,GAAY,IAAA,GAAS,QAAA,EAAYE,GAAY,IAAA,GAAS,QAAA,CAAU,CAClE,IAAME,CAAAA,CAAiBJ,EAAW,IAAA,EAAM,IAAA,GAAS,SAC3CK,CAAAA,CAAiBH,CAAAA,CAAW,MAAM,IAAA,GAAS,QAAA,CACjD,GAAIE,CAAAA,GAAmBC,CAAAA,CACrB,OAAO,CAAE,KAAA,CAAO,MAAO,MAAA,CAAQ,yDAA0D,CAE7F,CAEA,IAAMC,EAAcV,CAAAA,CAAsBI,CAAAA,CAAYC,CAAY,CAAA,CAC5DM,CAAAA,CAAcX,EAAsBM,CAAAA,CAAYC,CAAY,EAElE,OAAIN,EAAAA,CAAoB,IAAI,CAAA,EAAGS,CAAW,IAAIC,CAAW,CAAA,CAAE,EAClD,CAAE,KAAA,CAAO,IAAK,CAAA,CAGnBT,EAAAA,CAAkBQ,CAAW,CAAA,EAAKR,EAAAA,CAAkBS,CAAW,CAAA,CAC1D,CAAE,MAAO,KAAA,CAAO,MAAA,CAAQ,4DAA6D,CAAA,CAGvF,CAAE,MAAO,KAAA,CAAO,MAAA,CAAQ,uCAAwC,CACzE","file":"model.js","sourcesContent":["import type { Port } from \"../types\";\n\nconst DIRECTIONAL_HANDLE_SUFFIX = /-(source|target)$/i;\nconst FACE_ALIAS_HANDLE_SUFFIX = /--(rear|front)$/i;\n\n/**\n * Normalize source/target directional suffixes used for bidirectional handles.\n * Example: `Rear Ports-1-target` -\\> `Rear Ports-1`.\n */\nexport function normalizeDirectionalHandleId(\n handleId: string | null | undefined\n): string {\n if (!handleId) return \"\";\n return handleId.replace(DIRECTIONAL_HANDLE_SUFFIX, \"\");\n}\n\n/**\n * Normalize rendered handle ids back to their canonical endpoint id.\n * This removes the directional suffix and the split-face alias, in the order\n * the renderer composes them: `<base>[--rear|--front][-source|-target]`.\n * (Previously only `--rear` was stripped; `--front` ghost aliases now\n * normalize too.)\n */\nexport function normalizeHandleId(handleId: string | null | undefined): string {\n return normalizeDirectionalHandleId(handleId).replace(\n FACE_ALIAS_HANDLE_SUFFIX,\n \"\"\n );\n}\n\n/**\n * Returns true when a port type is considered power-related.\n */\nexport function isPowerPortType(portType: string | undefined): boolean {\n return (\n typeof portType === \"string\" && portType.toLowerCase().includes(\"power\")\n );\n}\n\n/**\n * Build a stable handle id for a port.\n * Power ports use the label directly to preserve legacy ids.\n */\nexport function buildPortHandleId(\n portBlockName: string,\n port: Pick<Port, \"label\" | \"id\"> & { type?: string }\n): string {\n if (typeof port.id === \"string\" && port.id.length > 0) {\n return port.id;\n }\n return isPowerPortType(port.type)\n ? port.label\n : `${portBlockName}-${port.label}`;\n}\n\n/**\n * Heuristic for whether a handle id represents a power endpoint.\n */\nexport function looksLikePowerHandleId(handleId: string): boolean {\n const normalized = normalizeDirectionalHandleId(handleId).toLowerCase();\n return normalized.startsWith(\"pwr\") || normalized.includes(\"power\");\n}\n\n/**\n * Options for {@link applyDirectionalPortHandleId}.\n */\nexport interface ApplyDirectionalPortHandleIdOptions {\n /**\n * The port's `type` (e.g. `rj45`, `power_ac`). When provided, power-ness is\n * decided from it via {@link isPowerPortType} — the same rule the renderer\n * uses to decide whether a port mounts directional handles at all. Always\n * pass this when the port is available.\n */\n portType?: string;\n}\n\n/**\n * Apply a directional (`source`/`target`) suffix to a rendered handle id — the\n * inverse of {@link normalizeDirectionalHandleId}. Any existing directional\n * suffix is replaced first, and passing no direction is a no-op.\n * Example: `Rear Ports-1` + `target` -\\> `Rear Ports-1-target`.\n *\n * Power endpoints are matched without a direction, so power handles are\n * returned unchanged. Whether a handle is a power handle is decided from\n * `options.portType` when given (exact — the renderer's rule). Without it the\n * decision falls back to {@link looksLikePowerHandleId}, a string heuristic\n * that cannot recognize power ports whose ids lack `power`/`pwr` (e.g. a port\n * labeled `PSU-A`) and misclassifies non-power ports whose group name contains\n * the word (e.g. `Power Room Uplinks-1`). Pass `portType` whenever you have\n * the port.\n */\nexport function applyDirectionalPortHandleId(\n handleId: string,\n direction?: \"source\" | \"target\" | null,\n options?: ApplyDirectionalPortHandleIdOptions\n): string {\n if (!direction) return handleId;\n const hasPortType =\n typeof options?.portType === \"string\" && options.portType.length > 0;\n const isPower = hasPortType\n ? isPowerPortType(options.portType)\n : looksLikePowerHandleId(handleId);\n if (isPower) return handleId;\n return `${normalizeDirectionalHandleId(handleId)}-${direction}`;\n}\n\n// ---------------------------------------------------------------------------\n// Typed handle-id grammar\n//\n// Handle ids are the library's de-facto wire protocol: they are what React\n// Flow stores on edges, what documents persist, and what consumers match\n// against. This section is the single authority for that grammar. The full\n// composed form is:\n//\n// <base>[--rear|--front][-source|-target]\n//\n// where <base> is one of the shapes below. Everything here is *structural* —\n// it reads only the string. Shapes whose tokens can collide with port group\n// names (a group literally named `fiber`, a port label `left-3`) cannot be\n// told apart from the string alone; callers that have the node should combine\n// this with the node type (as `classifyFiberEndpoint` does).\n// ---------------------------------------------------------------------------\n\n/**\n * The structural shape of a handle id's base (suffixes already stripped).\n *\n * - `outlet` — vertical-PDU outlets: a bare number (`\"4\"`).\n * - `fiber` — `fiber-<n>` (tube/cable nodes) or `fiber-<tube>-<n>`\n * (FibreFlowMap rows).\n * - `tube` — `tube-<n>` (tube and multi-tube-cable nodes).\n * - `splice-side` — bare `left` / `right` (splice nodes).\n * - `side-port` — `left-<n>` / `right-<n>` (coupler ports).\n * - `split-io` — `input-<n>` / `output-<n>` (fibre-split nodes).\n * - `cable-button` — the literal `cable-button` connector on cable nodes.\n * - `port` — everything else: device-family ports built by\n * {@link buildPortHandleId} (`<group>-<label>`, a bare power label, or an\n * explicit `port.id`). The base is not split further because group names\n * and labels may themselves contain `-`.\n * @public\n */\nexport type HandleShape =\n | { kind: \"outlet\"; outlet: number }\n | { kind: \"fiber\"; fiber: number; tube?: number }\n | { kind: \"tube\"; tube: number }\n | { kind: \"splice-side\"; side: \"left\" | \"right\" }\n | { kind: \"side-port\"; side: \"left\" | \"right\"; index: number }\n | { kind: \"split-io\"; io: \"input\" | \"output\"; index: number }\n | { kind: \"cable-button\" }\n | { kind: \"port\" };\n\n/**\n * A structurally parsed handle id.\n * @public\n */\nexport interface ParsedHandleId {\n /** The id exactly as given. */\n raw: string;\n /** `raw` with the directional suffix and face alias stripped. */\n base: string;\n /** Directional suffix, when present (`X-source` / `X-target`). */\n direction?: \"source\" | \"target\";\n /**\n * Split-face alias, when present (`X--rear` / `X--front`) — added by the\n * renderer when both faces of a device are shown and a handle id would\n * otherwise collide with its opposite face.\n */\n faceAlias?: \"front\" | \"rear\";\n /** Structural classification of {@link ParsedHandleId.base}. */\n shape: HandleShape;\n}\n\nconst OUTLET_BASE = /^\\d+$/;\nconst FIBER_TUBE_BASE = /^fiber-(\\d+)-(\\d+)$/;\nconst FIBER_BASE = /^fiber-(\\d+)$/;\nconst TUBE_BASE = /^tube-(\\d+)$/;\nconst SIDE_PORT_BASE = /^(left|right)-(\\d+)$/;\nconst SPLIT_IO_BASE = /^(input|output)-(\\d+)$/;\n\nfunction classifyHandleBase(base: string): HandleShape {\n if (OUTLET_BASE.test(base)) {\n return { kind: \"outlet\", outlet: Number(base) };\n }\n const fiberTube = FIBER_TUBE_BASE.exec(base);\n if (fiberTube) {\n return { kind: \"fiber\", tube: Number(fiberTube[1]), fiber: Number(fiberTube[2]) };\n }\n const fiber = FIBER_BASE.exec(base);\n if (fiber) {\n return { kind: \"fiber\", fiber: Number(fiber[1]) };\n }\n const tube = TUBE_BASE.exec(base);\n if (tube) {\n return { kind: \"tube\", tube: Number(tube[1]) };\n }\n if (base === \"left\" || base === \"right\") {\n return { kind: \"splice-side\", side: base };\n }\n const sidePort = SIDE_PORT_BASE.exec(base);\n if (sidePort) {\n return {\n kind: \"side-port\",\n side: sidePort[1] as \"left\" | \"right\",\n index: Number(sidePort[2]),\n };\n }\n const splitIo = SPLIT_IO_BASE.exec(base);\n if (splitIo) {\n return {\n kind: \"split-io\",\n io: splitIo[1] as \"input\" | \"output\",\n index: Number(splitIo[2]),\n };\n }\n if (base === \"cable-button\") {\n return { kind: \"cable-button\" };\n }\n return { kind: \"port\" };\n}\n\n/**\n * Parse a rendered/stored handle id into its structural parts: base, optional\n * directional suffix, optional split-face alias, and the {@link HandleShape}\n * of the base. Total — never throws; unrecognized bases classify as `port`.\n * @public\n */\nexport function parseHandleId(handleId: string | null | undefined): ParsedHandleId {\n const raw = handleId ?? \"\";\n const directionMatch = DIRECTIONAL_HANDLE_SUFFIX.exec(raw);\n const afterDirection = directionMatch\n ? raw.slice(0, raw.length - directionMatch[0].length)\n : raw;\n const faceMatch = FACE_ALIAS_HANDLE_SUFFIX.exec(afterDirection);\n const base = faceMatch\n ? afterDirection.slice(0, afterDirection.length - faceMatch[0].length)\n : afterDirection;\n const parsed: ParsedHandleId = { raw, base, shape: classifyHandleBase(base) };\n if (directionMatch) {\n parsed.direction = directionMatch[1].toLowerCase() as \"source\" | \"target\";\n }\n if (faceMatch) {\n parsed.faceAlias = faceMatch[1].toLowerCase() as \"front\" | \"rear\";\n }\n return parsed;\n}\n\n/**\n * Build a fiber handle id: `fiber-<n>`, or `fiber-<tube>-<n>` when a tube\n * index is given (the FibreFlowMap row form).\n * @public\n */\nexport function buildFiberHandleId(fiber: number, tube?: number): string {\n return tube === undefined ? `fiber-${fiber}` : `fiber-${tube}-${fiber}`;\n}\n\n/**\n * Build a tube handle id: `tube-<n>`.\n * @public\n */\nexport function buildTubeHandleId(tube: number): string {\n return `tube-${tube}`;\n}\n\n/**\n * Build a splice handle id: the bare `left` / `right` sides of a splice node.\n * @public\n */\nexport function buildSpliceHandleId(side: \"left\" | \"right\"): string {\n return side;\n}\n\n/**\n * Build a coupler port handle id: `left-<n>` / `right-<n>` (1-based).\n * @public\n */\nexport function buildCouplerHandleId(side: \"left\" | \"right\", index: number): string {\n return `${side}-${index}`;\n}\n\n/**\n * Build a fibre-split io handle id: `input-<n>` / `output-<n>`.\n * @public\n */\nexport function buildSplitIoHandleId(io: \"input\" | \"output\", index: number): string {\n return `${io}-${index}`;\n}\n\n/**\n * Append a split-face alias to a handle id: `X` + `rear` -\\> `X--rear`.\n * Any existing alias is replaced. The renderer applies this when a hidden or\n * ghost face would mount a handle whose id collides with the visible face.\n * @public\n */\nexport function applyFaceAliasHandleId(\n handleId: string,\n face: \"front\" | \"rear\"\n): string {\n return `${handleId.replace(FACE_ALIAS_HANDLE_SUFFIX, \"\")}--${face}`;\n}\n\n/**\n * Normalize a handle id for tolerant matching by stripping `+` characters.\n * Port labels like `SFP+-1` historically produced handles that some producers\n * emitted `+`-stripped (`SFP-1`), so comparisons between independently built\n * handles must be `+`-insensitive. Apply to BOTH sides of a comparison — this\n * is a matching normalization, not part of the build grammar:\n * {@link buildPortHandleId} keeps `+` so built ids always equal rendered ids.\n * @public\n */\nexport function sanitizeHandleIdForMatching(handleId: string): string {\n return handleId.replace(/\\+/g, \"\");\n}\n\n/**\n * For pass-through nodes with two sides, return the handle id of the opposite\n * side: splice `left` \\<-\\> `right`, coupler `left-<n>` \\<-\\> `right-<n>`.\n * Returns `undefined` for every other shape. Directional suffixes and face\n * aliases are stripped (neither occurs on splice/coupler handles in practice).\n * @public\n */\nexport function getOppositeSideHandleId(handleId: string): string | undefined {\n const { shape } = parseHandleId(handleId);\n if (shape.kind === \"splice-side\") {\n return shape.side === \"left\" ? \"right\" : \"left\";\n }\n if (shape.kind === \"side-port\") {\n return buildCouplerHandleId(shape.side === \"left\" ? \"right\" : \"left\", shape.index);\n }\n return undefined;\n}\n","import type { PortPosition } from '../types';\n\nconst EDGE_OFFSET_LOW_PERCENT = 12;\nconst EDGE_OFFSET_CENTER_LOW_PERCENT = 34;\nconst EDGE_OFFSET_CENTER_PERCENT = 50;\nconst EDGE_OFFSET_CENTER_HIGH_PERCENT = 66;\nconst EDGE_OFFSET_HIGH_PERCENT = 88;\nconst FACE_POSITION_FRONT_PERCENT = 22;\nconst FACE_POSITION_REAR_PERCENT = 78;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst toNumber = (value: unknown): number | undefined => {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n};\n\nconst normalizeSemanticPositionTokens = (value: string): string[] =>\n value\n .trim()\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length > 0);\n\nconst canonicalizeSideToken = (token: string): NonNullable<PortPosition['side']> | undefined => {\n if (token === 'top' || token === 'up') {\n return 'top';\n }\n if (token === 'right') {\n return 'right';\n }\n if (token === 'bottom' || token === 'down') {\n return 'bottom';\n }\n if (token === 'left') {\n return 'left';\n }\n return undefined;\n};\n\nconst hasCenterToken = (tokens: string[]): boolean =>\n tokens.includes('center') || tokens.includes('centre') || tokens.includes('middle') || tokens.includes('mid');\n\nconst resolveAxisOffsetPercent = (\n primarySide: NonNullable<PortPosition['side']>,\n tokens: string[],\n): number | undefined => {\n const hasLeft = tokens.includes('left');\n const hasRight = tokens.includes('right');\n const hasTop = tokens.includes('top') || tokens.includes('up');\n const hasBottom = tokens.includes('bottom') || tokens.includes('down');\n const hasCenter = hasCenterToken(tokens);\n\n if (primarySide === 'top' || primarySide === 'bottom') {\n if (hasLeft && hasCenter && !hasRight) return EDGE_OFFSET_CENTER_LOW_PERCENT;\n if (hasRight && hasCenter && !hasLeft) return EDGE_OFFSET_CENTER_HIGH_PERCENT;\n if (hasLeft && !hasRight) return EDGE_OFFSET_LOW_PERCENT;\n if (hasRight && !hasLeft) return EDGE_OFFSET_HIGH_PERCENT;\n if (hasCenter) return EDGE_OFFSET_CENTER_PERCENT;\n return undefined;\n }\n\n if (hasTop && hasCenter && !hasBottom) return EDGE_OFFSET_CENTER_LOW_PERCENT;\n if (hasBottom && hasCenter && !hasTop) return EDGE_OFFSET_CENTER_HIGH_PERCENT;\n if (hasTop && !hasBottom) return EDGE_OFFSET_LOW_PERCENT;\n if (hasBottom && !hasTop) return EDGE_OFFSET_HIGH_PERCENT;\n if (hasCenter) return EDGE_OFFSET_CENTER_PERCENT;\n return undefined;\n};\n\nconst resolveSemanticEdgePortPosition = (tokens: string[]): PortPosition | undefined => {\n const hasFaceTokens = tokens.includes('front') || tokens.includes('rear') || tokens.includes('back');\n if (hasFaceTokens) {\n return undefined;\n }\n\n const primarySide = tokens\n .map(canonicalizeSideToken)\n .find((side): side is NonNullable<PortPosition['side']> => side !== undefined);\n\n if (!primarySide) {\n return undefined;\n }\n\n const offsetPercent = resolveAxisOffsetPercent(primarySide, tokens);\n if (offsetPercent === undefined) {\n return undefined;\n }\n\n return {\n side: primarySide,\n offsetPercent,\n };\n};\n\nconst resolveSemanticCompositePortPosition = (tokens: string[]): PortPosition | undefined => {\n if (tokens.length === 0) {\n return undefined;\n }\n\n const hasLeft = tokens.includes('left');\n const hasRight = tokens.includes('right');\n const hasCenter = hasCenterToken(tokens);\n const hasFront = tokens.includes('front') || tokens.includes('top') || tokens.includes('up');\n const hasRear = tokens.includes('rear') || tokens.includes('back') || tokens.includes('bottom') || tokens.includes('down');\n\n let xPercent: number | undefined;\n if (hasLeft && hasCenter && !hasRight) {\n xPercent = EDGE_OFFSET_CENTER_LOW_PERCENT;\n } else if (hasRight && hasCenter && !hasLeft) {\n xPercent = EDGE_OFFSET_CENTER_HIGH_PERCENT;\n } else if (hasLeft && !hasRight) {\n xPercent = EDGE_OFFSET_LOW_PERCENT;\n } else if (hasRight && !hasLeft) {\n xPercent = EDGE_OFFSET_HIGH_PERCENT;\n } else if (hasCenter) {\n xPercent = EDGE_OFFSET_CENTER_PERCENT;\n }\n\n let yPercent: number | undefined;\n if (hasFront && !hasRear) {\n yPercent = FACE_POSITION_FRONT_PERCENT;\n } else if (hasRear && !hasFront) {\n yPercent = FACE_POSITION_REAR_PERCENT;\n } else if (hasCenter) {\n yPercent = EDGE_OFFSET_CENTER_PERCENT;\n }\n\n if (xPercent === undefined || yPercent === undefined) {\n return undefined;\n }\n\n return { xPercent, yPercent };\n};\n\nexport const parseSemanticPortPosition = (value: string): PortPosition | undefined => {\n const tokens = normalizeSemanticPositionTokens(value);\n if (tokens.length === 0) {\n return undefined;\n }\n\n if (tokens.length === 1) {\n const side = canonicalizeSideToken(tokens[0]);\n return side ? { side } : undefined;\n }\n\n return resolveSemanticEdgePortPosition(tokens) ?? resolveSemanticCompositePortPosition(tokens);\n};\n\nexport const normalizePortPositionInput = (value: unknown): PortPosition | undefined => {\n if (typeof value === 'string') {\n return parseSemanticPortPosition(value);\n }\n\n if (!isRecord(value)) {\n return undefined;\n }\n\n const sideRaw = value.side ?? value.port_side ?? value.portSide;\n const semanticPosition = typeof sideRaw === 'string' ? parseSemanticPortPosition(sideRaw) : undefined;\n const simpleSide = typeof sideRaw === 'string' ? canonicalizeSideToken(sideRaw.trim().toLowerCase()) : undefined;\n\n const side = simpleSide ?? semanticPosition?.side;\n const offsetPercent = toNumber(\n value.offsetPercent ??\n value.offset_percent ??\n value.offsetPct ??\n value.offset_pct\n ) ?? semanticPosition?.offsetPercent;\n const offsetPx = toNumber(\n value.offsetPx ??\n value.offset_px ??\n value.offsetPixels ??\n value.offset_pixels\n ) ?? semanticPosition?.offsetPx;\n const xPercent = toNumber(\n value.xPercent ??\n value.x_percent ??\n value.xPct ??\n value.x_pct ??\n value.horizontalPercent ??\n value.horizontal_percent ??\n value.x\n ) ?? semanticPosition?.xPercent;\n const yPercent = toNumber(\n value.yPercent ??\n value.y_percent ??\n value.yPct ??\n value.y_pct ??\n value.verticalPercent ??\n value.vertical_percent ??\n value.y\n ) ?? semanticPosition?.yPercent;\n const widthPercent = toNumber(\n value.widthPercent ??\n value.width_percent ??\n value.widthPct ??\n value.width_pct ??\n value.wPercent ??\n value.w_percent\n );\n const heightPercent = toNumber(\n value.heightPercent ??\n value.height_percent ??\n value.heightPct ??\n value.height_pct ??\n value.hPercent ??\n value.h_percent\n );\n\n if (\n side === undefined &&\n offsetPercent === undefined &&\n offsetPx === undefined &&\n xPercent === undefined &&\n yPercent === undefined &&\n widthPercent === undefined &&\n heightPercent === undefined\n ) {\n return undefined;\n }\n\n return {\n side,\n offsetPercent,\n offsetPx,\n xPercent,\n yPercent,\n widthPercent,\n heightPercent,\n };\n};\n","import type {\n DevicePort,\n DeviceFace,\n NetworkNode,\n Port,\n PortBlock,\n PortPosition,\n} from '../types';\nimport { isPowerPortType } from './handle-ids';\nimport { normalizePortPositionInput } from './port-position';\n\ntype IndexedPort = {\n index: number;\n port: DevicePort;\n};\n\ntype GroupedPortBucket = {\n face: DeviceFace;\n groupName: string;\n entries: IndexedPort[];\n};\n\nconst DEFAULT_FRONT_GROUP_NAME = 'Network Ports';\nconst DEFAULT_REAR_GROUP_NAME = 'Rear Network Ports';\nconst DEFAULT_POWER_GROUP_NAME = 'Power Ports';\nconst DISTRIBUTED_OFFSET_MIN_PERCENT = 8;\nconst DISTRIBUTED_OFFSET_MAX_PERCENT = 92;\n\nfunction clampPercent(value: number): number {\n return Math.max(0, Math.min(100, value));\n}\n\nfunction normalizePortAnchor(value: PortPosition['side'] | undefined): PortPosition['side'] | undefined {\n if (value === 'top' || value === 'right' || value === 'bottom' || value === 'left') {\n return value;\n }\n return undefined;\n}\n\nfunction getNormalizedPortPosition(position: unknown): PortPosition | undefined {\n return normalizePortPositionInput(position);\n}\n\nfunction hasExplicitAbsoluteCoordinates(port: DevicePort): boolean {\n const position = getNormalizedPortPosition(port.position);\n return (\n typeof position?.xPercent === 'number' &&\n typeof position?.yPercent === 'number'\n );\n}\n\nfunction hasExplicitOffset(port: DevicePort): boolean {\n const position = getNormalizedPortPosition(port.position);\n return (\n typeof position?.offsetPercent === 'number' ||\n typeof position?.offsetPx === 'number'\n );\n}\n\nfunction resolveRenderPortFace(port: DevicePort): DeviceFace {\n if (port.face === 'rear' || port.face === 'back') {\n return 'rear';\n }\n if (port.face === 'front') {\n return 'front';\n }\n return isPowerPortType(port.type) ? 'rear' : 'front';\n}\n\nfunction resolveRenderPortGroupName(port: DevicePort, face: DeviceFace): string {\n if (typeof port.group === 'string' && port.group.trim().length > 0) {\n return port.group.trim();\n }\n if (isPowerPortType(port.type)) {\n return DEFAULT_POWER_GROUP_NAME;\n }\n return face === 'rear' ? DEFAULT_REAR_GROUP_NAME : DEFAULT_FRONT_GROUP_NAME;\n}\n\nfunction getEntrySortValue(entry: IndexedPort): number {\n return typeof entry.port.order === 'number' && Number.isFinite(entry.port.order)\n ? entry.port.order\n : entry.index;\n}\n\nfunction toDistributedOffsetPercent(index: number, count: number): number {\n if (count <= 1) {\n return 50;\n }\n return (\n DISTRIBUTED_OFFSET_MIN_PERCENT +\n (index * (DISTRIBUTED_OFFSET_MAX_PERCENT - DISTRIBUTED_OFFSET_MIN_PERCENT)) / (count - 1)\n );\n}\n\nfunction buildDistributedOffsetMap(entries: IndexedPort[]): Map<number, number> {\n const distributedEntries = entries.filter((entry) => {\n const anchor = normalizePortAnchor(getNormalizedPortPosition(entry.port.position)?.side);\n return !!anchor && !hasExplicitAbsoluteCoordinates(entry.port) && !hasExplicitOffset(entry.port);\n });\n const offsetPercentByIndex = new Map<number, number>();\n const entriesByAnchor = new Map<NonNullable<PortPosition['side']>, IndexedPort[]>();\n\n distributedEntries.forEach((entry) => {\n const anchor = normalizePortAnchor(getNormalizedPortPosition(entry.port.position)?.side);\n if (!anchor) {\n return;\n }\n const group = entriesByAnchor.get(anchor);\n if (group) {\n group.push(entry);\n return;\n }\n entriesByAnchor.set(anchor, [entry]);\n });\n\n entriesByAnchor.forEach((anchorEntries) => {\n const sortedEntries = [...anchorEntries].sort((left, right) => {\n const orderDifference = getEntrySortValue(left) - getEntrySortValue(right);\n if (orderDifference !== 0) {\n return orderDifference;\n }\n return left.index - right.index;\n });\n sortedEntries.forEach((entry, index) => {\n offsetPercentByIndex.set(entry.index, toDistributedOffsetPercent(index, sortedEntries.length));\n });\n });\n\n return offsetPercentByIndex;\n}\n\nfunction toPortPosition(\n port: DevicePort,\n distributedOffsetPercent?: number,\n): PortPosition | undefined {\n const normalizedPosition = getNormalizedPortPosition(port.position);\n const anchor = normalizePortAnchor(normalizedPosition?.side);\n if (hasExplicitAbsoluteCoordinates(port)) {\n const position: PortPosition = {\n xPercent: clampPercent(normalizedPosition?.xPercent as number),\n yPercent: clampPercent(normalizedPosition?.yPercent as number),\n };\n if (typeof normalizedPosition?.widthPercent === 'number' && Number.isFinite(normalizedPosition.widthPercent)) {\n position.widthPercent = clampPercent(normalizedPosition.widthPercent);\n }\n if (typeof normalizedPosition?.heightPercent === 'number' && Number.isFinite(normalizedPosition.heightPercent)) {\n position.heightPercent = clampPercent(normalizedPosition.heightPercent);\n }\n if (anchor) {\n position.side = anchor;\n }\n return position;\n }\n\n if (!anchor) {\n return undefined;\n }\n\n const position: PortPosition = { side: anchor };\n if (typeof normalizedPosition?.offsetPx === 'number' && Number.isFinite(normalizedPosition.offsetPx)) {\n position.offsetPx = normalizedPosition.offsetPx;\n return position;\n }\n\n const explicitOffsetPercent =\n typeof normalizedPosition?.offsetPercent === 'number' && Number.isFinite(normalizedPosition.offsetPercent)\n ? clampPercent(normalizedPosition.offsetPercent)\n : undefined;\n const resolvedOffsetPercent =\n explicitOffsetPercent ?? (typeof distributedOffsetPercent === 'number'\n ? clampPercent(distributedOffsetPercent)\n : undefined);\n\n if (typeof resolvedOffsetPercent === 'number') {\n position.offsetPercent = resolvedOffsetPercent;\n }\n\n return position;\n}\n\nfunction toPort(entry: IndexedPort, distributedOffsetPercent?: number): Port {\n const position = toPortPosition(entry.port, distributedOffsetPercent);\n return {\n id: entry.port.id,\n label: entry.port.label,\n sourceTypeLabel: entry.port.sourceTypeLabel,\n connected: entry.port.connected ?? false,\n type: entry.port.type,\n cableType: entry.port.cableType,\n connectorType: entry.port.connectorType,\n compatibility: Array.isArray(entry.port.compatibility) ? [...entry.port.compatibility] : undefined,\n ...(position ? { position } : {}),\n };\n}\n\nfunction normalizePortBlockCollection(portBlocks: PortBlock[]): PortBlock[] {\n return portBlocks.map((block) => ({\n ...block,\n ports: block.ports.map((port) => {\n const position = getNormalizedPortPosition(port.position);\n return {\n ...port,\n ...(position ? { position } : { position: undefined }),\n };\n }),\n }));\n}\n\nfunction isGroupedPortBlockArray(value: unknown): value is PortBlock[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n typeof (entry as PortBlock).name === 'string' &&\n Array.isArray((entry as PortBlock).ports),\n )\n );\n}\n\nfunction isFlatDevicePortArray(value: unknown): value is DevicePort[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n typeof (entry as DevicePort).label === 'string' &&\n !(\n typeof (entry as { name?: unknown }).name === 'string' &&\n Array.isArray((entry as { ports?: unknown }).ports)\n ),\n )\n );\n}\n\n/**\n * Normalize flat `DevicePort[]` input into explicit front/rear grouped blocks.\n * This is the bridge between the recommended simple port schema and the\n * lower-level grouped renderer input.\n * @public\n */\nexport function buildPortBlocksFromPorts(\n devicePorts: DevicePort[],\n): { ports: PortBlock[]; rearPorts: PortBlock[] } {\n const bucketsByKey = new Map<string, GroupedPortBucket>();\n\n devicePorts.forEach((devicePort, index) => {\n if (typeof devicePort.label !== 'string' || devicePort.label.trim().length === 0) {\n return;\n }\n\n const face = resolveRenderPortFace(devicePort);\n const groupName = resolveRenderPortGroupName(devicePort, face);\n const key = `${face}:${groupName}`;\n const existingBucket = bucketsByKey.get(key);\n if (existingBucket) {\n existingBucket.entries.push({\n index,\n port: {\n ...devicePort,\n label: devicePort.label.trim(),\n },\n });\n return;\n }\n\n bucketsByKey.set(key, {\n face,\n groupName,\n entries: [\n {\n index,\n port: {\n ...devicePort,\n label: devicePort.label.trim(),\n },\n },\n ],\n });\n });\n\n const ports: PortBlock[] = [];\n const rearPorts: PortBlock[] = [];\n\n bucketsByKey.forEach((bucket) => {\n const sortedEntries = [...bucket.entries].sort((left, right) => {\n const orderDifference = getEntrySortValue(left) - getEntrySortValue(right);\n if (orderDifference !== 0) {\n return orderDifference;\n }\n return left.index - right.index;\n });\n const distributedOffsetByIndex = buildDistributedOffsetMap(sortedEntries);\n const block: PortBlock = {\n name: bucket.groupName,\n ports: sortedEntries.map((entry) =>\n toPort(entry, distributedOffsetByIndex.get(entry.index)),\n ),\n };\n if (bucket.face === 'rear') {\n rearPorts.push(block);\n return;\n }\n ports.push(block);\n });\n\n return { ports, rearPorts };\n}\n\n/**\n * Node-data subset accepted by {@link getResolvedPortBlocks}: a flat\n * `DevicePort[]` on `ports`, grouped `PortBlock[]` on\n * `ports`/`portGroups`/`rearPortGroups`, or a `rearPorts` block fallback.\n * @public\n */\nexport type ResolvedPortBlockInput = Pick<\n NetworkNode['data'],\n 'ports' | 'portGroups' | 'rearPortGroups'\n> & {\n rearPorts?: PortBlock[];\n};\n\n/**\n * Resolve the grouped front/rear port blocks from any supported input form:\n * flat `ports: DevicePort[]`, grouped `PortBlock[]`, or explicit\n * `portGroups` / `rearPortGroups`.\n * @public\n */\nexport function getResolvedPortBlocks(\n data: ResolvedPortBlockInput | undefined,\n): { ports: PortBlock[]; rearPorts: PortBlock[] } {\n if (isGroupedPortBlockArray(data?.ports)) {\n return {\n ports: normalizePortBlockCollection(data.ports),\n rearPorts: normalizePortBlockCollection(\n Array.isArray(data?.rearPortGroups)\n ? data.rearPortGroups\n : isGroupedPortBlockArray(data?.rearPorts)\n ? data.rearPorts\n : [],\n ),\n };\n }\n\n if (isFlatDevicePortArray(data?.ports)) {\n return buildPortBlocksFromPorts(data.ports);\n }\n\n return {\n ports: normalizePortBlockCollection(Array.isArray(data?.portGroups) ? data.portGroups : []),\n rearPorts: normalizePortBlockCollection(\n Array.isArray(data?.rearPortGroups)\n ? data.rearPortGroups\n : isGroupedPortBlockArray(data?.rearPorts)\n ? data.rearPorts\n : [],\n ),\n };\n}\n\n/**\n * Resolve the front-facing grouped port blocks for a node/device data object.\n * @public\n */\nexport function getFrontPortBlocks(\n data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): PortBlock[] {\n return getResolvedPortBlocks(data).ports;\n}\n\n/**\n * Resolve the rear-facing grouped port blocks for a node/device data object.\n * @public\n */\nexport function getRearPortBlocks(\n data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): PortBlock[] {\n return getResolvedPortBlocks(data).rearPorts;\n}\n\nexport function countPortBlocks(portBlocks: PortBlock[] | undefined): number {\n if (!Array.isArray(portBlocks)) {\n return 0;\n }\n\n return portBlocks.reduce((total, block) => {\n return total + (Array.isArray(block.ports) ? block.ports.length : 0);\n }, 0);\n}\n\n/**\n * Determine whether a node/device data object resolves to any front ports.\n * @public\n */\nexport function hasFrontPorts(\n data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): boolean {\n return countPortBlocks(getResolvedPortBlocks(data).ports) > 0;\n}\n\n/**\n * Determine whether a node/device data object resolves to any rear ports.\n * @public\n */\nexport function hasRearPorts(\n data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): boolean {\n return countPortBlocks(getResolvedPortBlocks(data).rearPorts) > 0;\n}\n\n/**\n * Choose the default device face based on the resolved front/rear port data.\n * @public\n */\nexport function getDefaultDeviceFace(\n data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): DeviceFace {\n const hasFront = hasFrontPorts(data);\n const hasRear = hasRearPorts(data);\n return hasRear && !hasFront ? 'rear' : 'front';\n}\n","import type { DeviceFace, DevicePort, NetworkNodeDataBase, Port, PortBlock, PortCableType, PortType } from '../types';\nimport { buildPortHandleId, isPowerPortType, normalizeHandleId } from './handle-ids';\nimport { normalizePortPositionInput } from './port-position';\nimport { buildPortBlocksFromPorts, getResolvedPortBlocks } from './render-ports';\n\n/** @deprecated Use {@link buildPortHandleId}. */\nexport const buildReactNetworksHandleId = buildPortHandleId;\n\nconst DEFAULT_FRONT_GROUP_NAME = 'Network Ports';\nconst DEFAULT_REAR_GROUP_NAME = 'Rear Network Ports';\nconst DEFAULT_POWER_GROUP_NAME = 'Power Ports';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst toText = (value: unknown): string | undefined => {\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\nconst normalizeFaceInput = (value: unknown): DeviceFace | undefined => {\n const normalized = toText(value)?.toLowerCase();\n if (normalized === 'front') {\n return 'front';\n }\n if (normalized === 'rear' || normalized === 'back') {\n return 'rear';\n }\n return undefined;\n};\n\nconst normalizeConnectorType = (\n value: unknown,\n): NonNullable<DevicePort['connectorType']> | undefined => {\n const normalized = toText(value)?.toUpperCase();\n if (\n normalized === 'C13' ||\n normalized === 'C14' ||\n normalized === 'C19' ||\n normalized === 'C20'\n ) {\n return normalized;\n }\n return undefined;\n};\n\nconst resolveDefaultFace = (portType: string | undefined): DeviceFace =>\n isPowerPortType(portType) ? 'rear' : 'front';\n\nconst resolveDefaultGroup = ({\n face,\n type,\n}: {\n face: DeviceFace;\n type: string | undefined;\n}): string => {\n if (isPowerPortType(type)) {\n return DEFAULT_POWER_GROUP_NAME;\n }\n return face === 'rear' ? DEFAULT_REAR_GROUP_NAME : DEFAULT_FRONT_GROUP_NAME;\n};\n\n/**\n * Resolve the effective device face for a flat port input.\n * @public\n */\nexport function resolveDevicePortFace(\n port: Pick<DevicePort, 'face' | 'type'>,\n): DeviceFace {\n return normalizeFaceInput(port.face) ?? resolveDefaultFace(port.type);\n}\n\n/** @deprecated Use {@link resolveDevicePortFace}. */\nexport const resolveReactNetworksPortFace = resolveDevicePortFace;\n\n/**\n * Resolve the effective port group name for a flat port input.\n * @public\n */\nexport function resolveDevicePortGroup(\n port: Pick<DevicePort, 'face' | 'group' | 'type'>,\n): string {\n return (\n toText(port.group) ??\n resolveDefaultGroup({\n face: resolveDevicePortFace(port),\n type: port.type,\n })\n );\n}\n\n/** @deprecated Use {@link resolveDevicePortGroup}. */\nexport const resolveReactNetworksPortGroup = resolveDevicePortGroup;\n\n/**\n * Normalize unknown **flat** port array input into canonical {@link DevicePort} entries.\n *\n * This handles a flat `DevicePort[]` only; grouped `PortBlock[]` input returns\n * `undefined` (blocks have no `label`). To resolve flat ports from any supported\n * form — including grouped `PortBlock[]` — use {@link resolveNodeDevicePorts}.\n * @public\n */\nexport function normalizeDevicePorts(value: unknown): DevicePort[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const ports = value\n .map((entry): DevicePort | null => {\n if (!isRecord(entry)) {\n return null;\n }\n\n const label = toText(entry.label);\n if (!label) {\n return null;\n }\n\n const type = toText(entry.type) as PortType | undefined;\n const face = resolveDevicePortFace({\n face: normalizeFaceInput(entry.face),\n type,\n });\n const group = resolveDevicePortGroup({\n face,\n group: toText(entry.group),\n type,\n });\n const position = normalizePortPositionInput(entry.position ?? entry);\n const order =\n typeof entry.order === 'number' && Number.isFinite(entry.order) ? entry.order : undefined;\n\n const port: DevicePort = {\n ...(toText(entry.id) ? { id: toText(entry.id) } : {}),\n label,\n face,\n group,\n connected: typeof entry.connected === 'boolean' ? entry.connected : undefined,\n ...(type ? { type } : {}),\n ...(toText(entry.cableType) ? { cableType: toText(entry.cableType) as PortCableType } : {}),\n ...(normalizeConnectorType(entry.connectorType)\n ? { connectorType: normalizeConnectorType(entry.connectorType) }\n : {}),\n ...(typeof order === 'number' ? { order } : {}),\n ...(position ? { position } : {}),\n };\n\n return port;\n })\n .filter((entry): entry is DevicePort => entry !== null);\n\n return ports.length > 0 ? ports : undefined;\n}\n\n/** @deprecated Use {@link normalizeDevicePorts}. */\nexport const normalizeReactNetworksPorts = normalizeDevicePorts;\n\nfunction normalizePortBlocks(value: unknown, face: DeviceFace): DevicePort[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.flatMap((entry) => {\n if (!isRecord(entry) || !Array.isArray(entry.ports)) {\n return [];\n }\n\n return entry.ports\n .map((rawPort, index): DevicePort | null => {\n if (!isRecord(rawPort)) {\n return null;\n }\n\n const label = toText(rawPort.label);\n if (!label) {\n return null;\n }\n\n const type = toText(rawPort.type) as PortType | undefined;\n const group = resolveDevicePortGroup({\n face,\n group: toText(entry.name),\n type,\n });\n const position = normalizePortPositionInput(rawPort.position ?? rawPort);\n const order =\n typeof rawPort.order === 'number' && Number.isFinite(rawPort.order)\n ? rawPort.order\n : index;\n\n return {\n ...(toText(rawPort.id) ? { id: toText(rawPort.id) } : {}),\n label,\n face,\n group,\n connected: typeof rawPort.connected === 'boolean' ? rawPort.connected : undefined,\n ...(type ? { type } : {}),\n ...(toText(rawPort.cableType)\n ? { cableType: toText(rawPort.cableType) as PortCableType }\n : {}),\n ...(normalizeConnectorType(rawPort.connectorType)\n ? { connectorType: normalizeConnectorType(rawPort.connectorType) }\n : {}),\n order,\n ...(position ? { position } : {}),\n };\n })\n .filter((port): port is DevicePort => port !== null);\n });\n}\n\n/**\n * Resolve flat device ports from any supported port input.\n *\n * Accepts every form the renderer accepts (`getResolvedPortBlocks`): a node-data\n * record (`ports` / `portGroups` / `rearPortGroups` / `rearPorts`), a flat\n * `DevicePort[]`, or a grouped `PortBlock[]`. Grouped `PortBlock[]` carried on\n * `ports` are treated as front groups (taking precedence over `portGroups`, matching\n * the renderer); rear groups come from `rearPortGroups`, falling back to `rearPorts`.\n * @public\n */\nexport function resolveNodeDevicePorts(value: unknown): DevicePort[] | undefined {\n if (isRecord(value)) {\n const flatPorts = normalizeDevicePorts(value.ports);\n if (flatPorts) {\n return flatPorts;\n }\n\n // `ports` may instead carry grouped PortBlock[] (front). It wins over\n // `portGroups`, mirroring getResolvedPortBlocks' precedence.\n const frontFromPorts = normalizePortBlocks(value.ports, 'front');\n const frontPorts =\n frontFromPorts.length > 0\n ? frontFromPorts\n : normalizePortBlocks(value.portGroups, 'front');\n const rearFromGroups = normalizePortBlocks(value.rearPortGroups, 'rear');\n const rearPorts =\n rearFromGroups.length > 0\n ? rearFromGroups\n : normalizePortBlocks(value.rearPorts, 'rear');\n const ports = [...frontPorts, ...rearPorts];\n return ports.length > 0 ? ports : undefined;\n }\n\n // Bare array input: a flat DevicePort[] or a grouped PortBlock[] (front groups).\n const flatPorts = normalizeDevicePorts(value);\n if (flatPorts) {\n return flatPorts;\n }\n const blockPorts = normalizePortBlocks(value, 'front');\n return blockPorts.length > 0 ? blockPorts : undefined;\n}\n\n/** @deprecated Use {@link resolveNodeDevicePorts}. */\nexport const resolveReactNetworksNodePorts = resolveNodeDevicePorts;\n\n/**\n * Flatten grouped port blocks into canonical flat {@link DevicePort} entries.\n * @public\n */\nexport function flattenDevicePortsFromGroups({\n portGroups,\n rearPortGroups,\n}: {\n portGroups?: PortBlock[];\n rearPortGroups?: PortBlock[];\n}): DevicePort[] {\n const ports: DevicePort[] = [];\n\n const appendFace = (blocks: PortBlock[] | undefined, face: DeviceFace) => {\n blocks?.forEach((block) => {\n block.ports.forEach((port, index) => {\n ports.push({\n ...(typeof port.id === 'string' && port.id.trim().length > 0 ? { id: port.id } : {}),\n label: port.label,\n face,\n group: block.name,\n connected: port.connected,\n ...(port.type ? { type: port.type } : {}),\n ...(port.cableType ? { cableType: port.cableType } : {}),\n ...(port.connectorType ? { connectorType: port.connectorType } : {}),\n order: index,\n ...(port.position ? { position: port.position } : {}),\n });\n });\n });\n };\n\n appendFace(portGroups, 'front');\n appendFace(rearPortGroups, 'rear');\n return ports;\n}\n\n/** @deprecated Use {@link flattenDevicePortsFromGroups}. */\nexport const flattenReactNetworksPortGroups = flattenDevicePortsFromGroups;\n\n/**\n * Build grouped port blocks from flat device port input.\n * @public\n */\nexport function buildDevicePortGroupsFromPorts(\n ports: DevicePort[] | undefined,\n): {\n portGroups: PortBlock[];\n rearPortGroups: PortBlock[];\n} {\n if (!Array.isArray(ports) || ports.length === 0) {\n return { portGroups: [], rearPortGroups: [] };\n }\n\n const { ports: portGroups, rearPorts: rearPortGroups } = buildPortBlocksFromPorts(ports);\n return {\n portGroups,\n rearPortGroups,\n };\n}\n\n/** @deprecated Use {@link buildDevicePortGroupsFromPorts}. */\nexport const buildReactNetworksPortGroupsFromPorts = buildDevicePortGroupsFromPorts;\n\nexport interface ListPossiblePortHandleIdsOptions {\n deviceType?: string;\n face?: DeviceFace;\n}\n\n/**\n * List the **canonical** handle ids that may exist on a rendered node for a port.\n *\n * The returned ids are in canonical form. When a device renders both faces and a\n * front/rear handle id collides, the renderer disambiguates the hidden face with a\n * `--rear` / `--front` suffix (see `normalizeHandleId`). This function does not emit\n * those split-face aliases, so callers reconciling stored/rendered handles should\n * compare through {@link normalizeHandleId} (which strips the suffix) rather than by\n * raw string equality. {@link validatePortHandleOnNodeData} does this for you.\n * @public\n */\nexport function listPossiblePortHandleIds(\n portBlockName: string,\n port: Pick<Port, 'id' | 'label' | 'type'>,\n options: ListPossiblePortHandleIdsOptions = {},\n): string[] {\n const baseHandle = buildPortHandleId(portBlockName, port);\n // Power-ness must be decided with the same rule the renderer and handle-id\n // builder use (`isPowerPortType`, a substring match), otherwise this list\n // diverges from the mounted handles for types like `power` / `power_dc` and\n // reports a phantom `-target`/`-source` handle that was never rendered.\n if (isPowerPortType(port.type)) {\n return [baseHandle];\n }\n\n const normalizedDeviceType = String(options.deviceType ?? '').toLowerCase();\n const face = options.face ?? 'front';\n\n if (normalizedDeviceType === 'switch' || normalizedDeviceType === 'router') {\n return [baseHandle, `${baseHandle}-target`];\n }\n\n if (normalizedDeviceType === 'patch-panel' || normalizedDeviceType === 'patch_panel') {\n return face === 'rear'\n ? [baseHandle, `${baseHandle}-target`]\n : [baseHandle, `${baseHandle}-source`];\n }\n\n return [baseHandle];\n}\n\n/**\n * A rendered/stored handle id resolved back to the port that owns it.\n * @public\n */\nexport interface DescribedPortHandle {\n /** The port the handle belongs to. */\n port: DevicePort;\n /** The port block (group) the port renders under. */\n blockName: string;\n /** The device face the block renders on. */\n face: DeviceFace;\n}\n\n/**\n * Resolve a handle id back to the port that owns it — the inverse of\n * {@link buildPortHandleId} / {@link listPossiblePortHandleIds} over a node's\n * resolved port blocks. Directional suffixes and split-face aliases are\n * normalized before comparison (via {@link normalizeHandleId}), so ids taken\n * straight from edges match. Pass `options.sanitizeHandle` to match under an\n * additional app-side normalization (e.g.\n * {@link sanitizeHandleIdForMatching} for `+`-insensitive comparison).\n * Returns `undefined` when no port on the node renders that handle.\n * @public\n */\nexport function describePortHandle(\n nodeData: Pick<NetworkNodeDataBase, 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n handle: string,\n options: {\n deviceType?: string;\n sanitizeHandle?: (value: string) => string;\n } = {},\n): DescribedPortHandle | undefined {\n const sanitize = options.sanitizeHandle ?? ((value: string) => normalizeHandleId(value));\n const normalizedRequestedHandle = sanitize(handle);\n const { ports, rearPorts } = getResolvedPortBlocks(nodeData);\n\n const findInBlocks = (blocks: PortBlock[], face: DeviceFace): DescribedPortHandle | undefined => {\n for (const portBlock of blocks) {\n for (const port of portBlock.ports) {\n const possibleHandles = listPossiblePortHandleIds(portBlock.name, port, {\n deviceType: options.deviceType,\n face,\n });\n if (\n possibleHandles.some(\n (candidate) => sanitize(candidate) === normalizedRequestedHandle,\n )\n ) {\n return { port, blockName: portBlock.name, face };\n }\n }\n }\n return undefined;\n };\n\n return findInBlocks(ports, 'front') ?? findInBlocks(rearPorts, 'rear');\n}\n\n/**\n * Validate that a handle id exists on node data after grouping resolution.\n * @public\n */\nexport function validatePortHandleOnNodeData(\n nodeData: Pick<NetworkNodeDataBase, 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n handle: string,\n options: {\n deviceType?: string;\n sanitizeHandle?: (value: string) => string;\n } = {},\n): boolean {\n return describePortHandle(nodeData, handle, options) !== undefined;\n}\n\n/**\n * Convert flat ports to grouped blocks using the same rules as renderers.\n * @public\n */\nexport function resolveDevicePortBlocksFromFlatPorts(\n ports: DevicePort[],\n): { portGroups: PortBlock[]; rearPortGroups: PortBlock[] } {\n return buildDevicePortGroupsFromPorts(ports);\n}\n","/**\n * Power connector utilities for consistent styling across devices and PDUs\n */\n\nexport interface PowerConnectorConfig {\n type: 'C13' | 'C14' | 'C19' | 'C20' | 'AC';\n color: string;\n borderColor: string;\n amperage: number;\n voltage: number;\n phases: number;\n}\n\nexport const POWER_CONNECTORS: Record<string, PowerConnectorConfig> = {\n C13: {\n type: 'C13',\n color: '#f59e0b', // Orange\n borderColor: '#d97706',\n amperage: 10,\n voltage: 230,\n phases: 1\n },\n C14: {\n type: 'C14',\n color: '#f59e0b', // Orange\n borderColor: '#d97706',\n amperage: 10,\n voltage: 230,\n phases: 1\n },\n C19: {\n type: 'C19',\n color: '#fbbf24', // Amber\n borderColor: '#d97706',\n amperage: 16,\n voltage: 230,\n phases: 1\n },\n C20: {\n type: 'C20',\n color: '#fbbf24', // Amber\n borderColor: '#d97706',\n amperage: 16,\n voltage: 230,\n phases: 1\n },\n AC: {\n type: 'AC',\n color: '#60a5fa', // Blue (generic AC/NEMA)\n borderColor: '#2563eb',\n amperage: 15,\n voltage: 120,\n phases: 1\n }\n};\n\nexport interface PowerPortStyle {\n width: number;\n height: number;\n borderRadius?: string;\n clipPath?: string;\n color: string;\n borderColor: string;\n fontSize: string;\n}\n\n/**\n * Get consistent power port styling for any connector type\n */\nexport function getPowerPortStyle(connectorType: string): PowerPortStyle {\n const connector = POWER_CONNECTORS[connectorType];\n if (!connector) {\n throw new Error(`Unknown connector type: ${connectorType}`);\n }\n\n const isHighPower = connector.amperage === 16; // C19/C20 are 16A\n const isGenericAc = connector.type === 'AC';\n\n // Size: C13/C14 are intentionally 30% larger while keeping C19/C20 unchanged.\n const width = isHighPower ? 12 : isGenericAc ? 10 : 8 * 1.3;\n const height = isHighPower ? 10 : isGenericAc ? 7 : 5 * 1.3;\n\n // Shape: C13/C14 have chamfered corners, C19/C20 and generic AC use rounded corners.\n const cornerStyle = isHighPower || isGenericAc\n ? { borderRadius: isGenericAc ? '2px' : '3px' }\n : {\n clipPath: `polygon(2px 0, calc(100% - 2px) 0, 100% 2px, 100% 100%, 0 100%, 0 2px)`,\n borderRadius: '0px'\n };\n\n return {\n width,\n height,\n color: connector.color,\n borderColor: connector.borderColor,\n fontSize: isHighPower ? '3px' : isGenericAc ? '3.5px' : '4px',\n ...cornerStyle\n };\n}\n\n/**\n * Get connector configuration by type\n */\nexport function getConnectorConfig(connectorType: string): PowerConnectorConfig {\n const connector = POWER_CONNECTORS[connectorType];\n if (!connector) {\n throw new Error(`Unknown connector type: ${connectorType}`);\n }\n return connector;\n}\n\n/**\n * Determine if a connector is high power (16A)\n */\nexport function isHighPowerConnector(connectorType: string): boolean {\n const connector = getConnectorConfig(connectorType);\n return connector.amperage === 16;\n}\n\n/**\n * Get PDU port type based on position in cycle\n * PDU cycle: [8xC13, 2xC19] = 10 ports per cycle\n */\nexport function getPDUPortType(portIndex: number): string {\n const cyclePosition = portIndex % 10; // 10 ports per cycle\n return cyclePosition < 8 ? 'C13' : 'C19';\n}\n\n/**\n * Get device connector type based on device name\n */\nexport function getDeviceConnectorType(deviceName: string): string {\n const name = deviceName.toLowerCase();\n \n // High power devices use C20\n if (name.includes('mx304') || name.includes('mx480') || name.includes('mx960')) {\n return 'C20';\n }\n \n // Standard devices use C14\n return 'C14';\n}\n","import type {\n DevicePort,\n DiagramDocument,\n DiagramEdge,\n DiagramEdgeEndpoint,\n DiagramEndpoint,\n DiagramExternalRef,\n DiagramNode,\n DiagramNodeKind,\n DiagramNodePlacement,\n DiagramNodeRender,\n DiagramUiState,\n NetworkEdge,\n NetworkNode,\n NetworkNodeType,\n Port,\n PortBlock,\n PortPosition,\n PortType,\n} from '../types';\nimport { isPowerPortType, normalizeHandleId } from './handle-ids';\nimport { getPDUPortType } from './power-connectors';\nimport { getResolvedPortBlocks } from './render-ports';\n\nexport interface NetworkGraphSnapshot {\n nodes: NetworkNode[];\n edges: NetworkEdge[];\n}\n\nconst CONTAINER_SUBTYPES = new Set(['patch-panel', 'closure', 'splice-tray']);\nconst IMAGE_RENDER_KEYS = [\n 'frontImageUrl',\n 'rearImageUrl',\n 'frontImageAttribution',\n 'rearImageAttribution',\n 'imageFit',\n 'imagePosition',\n 'imageRepeat',\n 'stretchToFit',\n] as const;\nconst DIMENSION_RENDER_KEYS = [\n 'panelWidth',\n 'panelHeight',\n 'closureWidth',\n 'closureHeight',\n 'headerHeight',\n] as const;\nconst GENERAL_RENDER_KEYS = [\n 'status',\n 'width',\n 'widthPx',\n 'height',\n 'header',\n 'color',\n 'colorMode',\n 'showUMarkers',\n 'customHeaderText',\n 'powerSide',\n 'rotation',\n 'rackWidthPx',\n 'rackFaceGapPx',\n] as const;\nconst UI_NODE_STATE_KEYS = ['face', 'handleSideMap'] as const;\nconst EXCLUDED_CONFIG_KEYS = new Set<string>([\n 'label',\n 'ports',\n 'portGroups',\n 'rearPortGroups',\n 'pduPortGroups',\n 'connections',\n 'netboxId',\n 'netboxType',\n 'inventoryId',\n 'inventoryType',\n 'uPosition',\n 'mountMode',\n 'rackLayout',\n 'slotPosition',\n 'holderPosition',\n ...IMAGE_RENDER_KEYS,\n ...DIMENSION_RENDER_KEYS,\n ...GENERAL_RENDER_KEYS,\n ...UI_NODE_STATE_KEYS,\n]);\n\nfunction clonePosition(position: { x?: number; y?: number } | undefined): { x: number; y: number } {\n return {\n x: typeof position?.x === 'number' ? position.x : 0,\n y: typeof position?.y === 'number' ? position.y : 0,\n };\n}\n\nfunction clonePortPosition(position: PortPosition | undefined): PortPosition | undefined {\n if (!position) {\n return undefined;\n }\n\n return { ...position };\n}\n\nfunction clonePortBlock(portBlock: PortBlock): PortBlock {\n return {\n ...portBlock,\n ports: portBlock.ports.map((port) => ({\n ...port,\n compatibility: Array.isArray(port.compatibility) ? [...port.compatibility] : undefined,\n position: clonePortPosition(port.position),\n })),\n };\n}\n\nfunction getNodeParentId(node: NetworkNode): string | undefined {\n if (typeof node.parentId === 'string') {\n return node.parentId;\n }\n\n const legacyParent = (node as NetworkNode & { parentNode?: string }).parentNode;\n return typeof legacyParent === 'string' ? legacyParent : undefined;\n}\n\nfunction toDiagramNodeKind(node: NetworkNode): { kind: DiagramNodeKind; subtype?: string } {\n if (node.type === 'vertical-pdu') {\n return { kind: 'pdu' };\n }\n\n if (node.type === 'server' || node.type === 'switch' || node.type === 'router' || node.type === 'device') {\n const subtype =\n typeof node.data?.deviceType === 'string' && node.data.deviceType.trim().length > 0\n ? node.data.deviceType\n : node.type;\n return { kind: 'equipment', subtype };\n }\n\n if (node.type === 'patch-panel' || node.type === 'closure' || node.type === 'splice-tray') {\n return { kind: 'container', subtype: node.type };\n }\n\n if (node.type === 'fibre-split') {\n return { kind: 'splitter' };\n }\n\n if (node.type === 'fibre-flow-cable' || node.type === 'fibre-flow-closure') {\n return { kind: node.type };\n }\n\n return { kind: node.type as DiagramNodeKind };\n}\n\nfunction toLegacyNodeType(node: DiagramNode): NetworkNodeType {\n if (node.kind === 'equipment') {\n const normalizedSubtype = (node.subtype ?? 'device').toLowerCase().replace(/_/g, '-');\n if (normalizedSubtype === 'server' || normalizedSubtype === 'switch' || normalizedSubtype === 'router') {\n return normalizedSubtype;\n }\n return 'device';\n }\n\n if (node.kind === 'pdu') {\n return 'vertical-pdu';\n }\n\n if (node.kind === 'container') {\n const subtype = node.subtype ?? 'patch-panel';\n if (CONTAINER_SUBTYPES.has(subtype)) {\n return subtype as NetworkNodeType;\n }\n return 'patch-panel';\n }\n\n if (node.kind === 'splitter') {\n return 'fibre-split';\n }\n\n return node.kind as NetworkNodeType;\n}\n\nfunction toDiagramPlacement(node: NetworkNode): DiagramNodePlacement {\n const placement: DiagramNodePlacement = {\n position: clonePosition(node.position),\n };\n\n const parentId = getNodeParentId(node);\n if (parentId) {\n placement.parentId = parentId;\n }\n if (node.extent === 'parent') {\n placement.extent = 'parent';\n }\n\n const data = node.data ?? {};\n if (\n typeof data.uPosition === 'number' ||\n data.mountMode === 'front' ||\n data.mountMode === 'rear' ||\n data.mountMode === 'both' ||\n data.rackLayout === 'single-face' ||\n data.rackLayout === 'split-face'\n ) {\n placement.rack = {\n ...(typeof data.uPosition === 'number' ? { uPosition: data.uPosition } : {}),\n ...(data.mountMode ? { mountMode: data.mountMode } : {}),\n ...(data.rackLayout ? { rackLayout: data.rackLayout } : {}),\n ...(typeof data.rackWidthPx === 'number' ? { rackWidthPx: data.rackWidthPx } : {}),\n ...(typeof data.rackFaceGapPx === 'number' ? { rackFaceGapPx: data.rackFaceGapPx } : {}),\n };\n }\n\n if (typeof data.slotPosition === 'number' || typeof data.holderPosition === 'number') {\n placement.container = {\n ...(typeof data.slotPosition === 'number' ? { slotPosition: data.slotPosition } : {}),\n ...(typeof data.holderPosition === 'number' ? { holderPosition: data.holderPosition } : {}),\n };\n }\n\n return placement;\n}\n\nfunction toDiagramRender(node: NetworkNode): DiagramNodeRender | undefined {\n const data = node.data ?? {};\n const render: DiagramNodeRender = {};\n\n GENERAL_RENDER_KEYS.forEach((key) => {\n const value = data[key];\n if (value !== undefined) {\n (render as Record<string, unknown>)[key] = value;\n }\n });\n\n const images = IMAGE_RENDER_KEYS.reduce<Record<string, unknown>>((acc, key) => {\n const value = data[key];\n if (value !== undefined) {\n acc[key] = value;\n }\n return acc;\n }, {});\n if (Object.keys(images).length > 0) {\n render.images = images as DiagramNodeRender['images'];\n }\n\n const dimensions = DIMENSION_RENDER_KEYS.reduce<Record<string, number | undefined>>((acc, key) => {\n const value = data[key];\n if (typeof value === 'number') {\n acc[key] = value;\n }\n return acc;\n }, {});\n if (Object.keys(dimensions).length > 0) {\n render.dimensions = dimensions;\n }\n\n return Object.keys(render).length > 0 ? render : undefined;\n}\n\nfunction toDiagramExternalRefs(node: NetworkNode): DiagramExternalRef[] | undefined {\n const refs: DiagramExternalRef[] = [];\n const data = node.data ?? {};\n\n if (data.netboxId !== undefined) {\n refs.push({\n source: 'netbox',\n id: data.netboxId,\n type: data.netboxType,\n });\n }\n\n if (data.inventoryId !== undefined) {\n refs.push({\n source: 'inventory',\n id: data.inventoryId,\n type: data.inventoryType,\n });\n }\n\n return refs.length > 0 ? refs : undefined;\n}\n\nfunction toDiagramConfig(node: NetworkNode): Record<string, unknown> | undefined {\n const data = node.data ?? {};\n const config: Record<string, unknown> = {};\n\n Object.entries(data).forEach(([key, value]) => {\n if (EXCLUDED_CONFIG_KEYS.has(key)) {\n return;\n }\n if ((key === 'rackWidthPx' || key === 'rackFaceGapPx') && node.type !== 'rack') {\n return;\n }\n config[key] = value;\n });\n\n return Object.keys(config).length > 0 ? config : undefined;\n}\n\nfunction extractUiState(nodes: NetworkNode[], previousUi?: DiagramUiState): DiagramUiState | undefined {\n const nodeState: NonNullable<DiagramUiState['nodeState']> = {};\n\n nodes.forEach((node) => {\n const data = node.data ?? {};\n const entry: NonNullable<DiagramUiState['nodeState']>[string] = {};\n const resolvedFace =\n data.face === 'front' || data.face === 'rear'\n ? data.face\n : undefined;\n\n if (resolvedFace) {\n entry.face = resolvedFace;\n }\n if (data.handleSideMap && typeof data.handleSideMap === 'object') {\n entry.handleSideMap = { ...data.handleSideMap };\n }\n\n if (Object.keys(entry).length > 0) {\n nodeState[node.id] = entry;\n }\n });\n\n if (Object.keys(nodeState).length === 0 && !previousUi?.metadata) {\n return undefined;\n }\n\n return {\n ...(Object.keys(nodeState).length > 0 ? { nodeState } : {}),\n ...(previousUi?.metadata ? { metadata: previousUi.metadata } : {}),\n };\n}\n\nfunction inferEndpointMedium(portType: string | undefined): DiagramEndpoint['medium'] {\n const normalizedType = String(portType ?? '').toLowerCase();\n if (normalizedType.includes('power')) return 'power';\n if (normalizedType.includes('fiber')) return 'fiber';\n if (normalizedType.includes('console') || normalizedType.includes('mgmt') || normalizedType.includes('usb')) {\n return 'generic';\n }\n if (normalizedType.includes('ethernet') || normalizedType.includes('rj45')) return 'ethernet';\n return 'generic';\n}\n\nfunction buildPduPortBlocks(node: NetworkNode): PortBlock[] {\n const data = node.data ?? {};\n if (Array.isArray(data.pduPortGroups) && data.pduPortGroups.length > 0) {\n return data.pduPortGroups.map(clonePortBlock);\n }\n\n const { ports, rearPorts } = getResolvedPortBlocks(data);\n if (rearPorts.length > 0) {\n return rearPorts.map(clonePortBlock);\n }\n if (ports.length > 0) {\n return ports.map(clonePortBlock);\n }\n\n const powerPorts =\n typeof data.powerPorts === 'number' && Number.isFinite(data.powerPorts) && data.powerPorts > 0\n ? data.powerPorts\n : 36;\n\n return [\n {\n name: 'Power',\n ports: Array.from({ length: powerPorts }, (_, index) => {\n const connectorType = getPDUPortType(index);\n return {\n id: `${index + 1}`,\n label: `${index + 1}`,\n connected: false,\n type: connectorType === 'C19' ? 'power_c19' : 'power_c13',\n connectorType: connectorType as Port['connectorType'],\n };\n }),\n },\n ];\n}\n\nfunction toPortBasedEndpoints(node: NetworkNode): DiagramEndpoint[] {\n const blocksByFace: Array<{ face: 'front' | 'rear'; blocks: PortBlock[] }> =\n node.type === 'vertical-pdu'\n ? [{ face: 'rear', blocks: buildPduPortBlocks(node) }]\n : (() => {\n const { ports, rearPorts } = getResolvedPortBlocks(node.data);\n return [\n { face: 'front' as const, blocks: ports.map(clonePortBlock) },\n { face: 'rear' as const, blocks: rearPorts.map(clonePortBlock) },\n ];\n })();\n\n return blocksByFace.flatMap(({ face, blocks }) =>\n blocks.flatMap((block) =>\n block.ports.map((port) => ({\n id: port.id ?? `${block.name}-${port.label}`,\n label: port.label,\n face,\n group: block.name,\n role: isPowerPortType(port.type) ? 'power' : 'network',\n medium: inferEndpointMedium(port.type),\n type: port.type,\n cableType: port.cableType,\n connector: port.connectorType,\n compatibility: Array.isArray(port.compatibility) ? [...port.compatibility] : undefined,\n layout: clonePortPosition(port.position),\n }))\n )\n );\n}\n\nfunction createSyntheticEndpoints(node: NetworkNode): DiagramEndpoint[] {\n const data = node.data ?? {};\n\n if (node.type === 'tube') {\n const fibersPerTube = Math.max(1, Math.min(24, Number(data.fibersPerTube) || 12));\n const tubeIndex = Math.max(1, Number(data.tubeIndex) || 1);\n return [\n ...Array.from({ length: fibersPerTube }, (_, index) => ({\n id: `fiber-${index + 1}`,\n label: `${index + 1}`,\n role: 'fiber' as const,\n medium: 'fiber' as const,\n })),\n {\n id: `tube-${tubeIndex}`,\n label: `Tube ${tubeIndex}`,\n role: 'tube',\n medium: 'tube',\n },\n ];\n }\n\n if (node.type === 'cable') {\n const fiberCount = Math.max(1, Math.min(24, Number(data.fiberCount) || 12));\n return [\n ...Array.from({ length: fiberCount }, (_, index) => ({\n id: `fiber-${index + 1}`,\n label: `${index + 1}`,\n role: 'fiber' as const,\n medium: 'fiber' as const,\n })),\n {\n id: 'cable-button',\n label: 'Cable',\n role: 'cable',\n },\n ];\n }\n\n if (node.type === 'multi-tube-cable') {\n const tubeCount = Math.max(1, Math.min(24, Number(data.tubeCount) || 12));\n const tubeStartIndex = Math.max(1, Number(data.tubeStartIndex) || 1);\n return [\n ...Array.from({ length: tubeCount }, (_, index) => ({\n id: `tube-${tubeStartIndex + index}`,\n label: `${tubeStartIndex + index}`,\n role: 'tube' as const,\n medium: 'tube' as const,\n })),\n {\n id: 'cable-button',\n label: 'Cable',\n role: 'cable',\n },\n ];\n }\n\n if (node.type === 'splice') {\n return [\n { id: 'left', label: 'Left', role: 'splice', medium: 'fiber' },\n { id: 'right', label: 'Right', role: 'splice', medium: 'fiber' },\n ];\n }\n\n if (node.type === 'coupler') {\n const portCount = typeof data.couplerFiberCount === 'number' && [1, 2, 4].includes(data.couplerFiberCount)\n ? data.couplerFiberCount\n : 2;\n return [\n ...Array.from({ length: portCount }, (_, index) => ({\n id: `left-${index + 1}`,\n label: `${index + 1}`,\n role: 'coupler' as const,\n medium: 'fiber' as const,\n })),\n ...Array.from({ length: portCount }, (_, index) => ({\n id: `right-${index + 1}`,\n label: `${index + 1}`,\n role: 'coupler' as const,\n medium: 'fiber' as const,\n })),\n ];\n }\n\n if (node.type === 'fibre-split') {\n const inputCount = Math.max(1, Math.min(2, Number(data.inputCount) || 1));\n const outputCount = Math.max(1, Math.min(64, Number(data.outputCount) || 8));\n return [\n ...Array.from({ length: inputCount }, (_, index) => ({\n id: `input-${index + 1}`,\n label: `Input ${index + 1}`,\n role: 'splitter' as const,\n medium: 'fiber' as const,\n })),\n ...Array.from({ length: outputCount }, (_, index) => ({\n id: `output-${index + 1}`,\n label: `Output ${index + 1}`,\n role: 'splitter' as const,\n medium: 'fiber' as const,\n })),\n ];\n }\n\n return [];\n}\n\nfunction toDiagramEndpoints(node: NetworkNode): DiagramEndpoint[] | undefined {\n const endpoints = node.type === 'vertical-pdu' || Array.isArray(node.data?.ports) || Array.isArray(node.data?.portGroups) || Array.isArray(node.data?.rearPortGroups) || Array.isArray(node.data?.pduPortGroups)\n ? toPortBasedEndpoints(node)\n : createSyntheticEndpoints(node);\n\n return endpoints.length > 0 ? endpoints : undefined;\n}\n\nfunction mergeEndpoints(\n previousEndpoints: DiagramEndpoint[] | undefined,\n nextEndpoints: DiagramEndpoint[] | undefined\n): DiagramEndpoint[] | undefined {\n if (!nextEndpoints || nextEndpoints.length === 0) {\n return nextEndpoints;\n }\n\n const previousById = new Map((previousEndpoints ?? []).map((endpoint) => [endpoint.id, endpoint]));\n return nextEndpoints.map((endpoint) => {\n const previous = previousById.get(endpoint.id);\n if (!previous) {\n return endpoint;\n }\n\n return {\n ...previous,\n ...endpoint,\n compatibility: endpoint.compatibility ?? previous.compatibility,\n layout: endpoint.layout ?? previous.layout,\n };\n });\n}\n\nfunction mergeDocumentNode(previousNode: DiagramNode | undefined, nextNode: DiagramNode): DiagramNode {\n if (!previousNode) {\n return nextNode;\n }\n\n return {\n ...previousNode,\n ...nextNode,\n placement: nextNode.placement,\n render: nextNode.render,\n config: nextNode.config,\n externalRefs: nextNode.externalRefs,\n metadata: nextNode.metadata ?? previousNode.metadata,\n endpoints: mergeEndpoints(previousNode.endpoints, nextNode.endpoints),\n };\n}\n\nexport function networkGraphToDiagramDocument(\n snapshot: NetworkGraphSnapshot,\n previousDocument?: DiagramDocument\n): DiagramDocument {\n const previousNodesById = new Map((previousDocument?.nodes ?? []).map((node) => [node.id, node]));\n\n const nodes = snapshot.nodes.map((node) =>\n mergeDocumentNode(previousNodesById.get(node.id), {\n id: node.id,\n ...toDiagramNodeKind(node),\n label: node.data?.label ?? node.id,\n placement: toDiagramPlacement(node),\n render: toDiagramRender(node),\n config: toDiagramConfig(node),\n externalRefs: toDiagramExternalRefs(node),\n metadata: previousNodesById.get(node.id)?.metadata,\n endpoints: toDiagramEndpoints(node),\n })\n );\n\n const previousEdgesById = new Map((previousDocument?.edges ?? []).map((edge) => [edge.id, edge]));\n const edges = snapshot.edges.map((edge) => {\n const nextEdge: DiagramEdge = {\n id: edge.id,\n source: {\n nodeId: edge.source,\n endpointId: edge.sourceHandle ? normalizeHandleId(edge.sourceHandle) : undefined,\n },\n target: {\n nodeId: edge.target,\n endpointId: edge.targetHandle ? normalizeHandleId(edge.targetHandle) : undefined,\n },\n type: edge.type,\n hidden: edge.hidden === true ? true : undefined,\n data: edge.data ? { ...edge.data } : undefined,\n metadata: previousEdgesById.get(edge.id)?.metadata,\n };\n\n const previous = previousEdgesById.get(edge.id);\n return previous ? { ...previous, ...nextEdge } : nextEdge;\n });\n\n return {\n nodes,\n edges,\n ui: extractUiState(snapshot.nodes, previousDocument?.ui),\n metadata: previousDocument?.metadata,\n };\n}\n\nfunction buildEndpointConnectionMap(edges: DiagramEdge[]): Map<string, number> {\n const counts = new Map<string, number>();\n\n const increment = (ref: DiagramEdgeEndpoint) => {\n if (!ref.endpointId) {\n return;\n }\n const key = `${ref.nodeId}:${normalizeHandleId(ref.endpointId)}`;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n };\n\n edges.forEach((edge) => {\n increment(edge.source);\n increment(edge.target);\n });\n\n return counts;\n}\n\nfunction endpointToPortType(endpoint: DiagramEndpoint): PortType {\n if (endpoint.type) {\n return endpoint.type;\n }\n\n if (endpoint.role === 'power' || endpoint.medium === 'power') {\n const connector = String(endpoint.connector ?? '').toLowerCase();\n if (connector.includes('c19') || connector.includes('c20')) return 'power_c19';\n if (connector.includes('c13') || connector.includes('c14')) return 'power_c13';\n return 'power_ac';\n }\n\n if (endpoint.medium === 'fiber') return 'fiber';\n if (endpoint.medium === 'ethernet') return 'ethernet';\n return 'ethernet';\n}\n\nfunction endpointsToDevicePorts(endpoints: DiagramEndpoint[], edgeConnectionMap: Map<string, number>, nodeId: string): DevicePort[] {\n return endpoints.map((endpoint) => ({\n id: endpoint.id,\n label: endpoint.label,\n face: endpoint.face === 'rear' ? 'rear' : endpoint.face === 'front' ? 'front' : undefined,\n group: endpoint.group,\n connected: (edgeConnectionMap.get(`${nodeId}:${normalizeHandleId(endpoint.id)}`) ?? 0) > 0,\n type: endpointToPortType(endpoint),\n cableType: endpoint.cableType,\n connectorType: endpoint.connector as DevicePort['connectorType'],\n compatibility:\n endpoint.compatibility && endpoint.compatibility.length > 0\n ? [...endpoint.compatibility]\n : undefined,\n position: clonePortPosition(endpoint.layout),\n }));\n}\n\nfunction endpointsToPortBlocks(endpoints: DiagramEndpoint[], edgeConnectionMap: Map<string, number>, nodeId: string): PortBlock[] {\n const grouped = new Map<string, DevicePort[]>();\n\n endpoints.forEach((endpoint) => {\n const groupName = endpoint.group ?? 'Ports';\n const existing = grouped.get(groupName);\n const port: DevicePort = {\n id: endpoint.id,\n label: endpoint.label,\n connected: (edgeConnectionMap.get(`${nodeId}:${normalizeHandleId(endpoint.id)}`) ?? 0) > 0,\n type: endpointToPortType(endpoint),\n cableType: endpoint.cableType,\n connectorType: endpoint.connector as DevicePort['connectorType'],\n compatibility:\n endpoint.compatibility && endpoint.compatibility.length > 0\n ? [...endpoint.compatibility]\n : undefined,\n position: clonePortPosition(endpoint.layout),\n };\n\n if (existing) {\n existing.push(port);\n return;\n }\n\n grouped.set(groupName, [port]);\n });\n\n return [...grouped.entries()].map(([name, ports]) => ({\n name,\n ports: ports.map((port) => ({\n id: port.id,\n label: port.label,\n connected: port.connected ?? false,\n type: port.type,\n cableType: port.cableType,\n connectorType: port.connectorType,\n compatibility: port.compatibility,\n position: clonePortPosition(port.position),\n })),\n }));\n}\n\nfunction applyRenderToLegacyData(\n data: NetworkNode['data'],\n render: DiagramNodeRender | undefined,\n uiEntry: NonNullable<DiagramUiState['nodeState']>[string] | undefined\n): NetworkNode['data'] {\n if (!render && !uiEntry) {\n return data;\n }\n\n const nextData = { ...data };\n if (render) {\n GENERAL_RENDER_KEYS.forEach((key) => {\n const value = render[key];\n if (value !== undefined) {\n (nextData as Record<string, unknown>)[key] = value;\n }\n });\n\n if (render.images) {\n IMAGE_RENDER_KEYS.forEach((key) => {\n const value = render.images?.[key];\n if (value !== undefined) {\n (nextData as Record<string, unknown>)[key] = value;\n }\n });\n }\n\n if (render.dimensions) {\n DIMENSION_RENDER_KEYS.forEach((key) => {\n const value = render.dimensions?.[key];\n if (typeof value === 'number') {\n (nextData as Record<string, unknown>)[key] = value;\n }\n });\n }\n }\n\n const resolvedFace = uiEntry?.face;\n if (resolvedFace) {\n nextData.face = resolvedFace;\n }\n if (uiEntry?.handleSideMap) {\n nextData.handleSideMap = { ...uiEntry.handleSideMap };\n }\n\n return nextData;\n}\n\nfunction applyExternalRefsToLegacyData(data: NetworkNode['data'], refs: DiagramExternalRef[] | undefined): NetworkNode['data'] {\n if (!refs || refs.length === 0) {\n return data;\n }\n\n const nextData = { ...data };\n refs.forEach((ref) => {\n if (ref.source === 'netbox') {\n nextData.netboxId = ref.id;\n if (ref.type) {\n nextData.netboxType = ref.type;\n }\n }\n if (ref.source === 'inventory') {\n nextData.inventoryId = ref.id;\n if (ref.type) {\n nextData.inventoryType = ref.type;\n }\n }\n });\n\n return nextData;\n}\n\nfunction resolveLegacyHandleId(\n node: DiagramNode | undefined,\n endpointId: string | null | undefined,\n role: 'source' | 'target'\n): string | undefined {\n if (!endpointId) {\n return undefined;\n }\n\n const normalizedEndpointId = normalizeHandleId(endpointId);\n if (!node) {\n return normalizedEndpointId;\n }\n\n if (node.kind === 'equipment') {\n const subtype = (node.subtype ?? '').toLowerCase().replace(/_/g, '-');\n const endpoint = node.endpoints?.find((candidate) => candidate.id === normalizedEndpointId);\n const isPowerEndpoint = endpoint?.role === 'power' || endpoint?.medium === 'power';\n\n if ((subtype === 'switch' || subtype === 'router') && role === 'target' && !isPowerEndpoint) {\n return `${normalizedEndpointId}-target`;\n }\n\n if (subtype === 'patch-panel') {\n const face = endpoint?.face === 'rear' ? 'rear' : 'front';\n if (role === 'source' && face === 'front') {\n return `${normalizedEndpointId}-source`;\n }\n if (role === 'target' && face === 'rear') {\n return `${normalizedEndpointId}-target`;\n }\n }\n\n return normalizedEndpointId;\n }\n\n if (node.kind === 'splitter') {\n return normalizedEndpointId;\n }\n\n return normalizedEndpointId;\n}\n\nexport function diagramDocumentToNetworkGraph(document: DiagramDocument): NetworkGraphSnapshot {\n const edgeConnectionMap = buildEndpointConnectionMap(document.edges);\n const uiNodeState = document.ui?.nodeState ?? {};\n\n const nodes: NetworkNode[] = document.nodes.map((node) => {\n const legacyType = toLegacyNodeType(node);\n let data: NetworkNode['data'] = {\n label: node.label,\n ...(node.config ?? {}),\n } as NetworkNode['data'];\n\n data = applyRenderToLegacyData(data, node.render, uiNodeState[node.id]);\n data = applyExternalRefsToLegacyData(data, node.externalRefs);\n\n if (legacyType === 'device' || legacyType === 'server' || legacyType === 'switch' || legacyType === 'router') {\n data.deviceType = node.subtype ?? (legacyType === 'device' ? 'device' : legacyType);\n if (node.endpoints && node.endpoints.length > 0) {\n data.ports = endpointsToDevicePorts(node.endpoints, edgeConnectionMap, node.id);\n }\n } else if (legacyType === 'vertical-pdu') {\n const rearEndpoints = (node.endpoints ?? []).filter((endpoint) => endpoint.face !== 'front');\n if (rearEndpoints.length > 0) {\n data.pduPortGroups = endpointsToPortBlocks(rearEndpoints, edgeConnectionMap, node.id);\n data.powerPorts = rearEndpoints.length;\n }\n }\n\n if (node.placement.rack) {\n if (node.placement.rack.rackLayout) {\n data.rackLayout = node.placement.rack.rackLayout;\n }\n if (typeof node.placement.rack.rackWidthPx === 'number') {\n data.rackWidthPx = node.placement.rack.rackWidthPx;\n }\n if (typeof node.placement.rack.rackFaceGapPx === 'number') {\n data.rackFaceGapPx = node.placement.rack.rackFaceGapPx;\n }\n }\n\n if (legacyType !== 'rack' && node.placement.rack) {\n data.isRackMounted = true;\n if (typeof node.placement.rack.uPosition === 'number') {\n data.uPosition = node.placement.rack.uPosition;\n }\n if (node.placement.rack.mountMode) {\n data.mountMode = node.placement.rack.mountMode;\n }\n }\n\n if (node.placement.container) {\n if (typeof node.placement.container.slotPosition === 'number') {\n data.slotPosition = node.placement.container.slotPosition;\n }\n if (typeof node.placement.container.holderPosition === 'number') {\n data.holderPosition = node.placement.container.holderPosition;\n }\n }\n\n return {\n id: node.id,\n type: legacyType,\n position: clonePosition(node.placement.position),\n ...(node.placement.parentId ? { parentId: node.placement.parentId } : {}),\n ...(node.placement.extent ? { extent: node.placement.extent } : {}),\n data,\n } as NetworkNode;\n });\n\n const nodesById = new Map(document.nodes.map((node) => [node.id, node]));\n const edges: NetworkEdge[] = document.edges.map((edge) => ({\n id: edge.id,\n source: edge.source.nodeId,\n target: edge.target.nodeId,\n ...(edge.source.endpointId ? { sourceHandle: resolveLegacyHandleId(nodesById.get(edge.source.nodeId), edge.source.endpointId, 'source') } : {}),\n ...(edge.target.endpointId ? { targetHandle: resolveLegacyHandleId(nodesById.get(edge.target.nodeId), edge.target.endpointId, 'target') } : {}),\n ...(edge.type ? { type: edge.type } : {}),\n ...(edge.hidden === true ? { hidden: true } : {}),\n ...(edge.data ? { data: { ...edge.data } } : {}),\n }));\n\n return { nodes, edges };\n}\n","import type { NetworkEdge, NetworkNode } from '../types';\n\nexport type PathLayoutDirection = 'horizontal' | 'vertical';\n\nexport interface PathLayoutStepInput {\n id: string;\n type?: NetworkNode['type'];\n data?: NetworkNode['data'];\n}\n\nexport interface PathLayoutEdgeInput {\n id: string;\n source: string;\n target: string;\n type?: NetworkEdge['type'];\n data?: NetworkEdge['data'];\n}\n\nexport interface BuildPathLayoutOptions {\n direction?: PathLayoutDirection;\n spacing?: number;\n origin?: { x: number; y: number };\n}\n\nconst DEFAULT_SPACING = 220;\n\n/**\n * Lay out a sequential path of nodes and connecting edges with deterministic spacing.\n * @public\n */\nexport function buildPathLayoutGraph(\n steps: PathLayoutStepInput[],\n edges: PathLayoutEdgeInput[],\n options: BuildPathLayoutOptions = {},\n): { nodes: NetworkNode[]; edges: NetworkEdge[] } {\n const direction = options.direction ?? 'horizontal';\n const spacing = options.spacing ?? DEFAULT_SPACING;\n const origin = options.origin ?? { x: 0, y: 0 };\n\n const nodes = steps.map((step, index) => ({\n id: step.id,\n type: step.type ?? 'fiber',\n position:\n direction === 'vertical'\n ? { x: origin.x, y: origin.y + index * spacing }\n : { x: origin.x + index * spacing, y: origin.y },\n data: step.data ?? { label: step.id },\n })) as NetworkNode[];\n\n const layoutEdges: NetworkEdge[] = edges.map((edge) => ({\n id: edge.id,\n source: edge.source,\n target: edge.target,\n type: edge.type ?? 'fiber',\n data: edge.data,\n }));\n\n return { nodes, edges: layoutEdges };\n}\n","import type { NetworkNode } from '../types';\nimport { normalizeHandleId, parseHandleId } from './handle-ids';\n\nexport const isFiberNode = (node?: NetworkNode) =>\n node?.type === 'tube' ||\n node?.type === 'splice' ||\n node?.type === 'coupler' ||\n node?.type === 'fibre-split' ||\n node?.type === 'cable' ||\n node?.type === 'multi-tube-cable';\n\nexport const isFiberConnection = (sourceNode?: NetworkNode, targetNode?: NetworkNode) =>\n isFiberNode(sourceNode) || isFiberNode(targetNode);\n\n/**\n * Normalize a rendered handle id back to its canonical endpoint id.\n * The `node` parameter is retained for call-site ergonomics and future\n * node-specific normalization; today all node types share one rule.\n */\nexport const normalizeHandle = (_node?: NetworkNode, handle?: string | null) =>\n handle ? normalizeHandleId(handle) : '';\n\n/** Result shape shared with the store's `ValidationResult`. */\nexport interface ConnectionRuleResult {\n valid: boolean;\n reason?: string;\n}\n\n/**\n * Structural classification of a handle that participates in fiber routing.\n * This is the single place that maps a (node, handle) pair to its fiber role;\n * the connection matrix below is expressed purely in terms of these classes.\n */\nexport type FiberEndpointClass =\n | 'tube-fiber'\n | 'tube-tube'\n | 'cable-tube'\n | 'cable-button'\n | 'splice-side'\n | 'coupler-port'\n | 'splitter-input'\n | 'splitter-output'\n | 'other';\n\nexport function classifyFiberEndpoint(\n node: NetworkNode | undefined,\n handle: string | null | undefined,\n): FiberEndpointClass {\n if (!node || !handle) {\n return 'other';\n }\n\n // Structural shape from the single grammar authority; the node type turns\n // it into semantics (left/right means splice-side on a splice but a port on\n // a coupler, etc.).\n const { shape } = parseHandleId(handle);\n switch (node.type) {\n case 'tube':\n if (shape.kind === 'fiber') return 'tube-fiber';\n if (shape.kind === 'tube') return 'tube-tube';\n return 'other';\n case 'cable':\n if (shape.kind === 'fiber') return 'tube-fiber';\n if (shape.kind === 'cable-button') return 'cable-button';\n return 'other';\n case 'multi-tube-cable':\n if (shape.kind === 'tube') return 'cable-tube';\n if (shape.kind === 'cable-button') return 'cable-button';\n return 'other';\n case 'splice':\n return shape.kind === 'splice-side' ? 'splice-side' : 'other';\n case 'coupler':\n return shape.kind === 'side-port' ? 'coupler-port' : 'other';\n case 'fibre-split':\n if (shape.kind === 'split-io') {\n return shape.io === 'input' ? 'splitter-input' : 'splitter-output';\n }\n return 'other';\n default:\n return 'other';\n }\n}\n\n/**\n * Allowed *directed* `source>target` fiber-endpoint pairs.\n *\n * This list is an exact expansion of the legacy boolean rule set. Directionality\n * is preserved deliberately: e.g. a splitter input may be a connection target\n * (feed in) when paired with a splice, but never a source toward a splice — so\n * `splice-side>splitter-input` is present while `splitter-input>splice-side` is not.\n */\nconst ALLOWED_FIBER_PAIRS: ReadonlySet<string> = new Set([\n // Tube fiber <-> splice (either direction)\n 'tube-fiber>splice-side',\n 'splice-side>tube-fiber',\n // Splice <-> coupler (either direction)\n 'splice-side>coupler-port',\n 'coupler-port>splice-side',\n // Splice -> splitter input (feed in only)\n 'splice-side>splitter-input',\n // Splice <-> splitter output (either direction)\n 'splice-side>splitter-output',\n 'splitter-output>splice-side',\n // Coupler <-> coupler\n 'coupler-port>coupler-port',\n // Coupler <-> splitter (either input or output, either direction)\n 'coupler-port>splitter-input',\n 'splitter-input>coupler-port',\n 'coupler-port>splitter-output',\n 'splitter-output>coupler-port',\n // Splitter output <-> tube fiber (either direction)\n 'splitter-output>tube-fiber',\n 'tube-fiber>splitter-output',\n // Cable trunk button <-> cable trunk button\n 'cable-button>cable-button',\n // Cable tube <-> tube tube (either direction)\n 'cable-tube>tube-tube',\n 'tube-tube>cable-tube',\n]);\n\nfunction isTubeHandleClass(value: FiberEndpointClass): boolean {\n return value === 'tube-tube' || value === 'cable-tube';\n}\n\n/**\n * Evaluate whether a fiber-domain connection is structurally allowed.\n *\n * This is the single source of truth for fiber connection rules, consumed by\n * both the headless store validator and the interactive `NetworkDiagram`\n * validator. It does not consider handle occupancy (capacity) — fiber handles\n * support edge replacement, so occupancy is resolved by the caller.\n */\nexport function evaluateFiberConnectionRule(\n sourceNode: NetworkNode | undefined,\n sourceHandle: string | null | undefined,\n targetNode: NetworkNode | undefined,\n targetHandle: string | null | undefined,\n): ConnectionRuleResult {\n if (sourceNode?.type === 'splice' && targetNode?.type === 'splice') {\n const sourceIsRibbon = sourceNode.data?.mode === 'ribbon';\n const targetIsRibbon = targetNode.data?.mode === 'ribbon';\n if (sourceIsRibbon !== targetIsRibbon) {\n return { valid: false, reason: 'Ribbon splices can only connect to other ribbon splices' };\n }\n }\n\n const sourceClass = classifyFiberEndpoint(sourceNode, sourceHandle);\n const targetClass = classifyFiberEndpoint(targetNode, targetHandle);\n\n if (ALLOWED_FIBER_PAIRS.has(`${sourceClass}>${targetClass}`)) {\n return { valid: true };\n }\n\n if (isTubeHandleClass(sourceClass) && isTubeHandleClass(targetClass)) {\n return { valid: false, reason: 'Tube handles can only connect between cable and tube nodes' };\n }\n\n return { valid: false, reason: 'Connection not allowed by fiber rules' };\n}\n"]}
|
package/dist/model.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var U=/-(source|target)$/i,R=/--(rear|front)$/i;function k(e){return e?e.replace(U,""):""}function p(e){return k(e).replace(R,"")}function l(e){return typeof e=="string"&&e.toLowerCase().includes("power")}function D(e,t){return typeof t.id=="string"&&t.id.length>0?t.id:l(t.type)?t.label:`${e}-${t.label}`}function W(e){let t=k(e).toLowerCase();return t.startsWith("pwr")||t.includes("power")}function ae(e,t,n){return !t||(typeof n?.portType=="string"&&n.portType.length>0?l(n.portType):W(e))?e:`${k(e)}-${t}`}var se=/^\d+$/,ce=/^fiber-(\d+)-(\d+)$/,ue=/^fiber-(\d+)$/,de=/^tube-(\d+)$/,pe=/^(left|right)-(\d+)$/,le=/^(input|output)-(\d+)$/;function fe(e){if(se.test(e))return {kind:"outlet",outlet:Number(e)};let t=ce.exec(e);if(t)return {kind:"fiber",tube:Number(t[1]),fiber:Number(t[2])};let n=ue.exec(e);if(n)return {kind:"fiber",fiber:Number(n[1])};let r=de.exec(e);if(r)return {kind:"tube",tube:Number(r[1])};if(e==="left"||e==="right")return {kind:"splice-side",side:e};let i=pe.exec(e);if(i)return {kind:"side-port",side:i[1],index:Number(i[2])};let a=le.exec(e);return a?{kind:"split-io",io:a[1],index:Number(a[2])}:e==="cable-button"?{kind:"cable-button"}:{kind:"port"}}function T(e){let t=e??"",n=U.exec(t),r=n?t.slice(0,t.length-n[0].length):t,i=R.exec(r),a=i?r.slice(0,r.length-i[0].length):r,o={raw:t,base:a,shape:fe(a)};return n&&(o.direction=n[1].toLowerCase()),i&&(o.faceAlias=i[1].toLowerCase()),o}function me(e,t){return t===void 0?`fiber-${e}`:`fiber-${t}-${e}`}function be(e){return `tube-${e}`}function Pe(e){return e}function j(e,t){return `${e}-${t}`}function ye(e,t){return `${e}-${t}`}function ge(e,t){return `${e.replace(R,"")}--${t}`}function he(e){return e.replace(/\+/g,"")}function Ee(e){let{shape:t}=T(e);if(t.kind==="splice-side")return t.side==="left"?"right":"left";if(t.kind==="side-port")return j(t.side==="left"?"right":"left",t.index)}var Ne=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),m=e=>{if(typeof e=="number"&&Number.isFinite(e))return e;if(typeof e=="string"&&e.trim().length>0){let t=Number(e);if(Number.isFinite(t))return t}},ke=e=>e.trim().toLowerCase().split(/[^a-z0-9]+/).filter(t=>t.length>0),G=e=>{if(e==="top"||e==="up")return "top";if(e==="right")return "right";if(e==="bottom"||e==="down")return "bottom";if(e==="left")return "left"},K=e=>e.includes("center")||e.includes("centre")||e.includes("middle")||e.includes("mid"),De=(e,t)=>{let n=t.includes("left"),r=t.includes("right"),i=t.includes("top")||t.includes("up"),a=t.includes("bottom")||t.includes("down"),o=K(t);if(e==="top"||e==="bottom")return n&&o&&!r?34:r&&o&&!n?66:n&&!r?12:r&&!n?88:o?50:void 0;if(i&&o&&!a)return 34;if(a&&o&&!i)return 66;if(i&&!a)return 12;if(a&&!i)return 88;if(o)return 50},Te=e=>{if(e.includes("front")||e.includes("rear")||e.includes("back"))return;let n=e.map(G).find(i=>i!==void 0);if(!n)return;let r=De(n,e);if(r!==void 0)return {side:n,offsetPercent:r}},xe=e=>{if(e.length===0)return;let t=e.includes("left"),n=e.includes("right"),r=K(e),i=e.includes("front")||e.includes("top")||e.includes("up"),a=e.includes("rear")||e.includes("back")||e.includes("bottom")||e.includes("down"),o;t&&r&&!n?o=34:n&&r&&!t?o=66:t&&!n?o=12:n&&!t?o=88:r&&(o=50);let s;if(i&&!a?s=22:a&&!i?s=78:r&&(s=50),!(o===void 0||s===void 0))return {xPercent:o,yPercent:s}},C=e=>{let t=ke(e);if(t.length!==0){if(t.length===1){let n=G(t[0]);return n?{side:n}:void 0}return Te(t)??xe(t)}},b=e=>{if(typeof e=="string")return C(e);if(!Ne(e))return;let t=e.side??e.port_side??e.portSide,n=typeof t=="string"?C(t):void 0,i=(typeof t=="string"?G(t.trim().toLowerCase()):void 0)??n?.side,a=m(e.offsetPercent??e.offset_percent??e.offsetPct??e.offset_pct)??n?.offsetPercent,o=m(e.offsetPx??e.offset_px??e.offsetPixels??e.offset_pixels)??n?.offsetPx,s=m(e.xPercent??e.x_percent??e.xPct??e.x_pct??e.horizontalPercent??e.horizontal_percent??e.x)??n?.xPercent,c=m(e.yPercent??e.y_percent??e.yPct??e.y_pct??e.verticalPercent??e.vertical_percent??e.y)??n?.yPercent,u=m(e.widthPercent??e.width_percent??e.widthPct??e.width_pct??e.wPercent??e.w_percent),f=m(e.heightPercent??e.height_percent??e.heightPct??e.height_pct??e.hPercent??e.h_percent);if(!(i===void 0&&a===void 0&&o===void 0&&s===void 0&&c===void 0&&u===void 0&&f===void 0))return {side:i,offsetPercent:a,offsetPx:o,xPercent:s,yPercent:c,widthPercent:u,heightPercent:f}};var we="Network Ports",_e="Rear Network Ports",Fe="Power Ports",Y=8,Ie=92;function P(e){return Math.max(0,Math.min(100,e))}function A(e){if(e==="top"||e==="right"||e==="bottom"||e==="left")return e}function y(e){return b(e)}function q(e){let t=y(e.position);return typeof t?.xPercent=="number"&&typeof t?.yPercent=="number"}function Re(e){let t=y(e.position);return typeof t?.offsetPercent=="number"||typeof t?.offsetPx=="number"}function Ce(e){return e.face==="rear"||e.face==="back"?"rear":e.face==="front"?"front":l(e.type)?"rear":"front"}function Ge(e,t){return typeof e.group=="string"&&e.group.trim().length>0?e.group.trim():l(e.type)?Fe:t==="rear"?_e:we}function w(e){return typeof e.port.order=="number"&&Number.isFinite(e.port.order)?e.port.order:e.index}function Se(e,t){return t<=1?50:Y+e*(Ie-Y)/(t-1)}function Ae(e){let t=e.filter(i=>!!A(y(i.port.position)?.side)&&!q(i.port)&&!Re(i.port)),n=new Map,r=new Map;return t.forEach(i=>{let a=A(y(i.port.position)?.side);if(!a)return;let o=r.get(a);if(o){o.push(i);return}r.set(a,[i]);}),r.forEach(i=>{let a=[...i].sort((o,s)=>{let c=w(o)-w(s);return c!==0?c:o.index-s.index});a.forEach((o,s)=>{n.set(o.index,Se(s,a.length));});}),n}function He(e,t){let n=y(e.position),r=A(n?.side);if(q(e)){let s={xPercent:P(n?.xPercent),yPercent:P(n?.yPercent)};return typeof n?.widthPercent=="number"&&Number.isFinite(n.widthPercent)&&(s.widthPercent=P(n.widthPercent)),typeof n?.heightPercent=="number"&&Number.isFinite(n.heightPercent)&&(s.heightPercent=P(n.heightPercent)),r&&(s.side=r),s}if(!r)return;let i={side:r};if(typeof n?.offsetPx=="number"&&Number.isFinite(n.offsetPx))return i.offsetPx=n.offsetPx,i;let o=(typeof n?.offsetPercent=="number"&&Number.isFinite(n.offsetPercent)?P(n.offsetPercent):void 0)??(typeof t=="number"?P(t):void 0);return typeof o=="number"&&(i.offsetPercent=o),i}function Oe(e,t){let n=He(e.port,t);return {id:e.port.id,label:e.port.label,sourceTypeLabel:e.port.sourceTypeLabel,connected:e.port.connected??false,type:e.port.type,cableType:e.port.cableType,connectorType:e.port.connectorType,compatibility:Array.isArray(e.port.compatibility)?[...e.port.compatibility]:void 0,...n?{position:n}:{}}}function x(e){return e.map(t=>({...t,ports:t.ports.map(n=>{let r=y(n.position);return {...n,...r?{position:r}:{position:void 0}}})}))}function S(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t!==null&&typeof t=="object"&&typeof t.name=="string"&&Array.isArray(t.ports))}function Be(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t!==null&&typeof t=="object"&&typeof t.label=="string"&&!(typeof t.name=="string"&&Array.isArray(t.ports)))}function H(e){let t=new Map;e.forEach((i,a)=>{if(typeof i.label!="string"||i.label.trim().length===0)return;let o=Ce(i),s=Ge(i,o),c=`${o}:${s}`,u=t.get(c);if(u){u.entries.push({index:a,port:{...i,label:i.label.trim()}});return}t.set(c,{face:o,groupName:s,entries:[{index:a,port:{...i,label:i.label.trim()}}]});});let n=[],r=[];return t.forEach(i=>{let a=[...i.entries].sort((c,u)=>{let f=w(c)-w(u);return f!==0?f:c.index-u.index}),o=Ae(a),s={name:i.groupName,ports:a.map(c=>Oe(c,o.get(c.index)))};if(i.face==="rear"){r.push(s);return}n.push(s);}),{ports:n,rearPorts:r}}function g(e){return S(e?.ports)?{ports:x(e.ports),rearPorts:x(Array.isArray(e?.rearPortGroups)?e.rearPortGroups:S(e?.rearPorts)?e.rearPorts:[])}:Be(e?.ports)?H(e.ports):{ports:x(Array.isArray(e?.portGroups)?e.portGroups:[]),rearPorts:x(Array.isArray(e?.rearPortGroups)?e.rearPortGroups:S(e?.rearPorts)?e.rearPorts:[])}}var ve=D,Le="Network Ports",Me="Rear Network Ports",$e="Power Ports",_=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),d=e=>{if(typeof e!="string")return;let t=e.trim();return t.length>0?t:void 0},X=e=>{let t=d(e)?.toLowerCase();if(t==="front")return "front";if(t==="rear"||t==="back")return "rear"},F=e=>{let t=d(e)?.toUpperCase();if(t==="C13"||t==="C14"||t==="C19"||t==="C20")return t},ze=e=>l(e)?"rear":"front",Ue=({face:e,type:t})=>l(t)?$e:e==="rear"?Me:Le;function V(e){return X(e.face)??ze(e.type)}function I(e){return d(e.group)??Ue({face:V(e),type:e.type})}var We=I;function O(e){if(!Array.isArray(e))return;let t=e.map(n=>{if(!_(n))return null;let r=d(n.label);if(!r)return null;let i=d(n.type),a=V({face:X(n.face),type:i}),o=I({face:a,group:d(n.group),type:i}),s=b(n.position??n),c=typeof n.order=="number"&&Number.isFinite(n.order)?n.order:void 0;return {...d(n.id)?{id:d(n.id)}:{},label:r,face:a,group:o,connected:typeof n.connected=="boolean"?n.connected:void 0,...i?{type:i}:{},...d(n.cableType)?{cableType:d(n.cableType)}:{},...F(n.connectorType)?{connectorType:F(n.connectorType)}:{},...typeof c=="number"?{order:c}:{},...s?{position:s}:{}}}).filter(n=>n!==null);return t.length>0?t:void 0}function h(e,t){return Array.isArray(e)?e.flatMap(n=>!_(n)||!Array.isArray(n.ports)?[]:n.ports.map((r,i)=>{if(!_(r))return null;let a=d(r.label);if(!a)return null;let o=d(r.type),s=I({face:t,group:d(n.name),type:o}),c=b(r.position??r),u=typeof r.order=="number"&&Number.isFinite(r.order)?r.order:i;return {...d(r.id)?{id:d(r.id)}:{},label:a,face:t,group:s,connected:typeof r.connected=="boolean"?r.connected:void 0,...o?{type:o}:{},...d(r.cableType)?{cableType:d(r.cableType)}:{},...F(r.connectorType)?{connectorType:F(r.connectorType)}:{},order:u,...c?{position:c}:{}}}).filter(r=>r!==null)):[]}function J(e){if(_(e)){let r=O(e.ports);if(r)return r;let i=h(e.ports,"front"),a=i.length>0?i:h(e.portGroups,"front"),o=h(e.rearPortGroups,"rear"),s=o.length>0?o:h(e.rearPorts,"rear"),c=[...a,...s];return c.length>0?c:void 0}let t=O(e);if(t)return t;let n=h(e,"front");return n.length>0?n:void 0}var je=J;function Ke({portGroups:e,rearPortGroups:t}){let n=[],r=(i,a)=>{i?.forEach(o=>{o.ports.forEach((s,c)=>{n.push({...typeof s.id=="string"&&s.id.trim().length>0?{id:s.id}:{},label:s.label,face:a,group:o.name,connected:s.connected,...s.type?{type:s.type}:{},...s.cableType?{cableType:s.cableType}:{},...s.connectorType?{connectorType:s.connectorType}:{},order:c,...s.position?{position:s.position}:{}});});});};return r(e,"front"),r(t,"rear"),n}var Ye=Ke;function B(e){if(!Array.isArray(e)||e.length===0)return {portGroups:[],rearPortGroups:[]};let{ports:t,rearPorts:n}=H(e);return {portGroups:t,rearPortGroups:n}}var qe=B;function Q(e,t,n={}){let r=D(e,t);if(l(t.type))return [r];let i=String(n.deviceType??"").toLowerCase(),a=n.face??"front";return i==="switch"||i==="router"?[r,`${r}-target`]:i==="patch-panel"||i==="patch_panel"?a==="rear"?[r,`${r}-target`]:[r,`${r}-source`]:[r]}function Z(e,t,n={}){let r=n.sanitizeHandle??(c=>p(c)),i=r(t),{ports:a,rearPorts:o}=g(e),s=(c,u)=>{for(let f of c)for(let z of f.ports)if(Q(f.name,z,{deviceType:n.deviceType,face:u}).some(ie=>r(ie)===i))return {port:z,blockName:f.name,face:u}};return s(a,"front")??s(o,"rear")}function Xe(e,t,n={}){return Z(e,t,n)!==void 0}function Ve(e){return B(e)}function ee(e){return e%10<8?"C13":"C19"}var Je=new Set(["patch-panel","closure","splice-tray"]),v=["frontImageUrl","rearImageUrl","frontImageAttribution","rearImageAttribution","imageFit","imagePosition","imageRepeat","stretchToFit"],L=["panelWidth","panelHeight","closureWidth","closureHeight","headerHeight"],M=["status","width","widthPx","height","header","color","colorMode","showUMarkers","customHeaderText","powerSide","rotation","rackWidthPx","rackFaceGapPx"],Qe=["face","handleSideMap"],Ze=new Set(["label","ports","portGroups","rearPortGroups","pduPortGroups","connections","netboxId","netboxType","inventoryId","inventoryType","uPosition","mountMode","rackLayout","slotPosition","holderPosition",...v,...L,...M,...Qe]);function re(e){return {x:typeof e?.x=="number"?e.x:0,y:typeof e?.y=="number"?e.y:0}}function N(e){if(e)return {...e}}function E(e){return {...e,ports:e.ports.map(t=>({...t,compatibility:Array.isArray(t.compatibility)?[...t.compatibility]:void 0,position:N(t.position)}))}}function et(e){if(typeof e.parentId=="string")return e.parentId;let t=e.parentNode;return typeof t=="string"?t:void 0}function tt(e){return e.type==="vertical-pdu"?{kind:"pdu"}:e.type==="server"||e.type==="switch"||e.type==="router"||e.type==="device"?{kind:"equipment",subtype:typeof e.data?.deviceType=="string"&&e.data.deviceType.trim().length>0?e.data.deviceType:e.type}:e.type==="patch-panel"||e.type==="closure"||e.type==="splice-tray"?{kind:"container",subtype:e.type}:e.type==="fibre-split"?{kind:"splitter"}:e.type==="fibre-flow-cable"||e.type==="fibre-flow-closure"?{kind:e.type}:{kind:e.type}}function rt(e){if(e.kind==="equipment"){let t=(e.subtype??"device").toLowerCase().replace(/_/g,"-");return t==="server"||t==="switch"||t==="router"?t:"device"}if(e.kind==="pdu")return "vertical-pdu";if(e.kind==="container"){let t=e.subtype??"patch-panel";return Je.has(t)?t:"patch-panel"}return e.kind==="splitter"?"fibre-split":e.kind}function nt(e){let t={position:re(e.position)},n=et(e);n&&(t.parentId=n),e.extent==="parent"&&(t.extent="parent");let r=e.data??{};return (typeof r.uPosition=="number"||r.mountMode==="front"||r.mountMode==="rear"||r.mountMode==="both"||r.rackLayout==="single-face"||r.rackLayout==="split-face")&&(t.rack={...typeof r.uPosition=="number"?{uPosition:r.uPosition}:{},...r.mountMode?{mountMode:r.mountMode}:{},...r.rackLayout?{rackLayout:r.rackLayout}:{},...typeof r.rackWidthPx=="number"?{rackWidthPx:r.rackWidthPx}:{},...typeof r.rackFaceGapPx=="number"?{rackFaceGapPx:r.rackFaceGapPx}:{}}),(typeof r.slotPosition=="number"||typeof r.holderPosition=="number")&&(t.container={...typeof r.slotPosition=="number"?{slotPosition:r.slotPosition}:{},...typeof r.holderPosition=="number"?{holderPosition:r.holderPosition}:{}}),t}function ot(e){let t=e.data??{},n={};M.forEach(a=>{let o=t[a];o!==void 0&&(n[a]=o);});let r=v.reduce((a,o)=>{let s=t[o];return s!==void 0&&(a[o]=s),a},{});Object.keys(r).length>0&&(n.images=r);let i=L.reduce((a,o)=>{let s=t[o];return typeof s=="number"&&(a[o]=s),a},{});return Object.keys(i).length>0&&(n.dimensions=i),Object.keys(n).length>0?n:void 0}function it(e){let t=[],n=e.data??{};return n.netboxId!==void 0&&t.push({source:"netbox",id:n.netboxId,type:n.netboxType}),n.inventoryId!==void 0&&t.push({source:"inventory",id:n.inventoryId,type:n.inventoryType}),t.length>0?t:void 0}function at(e){let t=e.data??{},n={};return Object.entries(t).forEach(([r,i])=>{Ze.has(r)||(r==="rackWidthPx"||r==="rackFaceGapPx")&&e.type!=="rack"||(n[r]=i);}),Object.keys(n).length>0?n:void 0}function st(e,t){let n={};if(e.forEach(r=>{let i=r.data??{},a={},o=i.face==="front"||i.face==="rear"?i.face:void 0;o&&(a.face=o),i.handleSideMap&&typeof i.handleSideMap=="object"&&(a.handleSideMap={...i.handleSideMap}),Object.keys(a).length>0&&(n[r.id]=a);}),!(Object.keys(n).length===0&&!t?.metadata))return {...Object.keys(n).length>0?{nodeState:n}:{},...t?.metadata?{metadata:t.metadata}:{}}}function ct(e){let t=String(e??"").toLowerCase();return t.includes("power")?"power":t.includes("fiber")?"fiber":t.includes("console")||t.includes("mgmt")||t.includes("usb")?"generic":t.includes("ethernet")||t.includes("rj45")?"ethernet":"generic"}function ut(e){let t=e.data??{};if(Array.isArray(t.pduPortGroups)&&t.pduPortGroups.length>0)return t.pduPortGroups.map(E);let{ports:n,rearPorts:r}=g(t);if(r.length>0)return r.map(E);if(n.length>0)return n.map(E);let i=typeof t.powerPorts=="number"&&Number.isFinite(t.powerPorts)&&t.powerPorts>0?t.powerPorts:36;return [{name:"Power",ports:Array.from({length:i},(a,o)=>{let s=ee(o);return {id:`${o+1}`,label:`${o+1}`,connected:false,type:s==="C19"?"power_c19":"power_c13",connectorType:s}})}]}function dt(e){return (e.type==="vertical-pdu"?[{face:"rear",blocks:ut(e)}]:(()=>{let{ports:n,rearPorts:r}=g(e.data);return [{face:"front",blocks:n.map(E)},{face:"rear",blocks:r.map(E)}]})()).flatMap(({face:n,blocks:r})=>r.flatMap(i=>i.ports.map(a=>({id:a.id??`${i.name}-${a.label}`,label:a.label,face:n,group:i.name,role:l(a.type)?"power":"network",medium:ct(a.type),type:a.type,cableType:a.cableType,connector:a.connectorType,compatibility:Array.isArray(a.compatibility)?[...a.compatibility]:void 0,layout:N(a.position)}))))}function pt(e){let t=e.data??{};if(e.type==="tube"){let n=Math.max(1,Math.min(24,Number(t.fibersPerTube)||12)),r=Math.max(1,Number(t.tubeIndex)||1);return [...Array.from({length:n},(i,a)=>({id:`fiber-${a+1}`,label:`${a+1}`,role:"fiber",medium:"fiber"})),{id:`tube-${r}`,label:`Tube ${r}`,role:"tube",medium:"tube"}]}if(e.type==="cable"){let n=Math.max(1,Math.min(24,Number(t.fiberCount)||12));return [...Array.from({length:n},(r,i)=>({id:`fiber-${i+1}`,label:`${i+1}`,role:"fiber",medium:"fiber"})),{id:"cable-button",label:"Cable",role:"cable"}]}if(e.type==="multi-tube-cable"){let n=Math.max(1,Math.min(24,Number(t.tubeCount)||12)),r=Math.max(1,Number(t.tubeStartIndex)||1);return [...Array.from({length:n},(i,a)=>({id:`tube-${r+a}`,label:`${r+a}`,role:"tube",medium:"tube"})),{id:"cable-button",label:"Cable",role:"cable"}]}if(e.type==="splice")return [{id:"left",label:"Left",role:"splice",medium:"fiber"},{id:"right",label:"Right",role:"splice",medium:"fiber"}];if(e.type==="coupler"){let n=typeof t.couplerFiberCount=="number"&&[1,2,4].includes(t.couplerFiberCount)?t.couplerFiberCount:2;return [...Array.from({length:n},(r,i)=>({id:`left-${i+1}`,label:`${i+1}`,role:"coupler",medium:"fiber"})),...Array.from({length:n},(r,i)=>({id:`right-${i+1}`,label:`${i+1}`,role:"coupler",medium:"fiber"}))]}if(e.type==="fibre-split"){let n=Math.max(1,Math.min(2,Number(t.inputCount)||1)),r=Math.max(1,Math.min(64,Number(t.outputCount)||8));return [...Array.from({length:n},(i,a)=>({id:`input-${a+1}`,label:`Input ${a+1}`,role:"splitter",medium:"fiber"})),...Array.from({length:r},(i,a)=>({id:`output-${a+1}`,label:`Output ${a+1}`,role:"splitter",medium:"fiber"}))]}return []}function lt(e){let t=e.type==="vertical-pdu"||Array.isArray(e.data?.ports)||Array.isArray(e.data?.portGroups)||Array.isArray(e.data?.rearPortGroups)||Array.isArray(e.data?.pduPortGroups)?dt(e):pt(e);return t.length>0?t:void 0}function ft(e,t){if(!t||t.length===0)return t;let n=new Map((e??[]).map(r=>[r.id,r]));return t.map(r=>{let i=n.get(r.id);return i?{...i,...r,compatibility:r.compatibility??i.compatibility,layout:r.layout??i.layout}:r})}function mt(e,t){return e?{...e,...t,placement:t.placement,render:t.render,config:t.config,externalRefs:t.externalRefs,metadata:t.metadata??e.metadata,endpoints:ft(e.endpoints,t.endpoints)}:t}function bt(e,t){let n=new Map((t?.nodes??[]).map(o=>[o.id,o])),r=e.nodes.map(o=>mt(n.get(o.id),{id:o.id,...tt(o),label:o.data?.label??o.id,placement:nt(o),render:ot(o),config:at(o),externalRefs:it(o),metadata:n.get(o.id)?.metadata,endpoints:lt(o)})),i=new Map((t?.edges??[]).map(o=>[o.id,o])),a=e.edges.map(o=>{let s={id:o.id,source:{nodeId:o.source,endpointId:o.sourceHandle?p(o.sourceHandle):void 0},target:{nodeId:o.target,endpointId:o.targetHandle?p(o.targetHandle):void 0},type:o.type,hidden:o.hidden===true?true:void 0,data:o.data?{...o.data}:void 0,metadata:i.get(o.id)?.metadata},c=i.get(o.id);return c?{...c,...s}:s});return {nodes:r,edges:a,ui:st(e.nodes,t?.ui),metadata:t?.metadata}}function Pt(e){let t=new Map,n=r=>{if(!r.endpointId)return;let i=`${r.nodeId}:${p(r.endpointId)}`;t.set(i,(t.get(i)??0)+1);};return e.forEach(r=>{n(r.source),n(r.target);}),t}function ne(e){if(e.type)return e.type;if(e.role==="power"||e.medium==="power"){let t=String(e.connector??"").toLowerCase();return t.includes("c19")||t.includes("c20")?"power_c19":t.includes("c13")||t.includes("c14")?"power_c13":"power_ac"}return e.medium==="fiber"?"fiber":(e.medium==="ethernet","ethernet")}function yt(e,t,n){return e.map(r=>({id:r.id,label:r.label,face:r.face==="rear"?"rear":r.face==="front"?"front":void 0,group:r.group,connected:(t.get(`${n}:${p(r.id)}`)??0)>0,type:ne(r),cableType:r.cableType,connectorType:r.connector,compatibility:r.compatibility&&r.compatibility.length>0?[...r.compatibility]:void 0,position:N(r.layout)}))}function gt(e,t,n){let r=new Map;return e.forEach(i=>{let a=i.group??"Ports",o=r.get(a),s={id:i.id,label:i.label,connected:(t.get(`${n}:${p(i.id)}`)??0)>0,type:ne(i),cableType:i.cableType,connectorType:i.connector,compatibility:i.compatibility&&i.compatibility.length>0?[...i.compatibility]:void 0,position:N(i.layout)};if(o){o.push(s);return}r.set(a,[s]);}),[...r.entries()].map(([i,a])=>({name:i,ports:a.map(o=>({id:o.id,label:o.label,connected:o.connected??false,type:o.type,cableType:o.cableType,connectorType:o.connectorType,compatibility:o.compatibility,position:N(o.position)}))}))}function ht(e,t,n){if(!t&&!n)return e;let r={...e};t&&(M.forEach(a=>{let o=t[a];o!==void 0&&(r[a]=o);}),t.images&&v.forEach(a=>{let o=t.images?.[a];o!==void 0&&(r[a]=o);}),t.dimensions&&L.forEach(a=>{let o=t.dimensions?.[a];typeof o=="number"&&(r[a]=o);}));let i=n?.face;return i&&(r.face=i),n?.handleSideMap&&(r.handleSideMap={...n.handleSideMap}),r}function Et(e,t){if(!t||t.length===0)return e;let n={...e};return t.forEach(r=>{r.source==="netbox"&&(n.netboxId=r.id,r.type&&(n.netboxType=r.type)),r.source==="inventory"&&(n.inventoryId=r.id,r.type&&(n.inventoryType=r.type));}),n}function te(e,t,n){if(!t)return;let r=p(t);if(!e)return r;if(e.kind==="equipment"){let i=(e.subtype??"").toLowerCase().replace(/_/g,"-"),a=e.endpoints?.find(s=>s.id===r),o=a?.role==="power"||a?.medium==="power";if((i==="switch"||i==="router")&&n==="target"&&!o)return `${r}-target`;if(i==="patch-panel"){let s=a?.face==="rear"?"rear":"front";if(n==="source"&&s==="front")return `${r}-source`;if(n==="target"&&s==="rear")return `${r}-target`}return r}return e.kind==="splitter",r}function Nt(e){let t=Pt(e.edges),n=e.ui?.nodeState??{},r=e.nodes.map(o=>{let s=rt(o),c={label:o.label,...o.config??{}};if(c=ht(c,o.render,n[o.id]),c=Et(c,o.externalRefs),s==="device"||s==="server"||s==="switch"||s==="router")c.deviceType=o.subtype??(s==="device"?"device":s),o.endpoints&&o.endpoints.length>0&&(c.ports=yt(o.endpoints,t,o.id));else if(s==="vertical-pdu"){let u=(o.endpoints??[]).filter(f=>f.face!=="front");u.length>0&&(c.pduPortGroups=gt(u,t,o.id),c.powerPorts=u.length);}return o.placement.rack&&(o.placement.rack.rackLayout&&(c.rackLayout=o.placement.rack.rackLayout),typeof o.placement.rack.rackWidthPx=="number"&&(c.rackWidthPx=o.placement.rack.rackWidthPx),typeof o.placement.rack.rackFaceGapPx=="number"&&(c.rackFaceGapPx=o.placement.rack.rackFaceGapPx)),s!=="rack"&&o.placement.rack&&(c.isRackMounted=true,typeof o.placement.rack.uPosition=="number"&&(c.uPosition=o.placement.rack.uPosition),o.placement.rack.mountMode&&(c.mountMode=o.placement.rack.mountMode)),o.placement.container&&(typeof o.placement.container.slotPosition=="number"&&(c.slotPosition=o.placement.container.slotPosition),typeof o.placement.container.holderPosition=="number"&&(c.holderPosition=o.placement.container.holderPosition)),{id:o.id,type:s,position:re(o.placement.position),...o.placement.parentId?{parentId:o.placement.parentId}:{},...o.placement.extent?{extent:o.placement.extent}:{},data:c}}),i=new Map(e.nodes.map(o=>[o.id,o])),a=e.edges.map(o=>({id:o.id,source:o.source.nodeId,target:o.target.nodeId,...o.source.endpointId?{sourceHandle:te(i.get(o.source.nodeId),o.source.endpointId,"source")}:{},...o.target.endpointId?{targetHandle:te(i.get(o.target.nodeId),o.target.endpointId,"target")}:{},...o.type?{type:o.type}:{},...o.hidden===true?{hidden:true}:{},...o.data?{data:{...o.data}}:{}}));return {nodes:r,edges:a}}function kt(e,t,n={}){let r=n.direction??"horizontal",i=n.spacing??220,a=n.origin??{x:0,y:0},o=e.map((c,u)=>({id:c.id,type:c.type??"fiber",position:r==="vertical"?{x:a.x,y:a.y+u*i}:{x:a.x+u*i,y:a.y},data:c.data??{label:c.id}})),s=t.map(c=>({id:c.id,source:c.source,target:c.target,type:c.type??"fiber",data:c.data}));return {nodes:o,edges:s}}var Dt=(e,t)=>t?p(t):"";function $(e,t){if(!e||!t)return "other";let{shape:n}=T(t);switch(e.type){case "tube":return n.kind==="fiber"?"tube-fiber":n.kind==="tube"?"tube-tube":"other";case "cable":return n.kind==="fiber"?"tube-fiber":n.kind==="cable-button"?"cable-button":"other";case "multi-tube-cable":return n.kind==="tube"?"cable-tube":n.kind==="cable-button"?"cable-button":"other";case "splice":return n.kind==="splice-side"?"splice-side":"other";case "coupler":return n.kind==="side-port"?"coupler-port":"other";case "fibre-split":return n.kind==="split-io"?n.io==="input"?"splitter-input":"splitter-output":"other";default:return "other"}}var Tt=new Set(["tube-fiber>splice-side","splice-side>tube-fiber","splice-side>coupler-port","coupler-port>splice-side","splice-side>splitter-input","splice-side>splitter-output","splitter-output>splice-side","coupler-port>coupler-port","coupler-port>splitter-input","splitter-input>coupler-port","coupler-port>splitter-output","splitter-output>coupler-port","splitter-output>tube-fiber","tube-fiber>splitter-output","cable-button>cable-button","cable-tube>tube-tube","tube-tube>cable-tube"]);function oe(e){return e==="tube-tube"||e==="cable-tube"}function xt(e,t,n,r){if(e?.type==="splice"&&n?.type==="splice"){let o=e.data?.mode==="ribbon",s=n.data?.mode==="ribbon";if(o!==s)return {valid:false,reason:"Ribbon splices can only connect to other ribbon splices"}}let i=$(e,t),a=$(n,r);return Tt.has(`${i}>${a}`)?{valid:true}:oe(i)&&oe(a)?{valid:false,reason:"Tube handles can only connect between cable and tube nodes"}:{valid:false,reason:"Connection not allowed by fiber rules"}}export{ae as applyDirectionalPortHandleId,ge as applyFaceAliasHandleId,j as buildCouplerHandleId,B as buildDevicePortGroupsFromPorts,me as buildFiberHandleId,kt as buildPathLayoutGraph,D as buildPortHandleId,ve as buildReactNetworksHandleId,qe as buildReactNetworksPortGroupsFromPorts,Pe as buildSpliceHandleId,ye as buildSplitIoHandleId,be as buildTubeHandleId,$ as classifyFiberEndpoint,Z as describePortHandle,Nt as diagramDocumentToNetworkGraph,xt as evaluateFiberConnectionRule,Ye as flattenReactNetworksPortGroups,Ee as getOppositeSideHandleId,l as isPowerPortType,Q as listPossiblePortHandleIds,W as looksLikePowerHandleId,bt as networkGraphToDiagramDocument,O as normalizeDevicePorts,k as normalizeDirectionalHandleId,Dt as normalizeHandle,p as normalizeHandleId,b as normalizePortPositionInput,T as parseHandleId,C as parseSemanticPortPosition,Ve as resolveDevicePortBlocksFromFlatPorts,I as resolveDevicePortGroup,J as resolveNodeDevicePorts,je as resolveReactNetworksNodePorts,We as resolveReactNetworksPortGroup,he as sanitizeHandleIdForMatching,Xe as validatePortHandleOnNodeData};//# sourceMappingURL=model.mjs.map
|
|
2
|
+
//# sourceMappingURL=model.mjs.map
|