!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).GraphLib={})}(this,(function(e){"use strict";var t=function(){function e(){this._events={}}return e.prototype.on=function(e,t,s){return this._events[e]||(this._events[e]=[]),this._events[e].push({callback:t,once:!!s}),this},e.prototype.once=function(e,t){return this.on(e,t,!0)},e.prototype.emit=function(e){for(var t=this,s=[],d=1;d{t.has(s.id)||(t.add(s.id),e.push(s))}))}return!1}function d(e,t,s,r){if(s(e))return!0;t.add(e.id);for(const a of r(e.id))if(!t.has(a.id)&&d(a,t,s,r))return!0;return!1}const r=()=>!0;class a{graph;nodeFilter;edgeFilter;cacheEnabled;inEdgesMap=new Map;outEdgesMap=new Map;bothEdgesMap=new Map;allNodesMap=new Map;allEdgesMap=new Map;constructor(e){this.graph=e.graph;const t=e.nodeFilter||r,s=e.edgeFilter||r;this.nodeFilter=t,this.edgeFilter=e=>{const{source:d,target:r}=this.graph.getEdgeDetail(e.id);return!(!t(d)||!t(r))&&s(e,d,r)},"auto"===e.cache?(this.cacheEnabled=!0,this.startAutoCache()):"manual"===e.cache?this.cacheEnabled=!0:this.cacheEnabled=!1}clearCache=()=>{this.inEdgesMap.clear(),this.outEdgesMap.clear(),this.bothEdgesMap.clear(),this.allNodesMap.clear(),this.allEdgesMap.clear()};refreshCache=()=>{this.clearCache(),this.updateCache(this.graph.getAllNodes().map((e=>e.id)))};updateCache=e=>{const t=new Set;e.forEach((e=>{const s=this.bothEdgesMap.get(e);if(s&&s.forEach((e=>t.add(e.id))),this.hasNode(e)){const s=this.graph.getRelatedEdges(e,"in").filter(this.edgeFilter),d=this.graph.getRelatedEdges(e,"out").filter(this.edgeFilter),r=Array.from(new Set([...s,...d]));r.forEach((e=>t.add(e.id))),this.inEdgesMap.set(e,s),this.outEdgesMap.set(e,d),this.bothEdgesMap.set(e,r),this.allNodesMap.set(e,this.graph.getNode(e))}else this.inEdgesMap.delete(e),this.outEdgesMap.delete(e),this.bothEdgesMap.delete(e),this.allNodesMap.delete(e)})),t.forEach((e=>{this.hasEdge(e)?this.allEdgesMap.set(e,this.graph.getEdge(e)):this.allEdgesMap.delete(e)}))};startAutoCache(){this.refreshCache(),this.graph.on("changed",this.handleGraphChanged)}stopAutoCache(){this.graph.off("changed",this.handleGraphChanged)}handleGraphChanged=e=>{const t=new Set;e.changes.forEach((s=>{switch(s.type){case"NodeAdded":case"NodeRemoved":t.add(s.value.id);break;case"NodeDataUpdated":t.add(s.id);break;case"EdgeAdded":case"EdgeRemoved":t.add(s.value.source),t.add(s.value.target);break;case"EdgeUpdated":"source"!==s.propertyName&&"target"!==s.propertyName||(t.add(s.oldValue),t.add(s.newValue));break;case"EdgeDataUpdated":if(e.graph.hasEdge(s.id)){const d=e.graph.getEdge(s.id);t.add(d.source),t.add(d.target)}}})),this.updateCache(t)};checkNodeExistence(e){this.getNode(e)}hasNode(e){if(!this.graph.hasNode(e))return!1;const t=this.graph.getNode(e);return this.nodeFilter(t)}areNeighbors(e,t){return this.checkNodeExistence(e),this.getNeighbors(t).some((t=>t.id===e))}getNode(e){const t=this.graph.getNode(e);if(!this.nodeFilter(t))throw new Error("Node not found for id: "+e);return t}getRelatedEdges(e,t){if(this.checkNodeExistence(e),this.cacheEnabled)return"in"===t?this.inEdgesMap.get(e):"out"===t?this.outEdgesMap.get(e):this.bothEdgesMap.get(e);return this.graph.getRelatedEdges(e,t).filter(this.edgeFilter)}getDegree(e,t){return this.getRelatedEdges(e,t).length}getSuccessors(e){const t=this.getRelatedEdges(e,"out").map((e=>this.getNode(e.target)));return Array.from(new Set(t))}getPredecessors(e){const t=this.getRelatedEdges(e,"in").map((e=>this.getNode(e.source)));return Array.from(new Set(t))}getNeighbors(e){const t=this.getPredecessors(e),s=this.getSuccessors(e);return Array.from(new Set([...t,...s]))}hasEdge(e){if(!this.graph.hasEdge(e))return!1;const t=this.graph.getEdge(e);return this.edgeFilter(t)}getEdge(e){const t=this.graph.getEdge(e);if(!this.edgeFilter(t))throw new Error("Edge not found for id: "+e);return t}getEdgeDetail(e){const t=this.getEdge(e);return{edge:t,source:this.getNode(t.source),target:this.getNode(t.target)}}hasTreeStructure(e){return this.graph.hasTreeStructure(e)}getRoots(e){return this.graph.getRoots(e).filter(this.nodeFilter)}getChildren(e,t){return this.checkNodeExistence(e),this.graph.getChildren(e,t).filter(this.nodeFilter)}getParent(e,t){this.checkNodeExistence(e);const s=this.graph.getParent(e,t);return s&&this.nodeFilter(s)?s:null}getAllNodes(){return this.cacheEnabled?Array.from(this.allNodesMap.values()):this.graph.getAllNodes().filter(this.nodeFilter)}getAllEdges(){return this.cacheEnabled?Array.from(this.allEdgesMap.values()):this.graph.getAllEdges().filter(this.edgeFilter)}bfs(e,t,d="out"){const r={in:this.getPredecessors.bind(this),out:this.getSuccessors.bind(this),both:this.getNeighbors.bind(this)}[d];s([this.getNode(e)],new Set,t,r)}dfs(e,t,s="out"){const r={in:this.getPredecessors.bind(this),out:this.getSuccessors.bind(this),both:this.getNeighbors.bind(this)}[s];d(this.getNode(e),new Set,t,r)}}class h extends t{nodeMap=new Map;edgeMap=new Map;inEdgesMap=new Map;outEdgesMap=new Map;bothEdgesMap=new Map;treeIndices=new Map;changes=[];batchCount=0;onChanged=()=>{};constructor(e){super(),e&&(e.nodes&&this.addNodes(e.nodes),e.edges&&this.addEdges(e.edges),e.tree&&this.addTree(e.tree),e.onChanged&&(this.onChanged=e.onChanged))}batch=e=>{this.batchCount+=1,e(),this.batchCount-=1,this.batchCount||this.commit()};commit(){const e=this.changes;this.changes=[];const t={graph:this,changes:e};this.emit("changed",t),this.onChanged(t)}reduceChanges(e){let t=[];return e.forEach((e=>{switch(e.type){case"NodeRemoved":{let s=!1;t=t.filter((t=>{if("NodeAdded"===t.type){const d=t.value.id===e.value.id;return d&&(s=!0),!d}return"NodeDataUpdated"===t.type?t.id!==e.value.id:"TreeStructureChanged"!==t.type||t.nodeId!==e.value.id})),s||t.push(e);break}case"EdgeRemoved":{let s=!1;t=t.filter((t=>{if("EdgeAdded"===t.type){const d=t.value.id===e.value.id;return d&&(s=!0),!d}return"EdgeDataUpdated"!==t.type&&"EdgeUpdated"!==t.type||t.id!==e.value.id})),s||t.push(e);break}case"NodeDataUpdated":case"EdgeDataUpdated":case"EdgeUpdated":{const s=t.findIndex((t=>t.type===e.type&&t.id===e.id&&(void 0===e.propertyName||t.propertyName===e.propertyName))),d=t[s];d?void 0!==e.propertyName?d.newValue=e.newValue:(t.splice(s,1),t.push(e)):t.push(e);break}case"TreeStructureDetached":t=t.filter((t=>"TreeStructureAttached"===t.type?t.treeKey!==e.treeKey:"TreeStructureChanged"!==t.type||t.treeKey!==e.treeKey)),t.push(e);break;case"TreeStructureChanged":{const s=t.find((t=>"TreeStructureChanged"===t.type&&t.treeKey===e.treeKey&&t.nodeId===e.nodeId));s?s.newParentId=e.newParentId:t.push(e);break}default:t.push(e)}})),t}checkNodeExistence(e){this.getNode(e)}hasNode(e){return this.nodeMap.has(e)}areNeighbors(e,t){return this.getNeighbors(t).some((t=>t.id===e))}getNode(e){const t=this.nodeMap.get(e);if(!t)throw new Error("Node not found for id: "+e);return t}getRelatedEdges(e,t){if(this.checkNodeExistence(e),"in"===t){const t=this.inEdgesMap.get(e);return Array.from(t)}if("out"===t){const t=this.outEdgesMap.get(e);return Array.from(t)}{const t=this.bothEdgesMap.get(e);return Array.from(t)}}getDegree(e,t){return this.getRelatedEdges(e,t).length}getSuccessors(e){const t=this.getRelatedEdges(e,"out").map((e=>this.getNode(e.target)));return Array.from(new Set(t))}getPredecessors(e){const t=this.getRelatedEdges(e,"in").map((e=>this.getNode(e.source)));return Array.from(new Set(t))}getNeighbors(e){const t=this.getPredecessors(e),s=this.getSuccessors(e);return Array.from(new Set([...t,...s]))}doAddNode(e){if(this.hasNode(e.id))throw new Error("Node already exists: "+e.id);this.nodeMap.set(e.id,e),this.inEdgesMap.set(e.id,new Set),this.outEdgesMap.set(e.id,new Set),this.bothEdgesMap.set(e.id,new Set),this.treeIndices.forEach((t=>{t.childrenMap.set(e.id,new Set)})),this.changes.push({type:"NodeAdded",value:e})}addNodes(e){this.batch((()=>{for(const t of e)this.doAddNode(t)}))}addNode(e){this.addNodes([e])}doRemoveNode(e){const t=this.getNode(e),s=this.bothEdgesMap.get(e);s?.forEach((e=>this.doRemoveEdge(e.id))),this.nodeMap.delete(e),this.treeIndices.forEach((s=>{s.childrenMap.get(e)?.forEach((e=>{s.parentMap.delete(e.id)}));const d=s.parentMap.get(e);d&&s.childrenMap.get(d.id)?.delete(t),s.parentMap.delete(e),s.childrenMap.delete(e)})),this.bothEdgesMap.delete(e),this.inEdgesMap.delete(e),this.outEdgesMap.delete(e),this.changes.push({type:"NodeRemoved",value:t})}removeNodes(e){this.batch((()=>{e.forEach((e=>this.doRemoveNode(e)))}))}removeNode(e){this.removeNodes([e])}updateNodeDataProperty(e,t,s){const d=this.getNode(e);this.batch((()=>{const r=d.data[t],a=s;d.data[t]=a,this.changes.push({type:"NodeDataUpdated",id:e,propertyName:t,oldValue:r,newValue:a})}))}mergeNodeData(e,t){this.batch((()=>{Object.entries(t).forEach((([t,s])=>{this.updateNodeDataProperty(e,t,s)}))}))}updateNodeData(...e){const t=e[0],s=this.getNode(t);if("string"==typeof e[1])return void this.updateNodeDataProperty(t,e[1],e[2]);let d;if("function"==typeof e[1]){const t=e[1];d=t(s.data)}else"object"==typeof e[1]&&(d=e[1]);this.batch((()=>{const e=s.data,r=d;s.data=d,this.changes.push({type:"NodeDataUpdated",id:t,oldValue:e,newValue:r})}))}checkEdgeExistence(e){if(!this.hasEdge(e))throw new Error("Edge not found for id: "+e)}hasEdge(e){return this.edgeMap.has(e)}getEdge(e){return this.checkEdgeExistence(e),this.edgeMap.get(e)}getEdgeDetail(e){const t=this.getEdge(e);return{edge:t,source:this.getNode(t.source),target:this.getNode(t.target)}}doAddEdge(e){if(this.hasEdge(e.id))throw new Error("Edge already exists: "+e.id);this.checkNodeExistence(e.source),this.checkNodeExistence(e.target),this.edgeMap.set(e.id,e);const t=this.inEdgesMap.get(e.target),s=this.outEdgesMap.get(e.source),d=this.bothEdgesMap.get(e.source),r=this.bothEdgesMap.get(e.target);t.add(e),s.add(e),d.add(e),r.add(e),this.changes.push({type:"EdgeAdded",value:e})}addEdges(e){this.batch((()=>{for(const t of e)this.doAddEdge(t)}))}addEdge(e){this.addEdges([e])}doRemoveEdge(e){const t=this.getEdge(e),s=this.outEdgesMap.get(t.source),d=this.inEdgesMap.get(t.target),r=this.bothEdgesMap.get(t.source),a=this.bothEdgesMap.get(t.target);s.delete(t),d.delete(t),r.delete(t),a.delete(t),this.edgeMap.delete(e),this.changes.push({type:"EdgeRemoved",value:t})}removeEdges(e){this.batch((()=>{e.forEach((e=>this.doRemoveEdge(e)))}))}removeEdge(e){this.removeEdges([e])}updateEdgeSource(e,t){const s=this.getEdge(e);this.checkNodeExistence(t);const d=s.source,r=t;this.outEdgesMap.get(d).delete(s),this.bothEdgesMap.get(d).delete(s),this.outEdgesMap.get(r).add(s),this.bothEdgesMap.get(r).add(s),s.source=t,this.batch((()=>{this.changes.push({type:"EdgeUpdated",id:e,propertyName:"source",oldValue:d,newValue:r})}))}updateEdgeTarget(e,t){const s=this.getEdge(e);this.checkNodeExistence(t);const d=s.target,r=t;this.inEdgesMap.get(d).delete(s),this.bothEdgesMap.get(d).delete(s),this.inEdgesMap.get(r).add(s),this.bothEdgesMap.get(r).add(s),s.target=t,this.batch((()=>{this.changes.push({type:"EdgeUpdated",id:e,propertyName:"target",oldValue:d,newValue:r})}))}updateEdgeDataProperty(e,t,s){const d=this.getEdge(e);this.batch((()=>{const r=d.data[t],a=s;d.data[t]=a,this.changes.push({type:"EdgeDataUpdated",id:e,propertyName:t,oldValue:r,newValue:a})}))}updateEdgeData(...e){const t=e[0],s=this.getEdge(t);if("string"==typeof e[1])return void this.updateEdgeDataProperty(t,e[1],e[2]);let d;if("function"==typeof e[1]){const t=e[1];d=t(s.data)}else"object"==typeof e[1]&&(d=e[1]);this.batch((()=>{const e=s.data,r=d;s.data=d,this.changes.push({type:"EdgeDataUpdated",id:t,oldValue:e,newValue:r})}))}mergeEdgeData(e,t){this.batch((()=>{Object.entries(t).forEach((([t,s])=>{this.updateEdgeDataProperty(e,t,s)}))}))}checkTreeExistence(e){if(!this.hasTreeStructure(e))throw new Error("Tree structure not found for treeKey: "+e)}hasTreeStructure(e){return this.treeIndices.has(e)}attachTreeStructure(e){this.treeIndices.has(e)||(this.treeIndices.set(e,{parentMap:new Map,childrenMap:new Map}),this.batch((()=>{this.changes.push({type:"TreeStructureAttached",treeKey:e})})))}detachTreeStructure(e){this.checkTreeExistence(e),this.treeIndices.delete(e),this.batch((()=>{this.changes.push({type:"TreeStructureDetached",treeKey:e})}))}addTree(e,t){this.batch((()=>{this.attachTreeStructure(t);const s=[],d=Array.isArray(e)?e:[e];for(;d.length;){const e=d.shift();s.push(e),e.children&&d.push(...e.children)}this.addNodes(s),s.forEach((e=>{e.children?.forEach((s=>{this.setParent(s.id,e.id,t)}))}))}))}getRoots(e){return this.checkTreeExistence(e),this.getAllNodes().filter((t=>!this.getParent(t.id,e)))}getChildren(e,t){this.checkNodeExistence(e),this.checkTreeExistence(t);const s=this.treeIndices.get(t).childrenMap.get(e);return Array.from(s||[])}getParent(e,t){this.checkNodeExistence(e),this.checkTreeExistence(t);return this.treeIndices.get(t).parentMap.get(e)||null}getAncestors(e,t){const s=[];let d,r=this.getNode(e);for(;d=this.getParent(r.id,t);)s.push(d),r=d;return s}setParent(e,t,s){this.checkTreeExistence(s);const d=this.treeIndices.get(s);if(!d)return;const r=this.getNode(e),a=d.parentMap.get(e);if(a?.id===t)return;if(null==t)return a&&d.childrenMap.get(a.id)?.delete(r),void d.parentMap.delete(e);const h=this.getNode(t);d.parentMap.set(e,h),a&&d.childrenMap.get(a.id)?.delete(r);let i=d.childrenMap.get(h.id);i||(i=new Set,d.childrenMap.set(h.id,i)),i.add(r),this.batch((()=>{this.changes.push({type:"TreeStructureChanged",treeKey:s,nodeId:e,oldParentId:a?.id,newParentId:h.id})}))}dfsTree(e,t,s){return d(this.getNode(e),new Set,t,(e=>this.getChildren(e,s)))}bfsTree(e,t,d){return s([this.getNode(e)],new Set,t,(e=>this.getChildren(e,d)))}getAllNodes(){return Array.from(this.nodeMap.values())}getAllEdges(){return Array.from(this.edgeMap.values())}bfs(e,t,d="out"){const r={in:this.getPredecessors.bind(this),out:this.getSuccessors.bind(this),both:this.getNeighbors.bind(this)}[d];return s([this.getNode(e)],new Set,t,r)}dfs(e,t,s="out"){const r={in:this.getPredecessors.bind(this),out:this.getSuccessors.bind(this),both:this.getNeighbors.bind(this)}[s];return d(this.getNode(e),new Set,t,r)}clone(){const e=this.getAllNodes().map((e=>({...e,data:{...e.data}}))),t=this.getAllEdges().map((e=>({...e,data:{...e.data}}))),s=new h({nodes:e,edges:t});return this.treeIndices.forEach((({parentMap:e,childrenMap:t},d)=>{const r=new Map;e.forEach(((e,t)=>{r.set(t,s.getNode(e.id))}));const a=new Map;t.forEach(((e,t)=>{a.set(t,new Set(Array.from(e).map((e=>s.getNode(e.id)))))})),s.treeIndices.set(d,{parentMap:r,childrenMap:a})})),s}toJSON(){return JSON.stringify({nodes:this.getAllNodes(),edges:this.getAllEdges()})}createView(e){return new a({graph:this,...e})}}e.Graph=h,e.GraphView=a,Object.defineProperty(e,"__esModule",{value:!0})}));