From 4863d0d29d782e3e6a37562eae0af523c6c15d4c Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Sat, 3 Dec 2022 09:47:01 +0100 Subject: [PATCH] Adding new flowchart renderer using elk --- cSpell.json | 1 + cypress/platform/knsv2.html | 15 +- packages/mermaid-flowchart-v3/package.json | 2 + .../src/flowRenderer-v3-cyto.js | 756 ++++++++++++++++++ .../src/flowRenderer-v3.js | 278 +++---- packages/mermaid/src/dagre-wrapper/markers.js | 2 +- .../mermaid/src/diagrams/flowchart/flowDb.js | 2 +- .../src/diagrams/flowchart/flowRenderer-v2.js | 2 +- pnpm-lock.yaml | 34 +- 9 files changed, 923 insertions(+), 169 deletions(-) create mode 100644 packages/mermaid-flowchart-v3/src/flowRenderer-v3-cyto.js diff --git a/cSpell.json b/cSpell.json index 64187e1ca..b692c1ebf 100644 --- a/cSpell.json +++ b/cSpell.json @@ -30,6 +30,7 @@ "doku", "dompurify", "edgechromium", + "elkjs", "faber", "flatmap", "ftplugin", diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index b5b8eea5e..d55a23bc9 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -57,17 +57,10 @@
Security check
 cyto LR
-      subgraph test
-        inside1 --> inside2
-      end
-      subgraph test2
-        inside3 --> inside4
-      end
-      out
-
- a(letter a
a) ---> b(letter b)--> c(letter c) --> d -->e(letter e
e) --> a b <--> - d(letter b
d) + inside1 --> inside2 & inside3 & inside4 & inside5 & inside6 + a(letter a
a) ---> b(letter b)--> c(letter c) --> d -->e(letter e
e) --> a + b <--> d(letter b
d) +
       mindmap
   root
diff --git a/packages/mermaid-flowchart-v3/package.json b/packages/mermaid-flowchart-v3/package.json
index 7aefb890e..dee91094e 100644
--- a/packages/mermaid-flowchart-v3/package.json
+++ b/packages/mermaid-flowchart-v3/package.json
@@ -44,6 +44,8 @@
     "cytoscape-fcose": "^2.1.0",
     "graphlib": "^2.1.0",
     "dagre-d3-es": "7.0.4",
+    "cytoscape-dagre": "^2.1.0",
+    "elkjs": "^0.8.2",
     "d3": "^7.0.0",
     "khroma": "^2.0.0",
     "non-layered-tidy-tree-layout": "^2.0.2"
diff --git a/packages/mermaid-flowchart-v3/src/flowRenderer-v3-cyto.js b/packages/mermaid-flowchart-v3/src/flowRenderer-v3-cyto.js
new file mode 100644
index 000000000..2a0d0fc23
--- /dev/null
+++ b/packages/mermaid-flowchart-v3/src/flowRenderer-v3-cyto.js
@@ -0,0 +1,756 @@
+import graphlib from 'graphlib';
+import { select, line, curveLinear, curveCardinal, curveBasis, selectAll } from 'd3';
+import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
+import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js';
+import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js';
+import dagre from 'cytoscape-dagre';
+
+// Replace with other function to avoid dependency to dagre-d3
+import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
+
+import common, { evaluate } from '../../mermaid/src/diagrams/common/common';
+import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils';
+
+import cytoscape from 'cytoscape';
+cytoscape.use(dagre);
+
+const conf = {};
+export const setConf = function (cnf) {
+  const keys = Object.keys(cnf);
+  for (const key of keys) {
+    conf[key] = cnf[key];
+  }
+};
+
+// /**
+//  * Function that adds the vertices found during parsing to the graph to be rendered.
+//  *
+//  * @param vert Object containing the vertices.
+//  * @param g The graph that is to be drawn.
+//  * @param svgId
+//  * @param root
+//  * @param doc
+//  * @param diagObj
+//  */
+export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookUpDb, graph) {
+  const svg = root.select(`[id="${svgId}"]`);
+  const nodes = svg.insert('g').attr('class', 'nodes');
+  const keys = Object.keys(vert);
+
+  // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
+  keys.forEach(function (id) {
+    const vertex = vert[id];
+
+    /**
+     * Variable for storing the classes for the vertex
+     *
+     * @type {string}
+     */
+    let classStr = 'default';
+    if (vertex.classes.length > 0) {
+      classStr = vertex.classes.join(' ');
+    }
+
+    const styles = getStylesFromArray(vertex.styles);
+
+    // Use vertex id as text in the box if no text is provided by the graph definition
+    let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
+
+    // We create a SVG label, either by delegating to addHtmlLabel or manually
+    let vertexNode;
+    if (evaluate(getConfig().flowchart.htmlLabels)) {
+      // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
+      const node = {
+        label: vertexText.replace(
+          /fa[blrs]?:fa-[\w-]+/g,
+          (s) => ``
+        ),
+      };
+      vertexNode = addHtmlLabel(svg, node).node();
+      vertexNode.parentNode.removeChild(vertexNode);
+    } else {
+      const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
+      svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
+
+      const rows = vertexText.split(common.lineBreakRegex);
+
+      for (const row of rows) {
+        const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
+        tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
+        tspan.setAttribute('dy', '1em');
+        tspan.setAttribute('x', '1');
+        tspan.textContent = row;
+        svgLabel.appendChild(tspan);
+      }
+      vertexNode = svgLabel;
+    }
+
+    let radious = 0;
+    let _shape = '';
+    // Set the shape based parameters
+    switch (vertex.type) {
+      case 'round':
+        radious = 5;
+        _shape = 'rect';
+        break;
+      case 'square':
+        _shape = 'rect';
+        break;
+      case 'diamond':
+        _shape = 'question';
+        break;
+      case 'hexagon':
+        _shape = 'hexagon';
+        break;
+      case 'odd':
+        _shape = 'rect_left_inv_arrow';
+        break;
+      case 'lean_right':
+        _shape = 'lean_right';
+        break;
+      case 'lean_left':
+        _shape = 'lean_left';
+        break;
+      case 'trapezoid':
+        _shape = 'trapezoid';
+        break;
+      case 'inv_trapezoid':
+        _shape = 'inv_trapezoid';
+        break;
+      case 'odd_right':
+        _shape = 'rect_left_inv_arrow';
+        break;
+      case 'circle':
+        _shape = 'circle';
+        break;
+      case 'ellipse':
+        _shape = 'ellipse';
+        break;
+      case 'stadium':
+        _shape = 'stadium';
+        break;
+      case 'subroutine':
+        _shape = 'subroutine';
+        break;
+      case 'cylinder':
+        _shape = 'cylinder';
+        break;
+      case 'group':
+        _shape = 'rect';
+        break;
+      case 'doublecircle':
+        _shape = 'doublecircle';
+        break;
+      default:
+        _shape = 'rect';
+    }
+    // // Add the node
+    const node = {
+      labelStyle: styles.labelStyle,
+      shape: _shape,
+      labelText: vertexText,
+      rx: radious,
+      ry: radious,
+      class: classStr,
+      style: styles.style,
+      id: vertex.id,
+      link: vertex.link,
+      linkTarget: vertex.linkTarget,
+      tooltip: diagObj.db.getTooltip(vertex.id) || '',
+      domId: diagObj.db.lookUpDomId(vertex.id),
+      haveCallback: vertex.haveCallback,
+      width: vertex.type === 'group' ? 500 : undefined,
+      dir: vertex.dir,
+      type: vertex.type,
+      props: vertex.props,
+      padding: getConfig().flowchart.padding,
+    };
+    const nodeEl = insertNode(nodes, node, vertex.dir);
+    const boundingBox = nodeEl.node().getBBox();
+    const data = {
+      id: vertex.id,
+      labelStyle: styles.labelStyle,
+      shape: _shape,
+      labelText: vertexText,
+      rx: radious,
+      ry: radious,
+      class: classStr,
+      style: styles.style,
+      link: vertex.link,
+      linkTarget: vertex.linkTarget,
+      tooltip: diagObj.db.getTooltip(vertex.id) || '',
+      domId: diagObj.db.lookUpDomId(vertex.id),
+      haveCallback: vertex.haveCallback,
+      width: vertex.type === 'group' ? 500 : undefined,
+      dir: vertex.dir,
+      type: vertex.type,
+      props: vertex.props,
+      padding: getConfig().flowchart.padding,
+      boundingBox,
+      el: nodeEl,
+      parent: parentLookUpDb.parentById[vertex.id],
+    };
+    // if (!Object.keys(parentLookUpDb.childrenById).includes(vertex.id)) {
+    graph.elements.nodes.push({
+      group: 'nodes',
+      // data,
+      data,
+    });
+    // }
+    log.trace('setNode', {
+      labelStyle: styles.labelStyle,
+      shape: _shape,
+      labelText: vertexText,
+      rx: radious,
+      ry: radious,
+      class: classStr,
+      style: styles.style,
+      id: vertex.id,
+      domId: diagObj.db.lookUpDomId(vertex.id),
+      width: vertex.type === 'group' ? 500 : undefined,
+      type: vertex.type,
+      dir: vertex.dir,
+      props: vertex.props,
+      padding: getConfig().flowchart.padding,
+      parent: parentLookUpDb.parentById[vertex.id],
+    });
+  });
+  return graph;
+};
+
+/**
+ * Add edges to graph based on parsed graph definition
+ *
+ * @param {object} edges The edges to add to the graph
+ * @param {object} g The graph object
+ * @param cy
+ * @param diagObj
+ * @param graph
+ */
+export const addEdges = function (edges, diagObj, graph) {
+  // log.info('abc78 edges = ', edges);
+  let cnt = 0;
+  let linkIdCnt = {};
+
+  let defaultStyle;
+  let defaultLabelStyle;
+
+  if (edges.defaultStyle !== undefined) {
+    const defaultStyles = getStylesFromArray(edges.defaultStyle);
+    defaultStyle = defaultStyles.style;
+    defaultLabelStyle = defaultStyles.labelStyle;
+  }
+
+  edges.forEach(function (edge) {
+    cnt++;
+
+    // Identify Link
+    var linkIdBase = 'L-' + edge.start + '-' + edge.end;
+    // count the links from+to the same node to give unique id
+    if (linkIdCnt[linkIdBase] === undefined) {
+      linkIdCnt[linkIdBase] = 0;
+      log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
+    } else {
+      linkIdCnt[linkIdBase]++;
+      log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
+    }
+    let linkId = linkIdBase + '-' + linkIdCnt[linkIdBase];
+    log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
+    var linkNameStart = 'LS-' + edge.start;
+    var linkNameEnd = 'LE-' + edge.end;
+
+    const edgeData = { style: '', labelStyle: '' };
+    edgeData.minlen = edge.length || 1;
+    //edgeData.id = 'id' + cnt;
+
+    // Set link type for rendering
+    if (edge.type === 'arrow_open') {
+      edgeData.arrowhead = 'none';
+    } else {
+      edgeData.arrowhead = 'normal';
+    }
+
+    // Check of arrow types, placed here in order not to break old rendering
+    edgeData.arrowTypeStart = 'arrow_open';
+    edgeData.arrowTypeEnd = 'arrow_open';
+
+    /* eslint-disable no-fallthrough */
+    switch (edge.type) {
+      case 'double_arrow_cross':
+        edgeData.arrowTypeStart = 'arrow_cross';
+      case 'arrow_cross':
+        edgeData.arrowTypeEnd = 'arrow_cross';
+        break;
+      case 'double_arrow_point':
+        edgeData.arrowTypeStart = 'arrow_point';
+      case 'arrow_point':
+        edgeData.arrowTypeEnd = 'arrow_point';
+        break;
+      case 'double_arrow_circle':
+        edgeData.arrowTypeStart = 'arrow_circle';
+      case 'arrow_circle':
+        edgeData.arrowTypeEnd = 'arrow_circle';
+        break;
+    }
+
+    let style = '';
+    let labelStyle = '';
+
+    switch (edge.stroke) {
+      case 'normal':
+        style = 'fill:none;';
+        if (defaultStyle !== undefined) {
+          style = defaultStyle;
+        }
+        if (defaultLabelStyle !== undefined) {
+          labelStyle = defaultLabelStyle;
+        }
+        edgeData.thickness = 'normal';
+        edgeData.pattern = 'solid';
+        break;
+      case 'dotted':
+        edgeData.thickness = 'normal';
+        edgeData.pattern = 'dotted';
+        edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
+        break;
+      case 'thick':
+        edgeData.thickness = 'thick';
+        edgeData.pattern = 'solid';
+        edgeData.style = 'stroke-width: 3.5px;fill:none;';
+        break;
+    }
+    if (edge.style !== undefined) {
+      const styles = getStylesFromArray(edge.style);
+      style = styles.style;
+      labelStyle = styles.labelStyle;
+    }
+
+    edgeData.style = edgeData.style += style;
+    edgeData.labelStyle = edgeData.labelStyle += labelStyle;
+
+    if (edge.interpolate !== undefined) {
+      edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
+    } else if (edges.defaultInterpolate !== undefined) {
+      edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
+    } else {
+      edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
+    }
+
+    if (edge.text === undefined) {
+      if (edge.style !== undefined) {
+        edgeData.arrowheadStyle = 'fill: #333';
+      }
+    } else {
+      edgeData.arrowheadStyle = 'fill: #333';
+      edgeData.labelpos = 'c';
+    }
+
+    edgeData.labelType = 'text';
+    edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
+
+    if (edge.style === undefined) {
+      edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
+    }
+
+    edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
+
+    edgeData.id = linkId;
+    edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
+
+    // Add the edge to the graph
+    graph.elements.edges.push({
+      group: 'edges',
+      data: { source: edge.start, target: edge.end, edgeData, id: cnt },
+    });
+  });
+  return graph;
+};
+
+const addmarkers = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute) {
+  // // TODO: Can we load this config only from the rendered graph type?
+  let url;
+  if (arrowMarkerAbsolute) {
+    url =
+      window.location.protocol +
+      '//' +
+      window.location.host +
+      window.location.pathname +
+      window.location.search;
+    url = url.replace(/\(/g, '\\(');
+    url = url.replace(/\)/g, '\\)');
+  }
+  switch (edgeData.arrowTypeStart) {
+    case 'arrow_cross':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-crossStart' + ')');
+      break;
+    case 'arrow_point':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-pointStart' + ')');
+      break;
+    case 'arrow_barb':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-barbStart' + ')');
+      break;
+    case 'arrow_circle':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-circleStart' + ')');
+      break;
+    case 'aggregation':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-aggregationStart' + ')');
+      break;
+    case 'extension':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-extensionStart' + ')');
+      break;
+    case 'composition':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-compositionStart' + ')');
+      break;
+    case 'dependency':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-dependencyStart' + ')');
+      break;
+    case 'lollipop':
+      svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-lollipopStart' + ')');
+      break;
+    default:
+  }
+  switch (edgeData.arrowTypeEnd) {
+    case 'arrow_cross':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-crossEnd' + ')');
+      break;
+    case 'arrow_point':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-pointEnd' + ')');
+      break;
+    case 'arrow_barb':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-barbEnd' + ')');
+      break;
+    case 'arrow_circle':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-circleEnd' + ')');
+      break;
+    case 'aggregation':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-aggregationEnd' + ')');
+      break;
+    case 'extension':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-extensionEnd' + ')');
+      break;
+    case 'composition':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-compositionEnd' + ')');
+      break;
+    case 'dependency':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-dependencyEnd' + ')');
+      break;
+    case 'lollipop':
+      svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-lollipopEnd' + ')');
+      break;
+    default:
+  }
+};
+
+/**
+ * Returns the all the styles from classDef statements in the graph definition.
+ *
+ * @param text
+ * @param diagObj
+ * @returns {object} ClassDef styles
+ */
+export const getClasses = function (text, diagObj) {
+  log.info('Extracting classes');
+  diagObj.db.clear('ver-2');
+  try {
+    // Parse the graph definition
+    diagObj.parse(text);
+    return diagObj.db.getClasses();
+  } catch (e) {
+    return;
+  }
+};
+
+const addSubGraphs = function (db) {
+  const parentLookUpDb = { parentById: {}, childrenById: {} };
+  const subgraphs = db.getSubGraphs();
+  log.info('Subgraphs - ', subgraphs);
+  subgraphs.forEach(function (subgraph) {
+    subgraph.nodes.forEach(function (node) {
+      parentLookUpDb.parentById[node] = subgraph.id;
+      if (parentLookUpDb.childrenById[subgraph.id] === undefined) {
+        parentLookUpDb.childrenById[subgraph.id] = [];
+      }
+      parentLookUpDb.childrenById[subgraph.id].push(node);
+    });
+  });
+
+  subgraphs.forEach(function (subgraph) {
+    const data = { id: subgraph.id };
+    if (parentLookUpDb.parentById[subgraph.id] !== undefined) {
+      data.parent = parentLookUpDb.parentById[subgraph.id];
+    }
+    // cy.add({
+    //   group: 'nodes',
+    //   data,
+    // });
+  });
+  return parentLookUpDb;
+};
+
+const insertEdge = function (edgesEl, edge, edgeData, bounds, diagObj) {
+  const src = edge.sourceEndpoint();
+  const segments = edge.segmentPoints();
+  // const dest = edge.target().position();
+  const dest = edge.targetEndpoint();
+  const segPoints = segments.map((segment) => [segment.x, segment.y]);
+  const points = [
+    [src.x, src.y],
+    [segments[0].x, segments[0].y],
+    [dest.x, dest.y],
+  ];
+  // console.log('Edge ctrl points:', edge.segmentPoints(), 'Bounds:', bounds, edge.source(), points);
+  // console.log('Edge ctrl points:', points);
+  const curve = line().curve(curveCardinal);
+  const edge2 = edgesEl
+    .insert('path')
+    .attr('d', curve(points))
+    .attr('class', 'path')
+    .attr('fill', 'none');
+  addmarkers(edge2, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute);
+  // edgesEl
+  //   .append('circle')
+  //   .style('stroke', 'red')
+  //   .style('fill', 'red')
+  //   .attr('r', 1)
+  //   .attr('cx', src.x)
+  //   .attr('cy', src.y);
+  // edgesEl
+  //   .append('circle')
+  //   .style('stroke', 'white')
+  //   .style('fill', 'white')
+  //   .attr('r', 1)
+  //   .attr('cx', segments[0].x)
+  //   .attr('cy', segments[0].y);
+  // edgesEl
+  //   .append('circle')
+  //   .style('stroke', 'pink')
+  //   .style('fill', 'pink')
+  //   .attr('r', 1)
+  //   .attr('cx', dest.x)
+  //   .attr('cy', dest.y);
+};
+
+/**
+ * Draws a flowchart in the tag with id: id based on the graph definition in text.
+ *
+ * @param text
+ * @param id
+ */
+
+export const draw = function (text, id, _version, diagObj) {
+  // Add temporary render element
+  diagObj.db.clear();
+  diagObj.db.setGen('gen-2');
+  // Parse the graph definition
+  diagObj.parser.parse(text);
+
+  return new Promise(function (resolve, reject) {
+    const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
+    // .attr('style', 'display:none')
+    let graph = {
+      styleEnabled: true,
+      // animate: false,
+      // ready: function () {
+      //   log.info('Ready', this);
+      // },
+      container: document.getElementById('cy'), // container to render in
+
+      boxSelectionEnabled: false,
+
+      style: [
+        {
+          selector: 'node',
+          css: {
+            content: 'data(id)',
+            'text-valign': 'center',
+            'text-halign': 'center',
+          },
+        },
+        {
+          selector: ':parent',
+          css: {
+            'text-valign': 'top',
+            'text-halign': 'center',
+          },
+        },
+        {
+          selector: 'edge',
+          css: {
+            'curve-style': 'bezier',
+            'target-arrow-shape': 'triangle',
+          },
+        },
+      ],
+
+      elements: {
+        nodes: [
+          { data: { id: 'a', parent: 'b' } },
+          { data: { id: 'b' } },
+          { data: { id: 'c', parent: 'b' } },
+          { data: { id: 'd' } },
+          { data: { id: 'e' } },
+          { data: { id: 'f', parent: 'e' } },
+        ],
+        edges: [
+          { data: { id: 'ad', source: 'a', target: 'd' } },
+          { data: { id: 'eb', source: 'e', target: 'b' } },
+        ],
+      },
+    };
+    log.info('Drawing flowchart using v3 renderer');
+    // Fetch the default direction, use TD if none was found
+    let dir = diagObj.db.getDirection();
+    if (dir === undefined) {
+      dir = 'TD';
+    }
+
+    const { securityLevel, flowchart: conf } = getConfig();
+
+    // Handle root and document for when rendering in sandbox mode
+    let sandboxElement;
+    if (securityLevel === 'sandbox') {
+      sandboxElement = select('#i' + id);
+    }
+    const root =
+      securityLevel === 'sandbox'
+        ? select(sandboxElement.nodes()[0].contentDocument.body)
+        : select('body');
+    const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
+
+    const svg = root.select(`[id="${id}"]`);
+    const markers = ['point', 'circle', 'cross'];
+    insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
+    // Fetch the vertices/nodes and edges/links from the parsed graph definition
+    const vert = diagObj.db.getVertices();
+
+    let subG;
+    const subGraphs = diagObj.db.getSubGraphs();
+    log.info('Subgraphs - ', subGraphs);
+    for (let i = subGraphs.length - 1; i >= 0; i--) {
+      subG = subGraphs[i];
+      log.info('Subgraph - ', subG);
+      diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
+    }
+
+    const parentLookUpDb = addSubGraphs(diagObj.db);
+    graph = addVertices(vert, id, root, doc, diagObj, parentLookUpDb, graph);
+    const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
+    const edges = diagObj.db.getEdges();
+    graph = addEdges(edges, diagObj, graph);
+
+    const cy = cytoscape(graph);
+
+    // c.style();
+    // Make cytoscape care about the dimensions of the nodes
+    cy.nodes().forEach(function (n) {
+      const boundingBox = n.data().boundingBox;
+      if (boundingBox) {
+        n.style('width', boundingBox.width);
+        n.style('height', boundingBox.height);
+      }
+      n.style('shape', 'rectangle');
+      // n.layoutDimensions = () => {
+      //   // console.log('Node dimensions', boundingBox.width, boundingBox.height);
+      //   if (boundingBox) {
+      //     return { w: boundingBox.width, h: boundingBox.height };
+      //   }
+      //   // return { w: boundingBox.width, h: boundingBox.height };
+
+      //   // const data = n.data();
+      //   // return { w: data.width, h: data.height };
+
+      //   return { w: 206, h: 160 };
+      // };
+    });
+
+    cy.layout({
+      // name: 'dagre',
+      // name: 'preset',
+      // name: 'cose',
+      // name: 'circle',
+      name: 'concentric',
+      headless: false,
+      styleEnabled: true,
+      animate: false,
+    }).run();
+
+    // function runLayouts(fit, callBack) {
+    //   // step-1 position child nodes
+    //   var parentNodes = cy.nodes(':parent');
+    //   var grid_layout = parentNodes.descendants().layout({
+    //     name: 'grid',
+    //     cols: 1,
+    //     fit: fit,
+    //   });
+    //   grid_layout.promiseOn('layoutstop').then(function (event) {
+    //     // step-2 position parent nodes
+    //     var dagre_layout = parentNodes.layout({
+    //       name: 'dagre',
+    //       rankDir: 'TB',
+    //       fit: fit,
+    //     });
+    //     dagre_layout.promiseOn('layoutstop').then(function (event) {
+    //       if (callBack) {
+    //         callBack.call(cy, event);
+    //       }
+    //     });
+    //     dagre_layout.run();
+    //   });
+    //   grid_layout.run();
+    // }
+    // runLayouts();
+
+    // log.info('Positions', cy.nodes().positions());
+    // window.cy = cy;
+    cy.ready((e) => {
+      log.info('Ready', e, cy.data());
+      //   // setTimeout(() => {
+      cy.nodes().map((node, id) => {
+        const data = node.data();
+
+        log.info(
+          'Position: (',
+          node.position().x,
+          ', ',
+          node.position().y,
+          ')',
+          data,
+          cy.elements()[0].renderedBoundingBox()
+        );
+        if (data.el) {
+          data.el.attr('transform', `translate(${node.position().x}, ${node.position().y})`);
+          // document
+          //   .querySelector(`[id="${data.domId}"]`)
+          //   .setAttribute('transform', `translate(${node.position().x}, ${node.position().y})`);
+          log.info('Id = ', data.domId, svg.select(`[id="${data.domId}"]`), data.el.node());
+        }
+        // else {
+        //   // console.log('No element found for node', data, node.position(), node.size());
+        // }
+      });
+
+      cy.edges().map((edge, id) => {
+        const data = edge.data();
+        if (edge[0]._private.bodyBounds) {
+          const bounds = edge[0]._private.rscratch;
+          // insertEdge(edgesEl, edge, data.edgeData, bounds, diagObj);
+        }
+      });
+
+      log.info(cy.json());
+      setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
+      // Remove element after layout
+      // renderEl.remove();
+      resolve();
+      // }, 500);
+    });
+  });
+};
+
+export default {
+  // setConf,
+  // addVertices,
+  // addEdges,
+  getClasses,
+  draw,
+};
diff --git a/packages/mermaid-flowchart-v3/src/flowRenderer-v3.js b/packages/mermaid-flowchart-v3/src/flowRenderer-v3.js
index 102a3c652..9b0f3092b 100644
--- a/packages/mermaid-flowchart-v3/src/flowRenderer-v3.js
+++ b/packages/mermaid-flowchart-v3/src/flowRenderer-v3.js
@@ -3,14 +3,19 @@ import { select, line, curveLinear, curveCardinal, curveBasis, selectAll } from
 import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
 import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js';
 import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js';
-
+import dagre from 'cytoscape-dagre';
 // Replace with other function to avoid dependency to dagre-d3
 import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
 
 import common, { evaluate } from '../../mermaid/src/diagrams/common/common';
 import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils';
 
-import cytoscape from 'cytoscape';
+// import ELK from 'elkjs/lib/elk-api';
+// const elk = new ELK({
+//   workerUrl: './elk-worker.min.js',
+// });
+import ELK from 'elkjs/lib/elk.bundled.js';
+const elk = new ELK();
 
 const conf = {};
 export const setConf = function (cnf) {
@@ -20,6 +25,8 @@ export const setConf = function (cnf) {
   }
 };
 
+const nodeDb = {};
+
 // /**
 //  * Function that adds the vertices found during parsing to the graph to be rendered.
 //  *
@@ -30,7 +37,7 @@ export const setConf = function (cnf) {
 //  * @param doc
 //  * @param diagObj
 //  */
-export const addVertices = function (vert, cy, svgId, root, doc, diagObj, parentLookUpDb) {
+export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookUpDb, graph) {
   const svg = root.select(`[id="${svgId}"]`);
   const nodes = svg.insert('g').attr('class', 'nodes');
   const keys = Object.keys(vert);
@@ -165,33 +172,36 @@ export const addVertices = function (vert, cy, svgId, root, doc, diagObj, parent
     };
     const nodeEl = insertNode(nodes, node, vertex.dir);
     const boundingBox = nodeEl.node().getBBox();
-    cy.add({
-      group: 'nodes',
-      data: {
-        id: vertex.id,
-        labelStyle: styles.labelStyle,
-        shape: _shape,
-        labelText: vertexText,
-        rx: radious,
-        ry: radious,
-        class: classStr,
-        style: styles.style,
-        link: vertex.link,
-        linkTarget: vertex.linkTarget,
-        tooltip: diagObj.db.getTooltip(vertex.id) || '',
-        domId: diagObj.db.lookUpDomId(vertex.id),
-        haveCallback: vertex.haveCallback,
-        width: vertex.type === 'group' ? 500 : undefined,
-        dir: vertex.dir,
-        type: vertex.type,
-        props: vertex.props,
-        padding: getConfig().flowchart.padding,
-        boundingBox,
-        el: nodeEl,
-        parent: parentLookUpDb[vertex.id],
-      },
+    const data = {
+      id: vertex.id,
+      labelStyle: styles.labelStyle,
+      shape: _shape,
+      labelText: vertexText,
+      rx: radious,
+      ry: radious,
+      class: classStr,
+      style: styles.style,
+      link: vertex.link,
+      linkTarget: vertex.linkTarget,
+      tooltip: diagObj.db.getTooltip(vertex.id) || '',
+      domId: diagObj.db.lookUpDomId(vertex.id),
+      haveCallback: vertex.haveCallback,
+      width: boundingBox.width,
+      height: boundingBox.height,
+      dir: vertex.dir,
+      type: vertex.type,
+      props: vertex.props,
+      padding: getConfig().flowchart.padding,
+      boundingBox,
+      el: nodeEl,
+      parent: parentLookUpDb.parentById[vertex.id],
+    };
+    // if (!Object.keys(parentLookUpDb.childrenById).includes(vertex.id)) {
+    graph.children.push({
+      ...data,
     });
-
+    // }
+    nodeDb[node.id] = data;
     log.trace('setNode', {
       labelStyle: styles.labelStyle,
       shape: _shape,
@@ -207,9 +217,10 @@ export const addVertices = function (vert, cy, svgId, root, doc, diagObj, parent
       dir: vertex.dir,
       props: vertex.props,
       padding: getConfig().flowchart.padding,
-      parent: parentLookUpDb[vertex.id],
+      parent: parentLookUpDb.parentById[vertex.id],
     });
   });
+  return graph;
 };
 
 /**
@@ -219,8 +230,9 @@ export const addVertices = function (vert, cy, svgId, root, doc, diagObj, parent
  * @param {object} g The graph object
  * @param cy
  * @param diagObj
+ * @param graph
  */
-export const addEdges = function (edges, cy, diagObj) {
+export const addEdges = function (edges, diagObj, graph) {
   // log.info('abc78 edges = ', edges);
   let cnt = 0;
   let linkIdCnt = {};
@@ -351,8 +363,16 @@ export const addEdges = function (edges, cy, diagObj) {
     edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
 
     // Add the edge to the graph
-    cy.add({ group: 'edges', data: { source: edge.start, target: edge.end, edgeData, id: cnt } });
+    graph.edges.push({
+      id: 'e' + edge.start + edge.end,
+      sources: [edge.start],
+      targets: [edge.end],
+      edgeData,
+      targetPort: 'PortSide.NORTH',
+      // id: cnt,
+    });
   });
+  return graph;
 };
 
 const addmarkers = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute) {
@@ -439,7 +459,7 @@ const addmarkers = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute
  */
 export const getClasses = function (text, diagObj) {
   log.info('Extracting classes');
-  diagObj.db.clear();
+  diagObj.db.clear('ver-2');
   try {
     // Parse the graph definition
     diagObj.parse(text);
@@ -449,40 +469,49 @@ export const getClasses = function (text, diagObj) {
   }
 };
 
-const addSubGraphs = function (cy, db) {
-  const parentLookUpDb = {};
+const addSubGraphs = function (db) {
+  const parentLookUpDb = { parentById: {}, childrenById: {} };
   const subgraphs = db.getSubGraphs();
-
+  log.info('Subgraphs - ', subgraphs);
   subgraphs.forEach(function (subgraph) {
-    parentLookUpDb[subgraph.id] = subgraph.parent;
+    subgraph.nodes.forEach(function (node) {
+      parentLookUpDb.parentById[node] = subgraph.id;
+      if (parentLookUpDb.childrenById[subgraph.id] === undefined) {
+        parentLookUpDb.childrenById[subgraph.id] = [];
+      }
+      parentLookUpDb.childrenById[subgraph.id].push(node);
+    });
   });
 
   subgraphs.forEach(function (subgraph) {
-    cy.add({
-      group: 'nodes',
-      data: { id: subgraph.id, parent: parentLookUpDb[subgraph.id], classes: 'flowchart-subgraph' },
-    });
+    const data = { id: subgraph.id };
+    if (parentLookUpDb.parentById[subgraph.id] !== undefined) {
+      data.parent = parentLookUpDb.parentById[subgraph.id];
+    }
+    // cy.add({
+    //   group: 'nodes',
+    //   data,
+    // });
   });
   return parentLookUpDb;
 };
 
-const insertEdge = function (edgesEl, edge, edgeData, bounds, diagObj) {
-  const src = edge.sourceEndpoint();
-  const segments = edge.segmentPoints();
+const insertEdge = function (edgesEl, edge, edgeData, diagObj) {
+  const src = edge.sections[0].startPoint;
+  const dest = edge.sections[0].endPoint;
+  const segments = edge.sections[0].bendPoints ? edge.sections[0].bendPoints : [];
   // const dest = edge.target().position();
-  const dest = edge.targetEndpoint();
+  // const dest = edge.targetEndpoint();
   const segPoints = segments.map((segment) => [segment.x, segment.y]);
-  const points = [
-    [src.x, src.y],
-    [segments[0].x, segments[0].y],
-    [dest.x, dest.y],
-  ];
+  const points = [[src.x, src.y], ...segPoints, [dest.x, dest.y]];
   // console.log('Edge ctrl points:', edge.segmentPoints(), 'Bounds:', bounds, edge.source(), points);
   // console.log('Edge ctrl points:', points);
-  const curve = line().curve(curveCardinal);
+  // const curve = line().curve(curveCardinal);
+  const curve = line().curve(curveLinear);
   const edge2 = edgesEl
     .insert('path')
     .attr('d', curve(points))
+    // .attr('d', points))
     .attr('class', 'path')
     .attr('fill', 'none');
   addmarkers(edge2, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute);
@@ -518,42 +547,31 @@ const insertEdge = function (edgesEl, edge, edgeData, bounds, diagObj) {
 
 export const draw = function (text, id, _version, diagObj) {
   // Add temporary render element
+  diagObj.db.clear();
+  diagObj.db.setGen('gen-2');
+  // Parse the graph definition
+  diagObj.parser.parse(text);
 
   return new Promise(function (resolve, reject) {
     const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
     // .attr('style', 'display:none')
-    const cy = cytoscape({
-      styleEnabled: true,
-      // animate: false,
-      // ready: function () {
-      //   log.info('Ready', this);
-      // },
-      container: document.getElementById('cy'), // container to render in
-      elements: [],
-      style: [
-        {
-          selector: 'edge',
-          style: {
-            'curve-style': 'segments',
-            // 'curve-style': 'bezier',
-            // 'segment-weights': '0.5',
-            // 'segment-distances': '0',
-            'edge-distances': 'node-position',
-
-            // 'source-endpoint': '180deg',
-            // 'target-endpoint': '0deg',
-          },
-          // 'edge-distance': 'intersection',
-        },
-        // {
-        //   selector: 'node',
-        //   style: {
-        //     width: 70,
-        //     height: 100,
-        //   },
-        // },
-      ],
-    });
+    let graph = {
+      id: 'root',
+      layoutOptions: {
+        'elk.algorithm': 'layered',
+        'elk.direction': 'DOWN',
+        'elk.port.side': 'SOUTH',
+        // 'nodePlacement.strategy': 'SIMPLE',
+        'org.eclipse.elk.graphviz.concentrate': true,
+        // 'org.eclipse.elk.spacing.nodeNode': 120,
+        // 'org.eclipse.elk.spacing.edgeEdge': 120,
+        // 'org.eclipse.elk.spacing.edgeNode': 120,
+        // 'org.eclipse.elk.spacing.nodeEdge': 120,
+        'org.eclipse.elk.spacing.componentComponent': 120,
+      },
+      children: [],
+      edges: [],
+    };
     log.info('Drawing flowchart using v3 renderer');
     // Fetch the default direction, use TD if none was found
     let dir = diagObj.db.getDirection();
@@ -579,89 +597,45 @@ export const draw = function (text, id, _version, diagObj) {
     insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
     // Fetch the vertices/nodes and edges/links from the parsed graph definition
     const vert = diagObj.db.getVertices();
-    const parentLookUpDb = addSubGraphs(cy, diagObj.db);
-    addVertices(vert, cy, id, root, doc, diagObj, parentLookUpDb);
+
+    let subG;
+    const subGraphs = diagObj.db.getSubGraphs();
+    log.info('Subgraphs - ', subGraphs);
+    for (let i = subGraphs.length - 1; i >= 0; i--) {
+      subG = subGraphs[i];
+      log.info('Subgraph - ', subG);
+      diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
+    }
+
+    const parentLookUpDb = addSubGraphs(diagObj.db);
+    graph = addVertices(vert, id, root, doc, diagObj, parentLookUpDb, graph);
     const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
     const edges = diagObj.db.getEdges();
-    addEdges(edges, cy, diagObj);
+    graph = addEdges(edges, diagObj, graph);
 
-    // c.style();
-    // Make cytoscape care about the dimensions of the nodes
-    cy.nodes().forEach(function (n) {
-      const boundingBox = n.data().boundingBox;
-      if (boundingBox) {
-        n.style('width', boundingBox.width);
-        n.style('height', boundingBox.height);
-      }
-      n.style('shape', 'square');
-      n.layoutDimensions = () => {
-        // console.log('Node dimensions', boundingBox.width, boundingBox.height);
-        if (boundingBox) {
-          return { w: boundingBox.width, h: boundingBox.height };
-        }
-        // return { w: boundingBox.width, h: boundingBox.height };
-
-        // const data = n.data();
-        // return { w: data.width, h: data.height };
-
-        return { w: 206, h: 160 };
-      };
-    });
-
-    cy.layout({
-      // name: 'grid',
-      name: 'preset',
-      // name: 'cose',
-      // name: 'circle',
-      // name: 'concentric',
-      headless: true,
-      styleEnabled: false,
-      animate: false,
-    }).run();
-    // log.info('Positions', cy.nodes().positions());
-    window.cy = cy;
-    cy.ready((e) => {
-      log.info('Ready', e);
-      // setTimeout(() => {
-      cy.nodes().map((node, id) => {
-        const data = node.data();
-
-        log.info(
-          'Position: (',
-          node.position().x,
-          ', ',
-          node.position().y,
-          ')',
-          data,
-          cy.elements()[0].renderedBoundingBox()
-        );
-        if (data.el) {
-          data.el.attr('transform', `translate(${node.position().x}, ${node.position().y})`);
+    elk.layout(graph).then(function (g) {
+      g.children.forEach(function (node) {
+        const data = nodeDb[node.id];
+        if (data) {
+          data.el.attr(
+            'transform',
+            `translate(${node.x + node.width / 2}, ${node.y + node.height / 2})`
+          );
           // document
           //   .querySelector(`[id="${data.domId}"]`)
           //   .setAttribute('transform', `translate(${node.position().x}, ${node.position().y})`);
           log.info('Id = ', data.domId, svg.select(`[id="${data.domId}"]`), data.el.node());
         }
-        // else {
-        //   // console.log('No element found for node', data, node.position(), node.size());
-        // }
       });
 
-      cy.edges().map((edge, id) => {
-        const data = edge.data();
-        if (edge[0]._private.bodyBounds) {
-          const bounds = edge[0]._private.rscratch;
-          insertEdge(edgesEl, edge, data.edgeData, bounds, diagObj);
-        }
+      g.edges.map((edge, id) => {
+        insertEdge(edgesEl, edge, edge.edgeData, diagObj);
       });
-
-      log.info(cy.json());
       setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
-      // Remove element after layout
-      // renderEl.remove();
       resolve();
-      // }, 500);
     });
+    // Remove element after layout
+    // renderEl.remove();
   });
 };
 
diff --git a/packages/mermaid/src/dagre-wrapper/markers.js b/packages/mermaid/src/dagre-wrapper/markers.js
index c231eb3e5..1a3f74bee 100644
--- a/packages/mermaid/src/dagre-wrapper/markers.js
+++ b/packages/mermaid/src/dagre-wrapper/markers.js
@@ -142,7 +142,7 @@ const point = (elem, type) => {
     .append('marker')
     .attr('id', type + '-pointEnd')
     .attr('class', 'marker ' + type)
-    .attr('viewBox', '0 0 10 10')
+    .attr('viewBox', '0 0 12 20')
     .attr('refX', 10)
     .attr('refY', 5)
     .attr('markerUnits', 'userSpaceOnUse')
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js
index 928889138..147ad5b6a 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js
@@ -439,7 +439,7 @@ export const clear = function (ver = 'gen-1') {
   commonClear();
 };
 export const setGen = (ver) => {
-  version = ver || 'gen-1';
+  version = ver || 'gen-2';
 };
 /** @returns {string} */
 export const defaultStyle = function () {
diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
index be3fffa0c..b058b8010 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
@@ -409,7 +409,7 @@ export const draw = function (text, id, _version, diagObj) {
 
   const edges = diagObj.db.getEdges();
 
-  log.info(edges);
+  log.info('Edges', edges);
   let i = 0;
   for (i = subGraphs.length - 1; i >= 0; i--) {
     // for (let i = 0; i < subGraphs.length; i++) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 561d55e8d..01ace7c08 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -311,6 +311,9 @@ importers:
       cytoscape-cose-bilkent:
         specifier: ^4.1.0
         version: 4.1.0_cytoscape@3.23.0
+      cytoscape-dagre:
+        specifier: ^2.1.0
+        version: 2.5.0_cytoscape@3.23.0
       cytoscape-fcose:
         specifier: ^2.1.0
         version: 2.1.0_cytoscape@3.23.0
@@ -320,6 +323,9 @@ importers:
       dagre-d3-es:
         specifier: 7.0.4
         version: 7.0.4
+      elkjs:
+        specifier: ^0.8.2
+        version: 0.8.2
       graphlib:
         specifier: ^2.1.0
         version: 2.1.8
@@ -3578,7 +3584,7 @@ packages:
   /axios/0.21.4_debug@4.3.2:
     resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
     dependencies:
-      follow-redirects: 1.15.2
+      follow-redirects: 1.15.2_debug@4.3.2
     transitivePeerDependencies:
       - debug
     dev: true
@@ -4674,6 +4680,15 @@ packages:
       cytoscape: 3.23.0
     dev: false
 
+  /cytoscape-dagre/2.5.0_cytoscape@3.23.0:
+    resolution: {integrity: sha512-VG2Knemmshop4kh5fpLO27rYcyUaaDkRw+6PiX4bstpB+QFt0p2oauMrsjVbUamGWQ6YNavh7x2em2uZlzV44g==}
+    peerDependencies:
+      cytoscape: ^3.2.22
+    dependencies:
+      cytoscape: 3.23.0
+      dagre: 0.8.5
+    dev: false
+
   /cytoscape-fcose/2.1.0_cytoscape@3.23.0:
     resolution: {integrity: sha512-Q3apPl66jf8/2sMsrCjNP247nbDkyIPjA9g5iPMMWNLZgP3/mn9aryF7EFY/oRPEpv7bKJ4jYmCoU5r5/qAc1Q==}
     peerDependencies:
@@ -4946,6 +4961,13 @@ packages:
       lodash-es: 4.17.21
     dev: false
 
+  /dagre/0.8.5:
+    resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==}
+    dependencies:
+      graphlib: 2.1.8
+      lodash: 4.17.21
+    dev: false
+
   /dargs/7.0.0:
     resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==}
     engines: {node: '>=8'}
@@ -5270,6 +5292,10 @@ packages:
     resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
     dev: true
 
+  /elkjs/0.8.2:
+    resolution: {integrity: sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==}
+    dev: false
+
   /emittery/0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
     engines: {node: '>=12'}
@@ -6201,7 +6227,7 @@ packages:
     resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
     dev: true
 
-  /follow-redirects/1.15.2:
+  /follow-redirects/1.15.2_debug@4.3.2:
     resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
     engines: {node: '>=4.0'}
     peerDependencies:
@@ -6209,6 +6235,8 @@ packages:
     peerDependenciesMeta:
       debug:
         optional: true
+    dependencies:
+      debug: 4.3.2
     dev: true
 
   /foreground-child/2.0.0:
@@ -6749,7 +6777,7 @@ packages:
     engines: {node: '>=8.0.0'}
     dependencies:
       eventemitter3: 4.0.7
-      follow-redirects: 1.15.2
+      follow-redirects: 1.15.2_debug@4.3.2
       requires-port: 1.0.0
     transitivePeerDependencies:
       - debug