diff --git a/.gitignore b/.gitignore
index 763301fc0025d020fbc9fb3a0781097e8e036d9c..64f046d19ab591744d17a9c5fce52f885d1b91c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 dist/
-node_modules/
\ No newline at end of file
+node_modules/
+coverage/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ffe1b195d489bf56fa2477e22389cd95a18bfb41
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,20 @@
+include:
+  - project: 'metabohub/web-components/mth-cicd'
+    ref: 1.0.3
+    file: '/templates/npm.gitlab-ci.yml'
+
+.publish:
+  stage: deploy
+  before_script:
+    - apt-get update && apt-get install -y git default-jre
+    - npm install
+    - npm run build
+
+
+test:
+  image: node:18
+  stage: test
+  before_script:
+    - npm install
+  script:
+    - npm run test
diff --git a/.npmrc b/.npmrc
index 7cdc81cfb9ae3bd27b8b8c62b5548bf2d0986438..9c9fc8246cf71c9b77fa234dd584e516bc037699 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1 +1,2 @@
-@metabohub:registry=https://forgemia.inra.fr/api/v4/packages/npm/
+@metabohub:registry=https://forgemia.inra.fr/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
+//forgemia.inra.fr/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
diff --git a/README.md b/README.md
index af7d0f4616c320228078742d846ada9272642aa1..f3a790cff43ce11b6740c1392c59b8fee45ec3a4 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,353 @@
-in package.json, change name of project
-src -> index.ts -> add all exported methods
-src/__tests__/ -> add unit test file
\ No newline at end of file
+# viz-layout
+
+---
+
+## Description
+
+The viz-layout is a layout to draw metabolic pathways vertically. To use the layout, a network with nodes and directed links is required. User can provide an object describing the network style. If defined in the network style, height and width of nodes can be taken into account. The layout change the position of nodes, and can add some classes for nodes and links.  
+
+The layout is designed to be applied to directed bipartite graphs, where metabolite nodes are only connected to reaction nodes. Reactions can be reversible, but for each reversible reaction, all links must be declared for one direction. To reverse the direction of a reaction, the source and target of the links are swapped. Additionally, metabolite nodes can be marked as side compounds, with common examples being water and ATP. A step of the layout will duplicate them and place them after the other nodes.
+
+The y-axis is inverted compared to the usual Cartesian convention (where smaller y values are lower on the graph). It was designed for systems like SVG where smaller y values are positioned at the top of the screen. Therefore, the initial reactants, placed at the start of the reaction sequence, will have smaller y values, positioning them at the top of the diagram. The products of the reaction will have larger y values, placing them lower.
+
+---
+
+## Table of Contents
+
+- [Getting Started](#getting-started)
+  - [Create a Typescript Project](#create-a-typescript-project)
+  - [Install via npm](#install-via-npm)
+  - [Typescript Configuration](#typescript-configuration)
+- [Usage](#usage)
+- [Types](#types)
+  - [Types for Network](#types-for-network)
+    - [Network](#network)
+    - [Node](#node)
+    - [Link](#link)
+  - [Types for Style](#types-for-style)
+    - [GraphStyleProperties](#graphstyleproperties)
+    - [NodeStyle](#nodestyle)
+  - [Types for Parameters](#types-for-parameters)
+- [Layout](#layout)
+  - [layoutOnNetwork( )](#layoutonnetwork)
+    - [Step of the Layout](#step-of-the-layout)
+        - [Base Layout](#base-layout)
+        - [Management of Side Compounds](#management-of-side-compounds)
+        - [Management of Reversible Reaction](#management-of-reversible-reaction)
+        - [Management of Directed Cycles](#management-of-directed-cycles)
+        - [Management of Main Chains](#management-of-main-chains)
+        - [Shifting Coordinates](#shifting-coordinates)
+
+---
+
+## Getting started
+
+#### Create a typescript project
+
+1. **Initialize a new project**
+
+   First, create a new directory for your project and initialize it with `npm`:
+
+   ```bash
+   mkdir my-project
+   cd my-project
+   npm init -y
+   ```
+
+2. **Install TypeScript**
+
+    You'll need to install TypeScript as a development dependency in your project:
+
+    ```bash
+    npm install --save-dev typescript
+    ```
+
+3. **Set up the TypeScript configuration**
+
+    TypeScript requires a `tsconfig.json` file (at the root of the project) to specify how your TypeScript code should be compiled. You can generate a default configuration file by running:
+
+    ```bash
+    npx tsc --init
+    ```
+
+   
+
+4. **Create your source files**
+
+    Inside your project, create a `src` directory and a `index.ts` file for your TypeScript code:
+
+    ```bash
+    mkdir src
+    touch src/index.ts
+    ```
+
+
+#### Install via npm
+
+The viz-layout package is currently only available on the MetaboHUB forge. To install it, you need to configure an `.npmrc` file (at the root of the project) to specify the retrieval path.
+
+```.npmrc
+@metabohub:registry=https://forgemia.inra.fr/api/v4/packages/npm/
+```
+
+Then you can install the package:
+
+```
+npm install @metabohub/viz-layout
+```
+
+#### Typescript configuration
+
+
+Once the installation step is completed, you need to declare the module. To do this, add the following line in the `env.d.ts` file (at the root of the project):
+
+```ts 
+declare module "@metabohub/viz-layout";
+```
+
+---
+## Usage
+
+
+```typescript
+// Imports
+import type { Network , Node, Link, GraphStyleProperties, NodeStyle} from "@metabohub/viz-layout/src/types/TypeVizCore";
+import { algorithmOnNetwork,PathType, defaultParameters } from "@metabohub/viz-layout"
+
+// Creation of network
+const nodes : {[key:string]:Node} = {
+	A: {
+		id: 'A',
+		x: 50,
+		y: 50,
+        classes: ["metabolite"],
+        metadata :{
+            isSideCompound : false
+        }
+	},
+	B: {
+		id: 'B',
+		x: 100,
+		y: 100,
+        classes: ["reaction"],
+        metadata : {
+            isReversible : true
+        }
+	}
+}
+const links : Link[] = [
+	{
+		source: nodes.A,
+		target: nodes.B,
+		id: 'A->B'
+	}
+]
+const network:Network = { id: 'network', nodes: nodes, links: links };
+
+
+// Creation of network styles
+const nodeStyle : NodeStyle = {
+            metabolite: {
+                height: 50,
+                width: 50
+            },
+            reaction: {
+                height: 100,
+                width: 100
+            }
+        };
+const networkStyle :GraphStyleProperties ={ nodeStyles: nodeStyle };
+
+// Choosing parameters
+const parameters = defaultParameters;
+parameters.doReactionReversible = false;
+parameters.pathType = PathType.ALL_LONGEST
+
+// Application of layout
+const newNetwork = await layoutOnNetwork(network, networkStyle, parameters);
+```
+
+---
+
+## Types 
+
+### Types for network
+
+##### Network
+
+| Attribut | Type | Description |
+| -------- | ---- | ----------- |
+| id | `string` | Network's id |
+| nodes | `{[key: string] Node}` | Object that contains nodes |
+| links | `Array<Link>` | List that contains links |
+
+
+##### Node
+
+| Attribut | Type | Description |
+| -------- | ---- | ----------- |
+| id | `string` | Node's id |
+| x | `number` | X node's position |
+| y | `number` | Y node's position |
+| classes | `Array<string>` | Node's classes to manage style |
+| metadata | `{[key: string]: string \| number \| {[key: string]: string \| number} \| Array<string> \| boolean}` | Node's metadata |
+
+
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
+  <path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
+</svg> 
+The id of the node has to be the same that the key associated with the node in 'nodes' in the network ! Morever, the string for id must follow one of the formats listed below: <br />
+
+- any string of alphabetic (`[a-zA-Z\200-\377]`) characters, underscores (`'_'`) or digits(`[0-9]`), not beginning with a digit
+- a numeral [`-`]?(`.`[`0`-`9`]⁺ `|` [`0`-`9`]⁺(`.`[`0`-`9`]*)? )
+- any double-quoted string (`"..."`) possibly containing escaped quotes (`\"`)
+- an HTML string (`<...>`)
+
+<br />
+
+
+The classes of a node can contain at least either `metabolite` or `reaction`.
+
+Metadata can contains those elements :
+| Key | Type | Description |
+| -------- | ---- | ----------- |
+| isSideCompound | `booleen` | Node is declared as a side compound |
+| isReversible | `booleen` | Node is declared as a reversible |
+
+If `isSideCompound` is not set, the step to manage side compounds won't do anything. A class `duplicate` will be added to side compounds nodes that have been duplicated.
+If `isReversible` is not set and reaction node doesn't contains a class "reaction", the step to manage reaction reversible won't do anything : there is no reaction that are reversible.
+
+
+##### Link
+
+| Attribut | Type | Description |
+| -------- | ---- | ----------- |
+| id | string | Link's id |
+| source | Node | Source node of the link |
+| target | Node | Target node of the link |
+| classes | Array<string> | Link's classes to manage style |
+
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
+  <path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
+</svg> The source and target need to be pointer to a node present in the network (see usage) ! 
+
+In order to have a bipartite graph, links should associate a metabolite node with a reaction node.
+A class `reversible` will be added by the layout for links associated with a reversible reaction if the step to manage reversible reaction is done. 
+
+### Types for style
+
+#### GraphStyleProperties
+
+| Attribut | Type | Description |
+| -------- | ---- | ----------- |
+| nodeStyles | { [key: string]: NodeStyle } | Object that contains nodes classes name associated to their style |
+
+The keys of nodeStyles need to be the same that classes of nodes to associate the style to nodes.
+
+#### NodeStyle
+
+| Attribut | Type | Description |
+| -------- | ---- | ----------- |
+| height | number | Node's height |
+| width | number | Node's width |
+
+
+### Types for parameters
+
+To initialize parameters, use the `defaultParameters` variable from the package. You can then modify the desired parameters as needed (see usage). See "Step of the layout" for a better understanding of some parameters.
+
+| Parameter                | Type        | Default       | Description                                                                                      |
+|--------------------------|-------------|---------------|--------------------------------------------------------------------------------------------------|
+| doDuplicateSideCompounds  | `boolean`     | true          | Whether to duplicate side compounds                                                            |
+| doPutAsideSideCompounds   | `boolean`     | true          | Whether to remove (temporarily) side compounds                                              |
+| doReactionReversible      | `boolean`     | true          | Whether to handle reversible reactions         |
+| doMainChain               | `boolean`     | true          | Whether to find and clusterized main chain                                       |
+| pathType                  | `PathType`    | ALL_LONGEST   | Defines the path type for the main chain step: LONGEST, ALL_LONGEST, or ALL.      |
+| doCycle                   | `boolean`     | true          | Whether to process directed cycles                                                          |
+| shiftCoord                | `boolean`     | false          | Whether to get top left corner coordinates of nodes (if false, center of nodes is returned)     |
+
+---
+
+## Layout
+
+
+`layoutOnNetwork(network, networkStyle, parameters)` is a asynchronous function.
+
+##### Input
+
+| Arguments | Type | default | Description | Optional |
+| ----- | ---- | ------- | ----------- | -------- |
+| network | `Network` | *no default* | Network object that contains nodes and links of the network | No |
+| networkStyle | `GraphStyleProperties` | { } | Network style object that contains classes and style associated with nodes | Yes |
+| parameters | `Parameters` | defaultParameters | Parameters of the step of layout | Yes |
+
+To change parameters, you need to get defaultParameters and then modify the accessible attributs.
+
+##### Output
+
+ Type | Description 
+ ---- | ----------- 
+ `Promise<Network>` | network with modified node positions, and some added classes  
+
+
+#### Step of the layout
+
+##### Base Layout
+
+The base layout used in the algorithm is a Sugiyama layout. It is implemented by the viz.js library (https://github.com/mdaines/viz-js), a JS wrapper of GraphViz (https://graphviz.org/documentation/). It correspond to the dot layout of GraphViz.
+
+<small>E. R. GANSNER, E. KOUTSOFIOS, S. C. NORTH et K.-P. VO, “A technique for drawing directed graphs”, IEEE Transactions on Software Engineering, t. 19, no 3, p. 214-230, 1993.</small>
+
+  
+##### Management of side compounds
+
+Nodes declared as side compounds are duplicated if `doDuplicateSideCompounds = true`.
+Nodes declared as side compounds are removed from the network object if `doPutAsideSideCompounds = true`. They can be temporary suppressed so that they are placed depending on the positions of the other nodes at the end.
+
+At the end of the layout, side compounds are reinserted in the network if `doPutAsideSideCompounds = true` (if they have been removed in the first place), and if `doDuplicateSideCompounds = true`. If side compounds are suppressed whitout having been duplicated, there is no method implemented to reinsert them.
+
+| `doDuplicateSideCompounds` | `doPutAsideSideCompounds` | Reinsertion of side compounds ? |
+|---|---|---|
+| true | true | true |
+| false | false | false |
+| true | false | false |
+| false | true | false |
+
+
+
+##### Management of reversible reaction
+
+The step is done if `doReactionReversible = true`. 
+
+For the step to have an effect, reaction nodes that represent a reversible reaction need to have the class `reaction` in their classes, and `metadata.isReversible = true`. 
+
+This step choose for each reversible reaction a direction that will maintien the continuity of a sequence of reaction in the drawing. Links associated with those reactions get a class `reversible`.
+
+##### Management of directed cycles
+
+The step is done if `doCycle = true`.
+
+Directed cycles of more that 3 nodes are found. They are placed in circle as much as possible. When several directed cycles have common nodes, all the nodes won't necessarily be placed in circle. Those will be placed by a force layout (of D3 library : https://d3js.org/).
+
+If `doReactionReversible = true`, different directed cycles can be found as both direction of reversible reaction are taken into account, else only the direction declared in the network is used.
+
+
+##### Management of main chains
+
+The step is done if `doMainChain = true`.
+
+Some nodes that represent "main chain" are grouped in a cluster subgraph in the viz.js library (Sugiyama layout). It can help the resulting drawing or not. You can test with or without the step.
+
+Main chain are defined as merge of path in the network. Start nodes are declared in the algorithm, then the network is convert into a DAG (no directed cycle). Longest path will be found from the start nodes. If several paths have common nodes, they can be merged. A merge of path represent a main chain.
+
+Several versions of this step are implemented :
+    - `LONGEST` : when longest paths are searched from a start node, if several are found, only one will be keeped
+    - `ALL_LONGEST` : when longest paths are searched from a start node, all the longest are keeped
+    - `ALL` : when longest paths are searched from a start node, all paths between start node and terminal node of longest paths are keeped
+
+`ALL_LONGEST` is the default version, to change it you need to change the parameter pathType with the defined type `PathType` (ex : `parameters.pathType = PathType.ALL`).
+
+
+##### Shifting coordinates
+
+If `shiftCoord = true`, the node's coordinates represent its top-left corner; otherwise, they represent the node's center. When shifting the coordinates, the adjustment is made so that positioning the node by its top-left corner keeps it centered in its original location. Specifically, the coordinates are shifted by half the node's height and width. To define a node's `height` and `width`, refer to `networkStyle`. This is useful when rendering networks in SVG format.
\ No newline at end of file
diff --git a/env.d.ts b/env.d.ts
index c4d9129b60709fd4dc15d5a6e35223340a69b8c7..e955ccfb3b567884184daede71782b4770d72ad9 100644
--- a/env.d.ts
+++ b/env.d.ts
@@ -1,4 +1 @@
-declare module '@metabohub/viz-core';
-declare module 'dagrejs';
-declare module '@metabohub/viz-context-menu';
-declare module 'line-intersect';
\ No newline at end of file
+declare module 'line-intersect';
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index f55e045c13bd92374f67867e21ad6005826c7bdf..0000000000000000000000000000000000000000
--- a/package-lock.json
+++ /dev/null
@@ -1,8011 +0,0 @@
-{
-  "name": "@metabohub/ts-package-name",
-  "version": "0.0.0",
-  "lockfileVersion": 3,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "@metabohub/ts-package-name",
-      "version": "0.0.0",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@mdi/font": "^7.4.47",
-        "@metabohub/viz-context-menu": "^0.0.2",
-        "@metabohub/viz-core": "^0.6.1",
-        "cytoscape": "^3.30.2",
-        "line-intersect": "^3.0.0",
-        "xml2js": "^0.6.2"
-      },
-      "devDependencies": {
-        "@types/jest": "^29.5.12",
-        "@types/jsdom": "^21.1.6",
-        "@types/node": "^20.11.14",
-        "@types/xml2js": "^0.4.14",
-        "@viz-js/viz": "^3.4.0",
-        "cytoscape": "^3.30.2",
-        "cytoscape-cose-bilkent": "^3.0.0",
-        "cytoscape-fcose": "^2.2.0",
-        "dagrejs": "^0.2.1",
-        "graph-data-structure": "^3.5.0",
-        "jest": "^29.7.0",
-        "jsdom": "^24.0.0",
-        "prettier": "^3.2.4",
-        "ts-jest": "^29.1.2",
-        "ts-node": "^10.9.2",
-        "tsup": "^8.0.2",
-        "typescript": "^5.4.5",
-        "xml-js": "^1.6.11"
-      }
-    },
-    "node_modules/@ampproject/remapping": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
-      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/code-frame": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
-      "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/highlight": "^7.24.7",
-        "picocolors": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/compat-data": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
-      "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/core": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
-      "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
-      "dev": true,
-      "dependencies": {
-        "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.5",
-        "@babel/helper-compilation-targets": "^7.23.6",
-        "@babel/helper-module-transforms": "^7.24.5",
-        "@babel/helpers": "^7.24.5",
-        "@babel/parser": "^7.24.5",
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.5",
-        "@babel/types": "^7.24.5",
-        "convert-source-map": "^2.0.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.3",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/babel"
-      }
-    },
-    "node_modules/@babel/generator": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
-      "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.25.6",
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25",
-        "jsesc": "^2.5.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.25.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
-      "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/compat-data": "^7.25.2",
-        "@babel/helper-validator-option": "^7.24.8",
-        "browserslist": "^4.23.1",
-        "lru-cache": "^5.1.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-      "dev": true,
-      "dependencies": {
-        "yallist": "^3.0.2"
-      }
-    },
-    "node_modules/@babel/helper-module-imports": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
-      "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/traverse": "^7.24.7",
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-transforms": {
-      "version": "7.25.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz",
-      "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.24.7",
-        "@babel/helper-simple-access": "^7.24.7",
-        "@babel/helper-validator-identifier": "^7.24.7",
-        "@babel/traverse": "^7.25.2"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
-      "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-simple-access": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
-      "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/traverse": "^7.24.7",
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-string-parser": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
-      "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
-      "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-option": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
-      "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helpers": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz",
-      "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.5",
-        "@babel/types": "^7.24.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
-      "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.24.7",
-        "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0",
-        "picocolors": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
-    },
-    "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/parser": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz",
-      "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==",
-      "dependencies": {
-        "@babel/types": "^7.25.6"
-      },
-      "bin": {
-        "parser": "bin/babel-parser.js"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-async-generators": {
-      "version": "7.8.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
-      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-bigint": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
-      "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-class-properties": {
-      "version": "7.12.13",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
-      "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.12.13"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-meta": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
-      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-json-strings": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
-      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz",
-      "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
-      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
-      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-numeric-separator": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
-      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-object-rest-spread": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
-      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-optional-catch-binding": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
-      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-optional-chaining": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
-      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-top-level-await": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
-      "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-typescript": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz",
-      "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/template": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
-      "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/code-frame": "^7.24.7",
-        "@babel/parser": "^7.25.0",
-        "@babel/types": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/traverse": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz",
-      "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/code-frame": "^7.24.7",
-        "@babel/generator": "^7.25.6",
-        "@babel/parser": "^7.25.6",
-        "@babel/template": "^7.25.0",
-        "@babel/types": "^7.25.6",
-        "debug": "^4.3.1",
-        "globals": "^11.1.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/types": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
-      "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
-      "dependencies": {
-        "@babel/helper-string-parser": "^7.24.8",
-        "@babel/helper-validator-identifier": "^7.24.7",
-        "to-fast-properties": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@bcoe/v8-coverage": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
-      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
-      "dev": true
-    },
-    "node_modules/@cspotcode/source-map-support": {
-      "version": "0.8.1",
-      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
-      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/trace-mapping": "0.3.9"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
-      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.0.3",
-        "@jridgewell/sourcemap-codec": "^1.4.10"
-      }
-    },
-    "node_modules/@esbuild/aix-ppc64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
-      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "aix"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/android-arm": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
-      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/android-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
-      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/android-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
-      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
-      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/darwin-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
-      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
-      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
-      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-arm": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
-      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
-      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-ia32": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
-      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-loong64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
-      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
-      "cpu": [
-        "loong64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
-      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
-      "cpu": [
-        "mips64el"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
-      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
-      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
-      "cpu": [
-        "riscv64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-s390x": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
-      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
-      "cpu": [
-        "s390x"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/linux-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
-      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
-      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "netbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
-      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "openbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/sunos-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
-      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "sunos"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/win32-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
-      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/win32-ia32": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
-      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@esbuild/win32-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
-      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@isaacs/cliui": {
-      "version": "8.0.2",
-      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
-      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
-      "dev": true,
-      "dependencies": {
-        "string-width": "^5.1.2",
-        "string-width-cjs": "npm:string-width@^4.2.0",
-        "strip-ansi": "^7.0.1",
-        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
-        "wrap-ansi": "^8.1.0",
-        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@istanbuljs/load-nyc-config": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
-      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
-      "dev": true,
-      "dependencies": {
-        "camelcase": "^5.3.1",
-        "find-up": "^4.1.0",
-        "get-package-type": "^0.1.0",
-        "js-yaml": "^3.13.1",
-        "resolve-from": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@istanbuljs/schema": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/console": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
-      "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/core": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
-      "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
-      "dev": true,
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/reporters": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "exit": "^0.1.2",
-        "graceful-fs": "^4.2.9",
-        "jest-changed-files": "^29.7.0",
-        "jest-config": "^29.7.0",
-        "jest-haste-map": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-resolve-dependencies": "^29.7.0",
-        "jest-runner": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "jest-watcher": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@jest/core/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/core/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/environment": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
-      "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/expect": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
-      "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
-      "dev": true,
-      "dependencies": {
-        "expect": "^29.7.0",
-        "jest-snapshot": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/expect-utils": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
-      "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
-      "dev": true,
-      "dependencies": {
-        "jest-get-type": "^29.6.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/fake-timers": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
-      "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@sinonjs/fake-timers": "^10.0.2",
-        "@types/node": "*",
-        "jest-message-util": "^29.7.0",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/globals": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
-      "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/expect": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "jest-mock": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/reporters": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
-      "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
-      "dev": true,
-      "dependencies": {
-        "@bcoe/v8-coverage": "^0.2.3",
-        "@jest/console": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "collect-v8-coverage": "^1.0.0",
-        "exit": "^0.1.2",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-instrument": "^6.0.0",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.0",
-        "istanbul-reports": "^3.1.3",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "slash": "^3.0.0",
-        "string-length": "^4.0.1",
-        "strip-ansi": "^6.0.0",
-        "v8-to-istanbul": "^9.0.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/schemas": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
-      "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
-      "dev": true,
-      "dependencies": {
-        "@sinclair/typebox": "^0.27.8"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/source-map": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
-      "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "callsites": "^3.0.0",
-        "graceful-fs": "^4.2.9"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/test-result": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
-      "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
-      "dev": true,
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/istanbul-lib-coverage": "^2.0.0",
-        "collect-v8-coverage": "^1.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/test-sequencer": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
-      "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/test-result": "^29.7.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/transform": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
-      "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@jest/types": "^29.6.3",
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "babel-plugin-istanbul": "^6.1.1",
-        "chalk": "^4.0.0",
-        "convert-source-map": "^2.0.0",
-        "fast-json-stable-stringify": "^2.1.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "pirates": "^4.0.4",
-        "slash": "^3.0.0",
-        "write-file-atomic": "^4.0.2"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/types": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
-      "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/schemas": "^29.6.3",
-        "@types/istanbul-lib-coverage": "^2.0.0",
-        "@types/istanbul-reports": "^3.0.0",
-        "@types/node": "*",
-        "@types/yargs": "^17.0.8",
-        "chalk": "^4.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
-      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
-      "devOptional": true,
-      "dependencies": {
-        "@jridgewell/set-array": "^1.2.1",
-        "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
-      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-      "devOptional": true,
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/set-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
-      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-      "devOptional": true,
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/source-map": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
-      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25"
-      }
-    },
-    "node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
-      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
-    },
-    "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.25",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
-      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-      "devOptional": true,
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.1.0",
-        "@jridgewell/sourcemap-codec": "^1.4.14"
-      }
-    },
-    "node_modules/@mdi/font": {
-      "version": "7.4.47",
-      "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz",
-      "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw=="
-    },
-    "node_modules/@metabohub/viz-context-menu": {
-      "version": "0.0.2",
-      "resolved": "https://forgemia.inra.fr/api/v4/projects/11129/packages/npm/@metabohub/viz-context-menu/-/@metabohub/viz-context-menu-0.0.2.tgz",
-      "integrity": "sha1-851aHxYa7Y9LimYhpJEgXdatdkQ=",
-      "dependencies": {
-        "@mdi/font": "^7.4.47",
-        "roboto-fontface": "*",
-        "vue": "^3.4.15",
-        "vuetify": "^3.5.2",
-        "webfontloader": "^1.6.28"
-      }
-    },
-    "node_modules/@metabohub/viz-core": {
-      "version": "0.6.1",
-      "resolved": "https://forgemia.inra.fr/api/v4/projects/8209/packages/npm/@metabohub/viz-core/-/@metabohub/viz-core-0.6.1.tgz",
-      "integrity": "sha1-OtkeLt1AEBpOMOIT8gszu3tftyQ=",
-      "dependencies": {
-        "@vueuse/core": "^10.7.0",
-        "d3": "^7.8.5",
-        "lodash-es": "^4.17.21",
-        "uuid": "^9.0.1",
-        "vue": "^3.3.4"
-      }
-    },
-    "node_modules/@microsoft/api-extractor": {
-      "version": "7.43.0",
-      "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz",
-      "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@microsoft/api-extractor-model": "7.28.13",
-        "@microsoft/tsdoc": "0.14.2",
-        "@microsoft/tsdoc-config": "~0.16.1",
-        "@rushstack/node-core-library": "4.0.2",
-        "@rushstack/rig-package": "0.5.2",
-        "@rushstack/terminal": "0.10.0",
-        "@rushstack/ts-command-line": "4.19.1",
-        "lodash": "~4.17.15",
-        "minimatch": "~3.0.3",
-        "resolve": "~1.22.1",
-        "semver": "~7.5.4",
-        "source-map": "~0.6.1",
-        "typescript": "5.4.2"
-      },
-      "bin": {
-        "api-extractor": "bin/api-extractor"
-      }
-    },
-    "node_modules/@microsoft/api-extractor-model": {
-      "version": "7.28.13",
-      "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz",
-      "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@microsoft/tsdoc": "0.14.2",
-        "@microsoft/tsdoc-config": "~0.16.1",
-        "@rushstack/node-core-library": "4.0.2"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/minimatch": {
-      "version": "3.0.8",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
-      "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/typescript": {
-      "version": "5.4.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
-      "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
-    "node_modules/@microsoft/api-extractor/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/@microsoft/tsdoc": {
-      "version": "0.14.2",
-      "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz",
-      "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/@microsoft/tsdoc-config": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz",
-      "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@microsoft/tsdoc": "0.14.2",
-        "ajv": "~6.12.6",
-        "jju": "~1.4.0",
-        "resolve": "~1.19.0"
-      }
-    },
-    "node_modules/@microsoft/tsdoc-config/node_modules/resolve": {
-      "version": "1.19.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
-      "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "is-core-module": "^2.1.0",
-        "path-parse": "^1.0.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@pkgjs/parseargs": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
-      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
-      "dev": true,
-      "optional": true,
-      "engines": {
-        "node": ">=14"
-      }
-    },
-    "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz",
-      "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ]
-    },
-    "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz",
-      "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ]
-    },
-    "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz",
-      "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ]
-    },
-    "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz",
-      "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz",
-      "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz",
-      "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz",
-      "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz",
-      "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz",
-      "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz",
-      "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==",
-      "cpu": [
-        "riscv64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz",
-      "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==",
-      "cpu": [
-        "s390x"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz",
-      "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz",
-      "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ]
-    },
-    "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz",
-      "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ]
-    },
-    "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz",
-      "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ]
-    },
-    "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz",
-      "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ]
-    },
-    "node_modules/@rushstack/node-core-library": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz",
-      "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "fs-extra": "~7.0.1",
-        "import-lazy": "~4.0.0",
-        "jju": "~1.4.0",
-        "resolve": "~1.22.1",
-        "semver": "~7.5.4",
-        "z-schema": "~5.0.2"
-      },
-      "peerDependencies": {
-        "@types/node": "*"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/fs-extra": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
-      "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "graceful-fs": "^4.1.2",
-        "jsonfile": "^4.0.0",
-        "universalify": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=6 <7 || >=8"
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/jsonfile": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "optionalDependencies": {
-        "graceful-fs": "^4.1.6"
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/universalify": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/@rushstack/node-core-library/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/@rushstack/rig-package": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz",
-      "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "resolve": "~1.22.1",
-        "strip-json-comments": "~3.1.1"
-      }
-    },
-    "node_modules/@rushstack/terminal": {
-      "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz",
-      "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@rushstack/node-core-library": "4.0.2",
-        "supports-color": "~8.1.1"
-      },
-      "peerDependencies": {
-        "@types/node": "*"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@rushstack/terminal/node_modules/supports-color": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/@rushstack/ts-command-line": {
-      "version": "4.19.1",
-      "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz",
-      "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@rushstack/terminal": "0.10.0",
-        "@types/argparse": "1.0.38",
-        "argparse": "~1.0.9",
-        "string-argv": "~0.3.1"
-      }
-    },
-    "node_modules/@sinclair/typebox": {
-      "version": "0.27.8",
-      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
-      "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
-      "dev": true
-    },
-    "node_modules/@sinonjs/commons": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
-      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
-      "dev": true,
-      "dependencies": {
-        "type-detect": "4.0.8"
-      }
-    },
-    "node_modules/@sinonjs/fake-timers": {
-      "version": "10.3.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
-      "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
-      "dev": true,
-      "dependencies": {
-        "@sinonjs/commons": "^3.0.0"
-      }
-    },
-    "node_modules/@tsconfig/node10": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
-      "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
-      "dev": true
-    },
-    "node_modules/@tsconfig/node12": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
-      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
-      "dev": true
-    },
-    "node_modules/@tsconfig/node14": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
-      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
-      "dev": true
-    },
-    "node_modules/@tsconfig/node16": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
-      "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
-      "dev": true
-    },
-    "node_modules/@types/argparse": {
-      "version": "1.0.38",
-      "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
-      "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/@types/babel__core": {
-      "version": "7.20.5",
-      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
-      "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/parser": "^7.20.7",
-        "@babel/types": "^7.20.7",
-        "@types/babel__generator": "*",
-        "@types/babel__template": "*",
-        "@types/babel__traverse": "*"
-      }
-    },
-    "node_modules/@types/babel__generator": {
-      "version": "7.6.8",
-      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
-      "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "node_modules/@types/babel__template": {
-      "version": "7.4.4",
-      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
-      "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
-      "dev": true,
-      "dependencies": {
-        "@babel/parser": "^7.1.0",
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "node_modules/@types/babel__traverse": {
-      "version": "7.20.5",
-      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz",
-      "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.20.7"
-      }
-    },
-    "node_modules/@types/estree": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
-      "devOptional": true
-    },
-    "node_modules/@types/graceful-fs": {
-      "version": "4.1.9",
-      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
-      "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/istanbul-lib-coverage": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
-      "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
-      "dev": true
-    },
-    "node_modules/@types/istanbul-lib-report": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
-      "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
-      "dev": true,
-      "dependencies": {
-        "@types/istanbul-lib-coverage": "*"
-      }
-    },
-    "node_modules/@types/istanbul-reports": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
-      "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
-      "dev": true,
-      "dependencies": {
-        "@types/istanbul-lib-report": "*"
-      }
-    },
-    "node_modules/@types/jest": {
-      "version": "29.5.12",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
-      "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
-      "dev": true,
-      "dependencies": {
-        "expect": "^29.0.0",
-        "pretty-format": "^29.0.0"
-      }
-    },
-    "node_modules/@types/jsdom": {
-      "version": "21.1.7",
-      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
-      "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*",
-        "@types/tough-cookie": "*",
-        "parse5": "^7.0.0"
-      }
-    },
-    "node_modules/@types/node": {
-      "version": "20.12.12",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
-      "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
-      "devOptional": true,
-      "dependencies": {
-        "undici-types": "~5.26.4"
-      }
-    },
-    "node_modules/@types/stack-utils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
-      "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
-      "dev": true
-    },
-    "node_modules/@types/tough-cookie": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
-      "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
-      "dev": true
-    },
-    "node_modules/@types/web-bluetooth": {
-      "version": "0.0.20",
-      "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
-      "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
-    },
-    "node_modules/@types/xml2js": {
-      "version": "0.4.14",
-      "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
-      "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/yargs": {
-      "version": "17.0.32",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
-      "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
-      "dev": true,
-      "dependencies": {
-        "@types/yargs-parser": "*"
-      }
-    },
-    "node_modules/@types/yargs-parser": {
-      "version": "21.0.3",
-      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
-      "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
-      "dev": true
-    },
-    "node_modules/@viz-js/viz": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/@viz-js/viz/-/viz-3.8.0.tgz",
-      "integrity": "sha512-xbqbRanQCG06yfpnv2hsgX43G26q0JCSjcYSpvmUlx1VRCzA0ngi7zsPn0jW9K2ITchpjQiC1tyO6+zFgS/IyA==",
-      "dev": true
-    },
-    "node_modules/@vue/compiler-core": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.4.tgz",
-      "integrity": "sha512-oNwn+BAt3n9dK9uAYvI+XGlutwuTq/wfj4xCBaZCqwwVIGtD7D6ViihEbyYZrDHIHTDE3Q6oL3/hqmAyFEy9DQ==",
-      "dependencies": {
-        "@babel/parser": "^7.25.3",
-        "@vue/shared": "3.5.4",
-        "entities": "^4.5.0",
-        "estree-walker": "^2.0.2",
-        "source-map-js": "^1.2.0"
-      }
-    },
-    "node_modules/@vue/compiler-core/node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
-    },
-    "node_modules/@vue/compiler-dom": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.4.tgz",
-      "integrity": "sha512-yP9RRs4BDLOLfldn6ah+AGCNovGjMbL9uHvhDHf5wan4dAHLnFGOkqtfE7PPe4HTXIqE7l/NILdYw53bo1C8jw==",
-      "dependencies": {
-        "@vue/compiler-core": "3.5.4",
-        "@vue/shared": "3.5.4"
-      }
-    },
-    "node_modules/@vue/compiler-sfc": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.4.tgz",
-      "integrity": "sha512-P+yiPhL+NYH7m0ZgCq7AQR2q7OIE+mpAEgtkqEeH9oHSdIRvUO+4X6MPvblJIWcoe4YC5a2Gdf/RsoyP8FFiPQ==",
-      "dependencies": {
-        "@babel/parser": "^7.25.3",
-        "@vue/compiler-core": "3.5.4",
-        "@vue/compiler-dom": "3.5.4",
-        "@vue/compiler-ssr": "3.5.4",
-        "@vue/shared": "3.5.4",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.11",
-        "postcss": "^8.4.44",
-        "source-map-js": "^1.2.0"
-      }
-    },
-    "node_modules/@vue/compiler-sfc/node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
-    },
-    "node_modules/@vue/compiler-ssr": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.4.tgz",
-      "integrity": "sha512-acESdTXsxPnYr2C4Blv0ggx5zIFMgOzZmYU2UgvIff9POdRGbRNBHRyzHAnizcItvpgerSKQbllUc9USp3V7eg==",
-      "dependencies": {
-        "@vue/compiler-dom": "3.5.4",
-        "@vue/shared": "3.5.4"
-      }
-    },
-    "node_modules/@vue/reactivity": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.4.tgz",
-      "integrity": "sha512-HKKbEuP7tYSGCq4e4nK6ZW6l5hyG66OUetefBp4budUyjvAYsnQDf+bgFzg2RAgnH0CInyqXwD9y47jwJEHrQw==",
-      "dependencies": {
-        "@vue/shared": "3.5.4"
-      }
-    },
-    "node_modules/@vue/runtime-core": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.4.tgz",
-      "integrity": "sha512-f3ek2sTA0AFu0n+w+kCtz567Euqqa3eHewvo4klwS7mWfSj/A+UmYTwsnUFo35KeyAFY60JgrCGvEBsu1n/3LA==",
-      "dependencies": {
-        "@vue/reactivity": "3.5.4",
-        "@vue/shared": "3.5.4"
-      }
-    },
-    "node_modules/@vue/runtime-dom": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.4.tgz",
-      "integrity": "sha512-ofyc0w6rbD5KtjhP1i9hGOKdxGpvmuB1jprP7Djlj0X7R5J/oLwuNuE98GJ8WW31Hu2VxQHtk/LYTAlW8xrJdw==",
-      "dependencies": {
-        "@vue/reactivity": "3.5.4",
-        "@vue/runtime-core": "3.5.4",
-        "@vue/shared": "3.5.4",
-        "csstype": "^3.1.3"
-      }
-    },
-    "node_modules/@vue/server-renderer": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.4.tgz",
-      "integrity": "sha512-FbjV6DJLgKRetMYFBA1UXCroCiED/Ckr53/ba9wivyd7D/Xw9fpo0T6zXzCnxQwyvkyrL7y6plgYhWhNjGxY5g==",
-      "dependencies": {
-        "@vue/compiler-ssr": "3.5.4",
-        "@vue/shared": "3.5.4"
-      },
-      "peerDependencies": {
-        "vue": "3.5.4"
-      }
-    },
-    "node_modules/@vue/shared": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.4.tgz",
-      "integrity": "sha512-L2MCDD8l7yC62Te5UUyPVpmexhL9ipVnYRw9CsWfm/BGRL5FwDX4a25bcJ/OJSD3+Hx+k/a8LDKcG2AFdJV3BA=="
-    },
-    "node_modules/@vuetify/loader-shared": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz",
-      "integrity": "sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "upath": "^2.0.1"
-      },
-      "peerDependencies": {
-        "vue": "^3.0.0",
-        "vuetify": "^3.0.0"
-      }
-    },
-    "node_modules/@vueuse/core": {
-      "version": "10.11.1",
-      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
-      "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
-      "dependencies": {
-        "@types/web-bluetooth": "^0.0.20",
-        "@vueuse/metadata": "10.11.1",
-        "@vueuse/shared": "10.11.1",
-        "vue-demi": ">=0.14.8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      }
-    },
-    "node_modules/@vueuse/core/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
-      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
-      "hasInstallScript": true,
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vueuse/metadata": {
-      "version": "10.11.1",
-      "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
-      "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      }
-    },
-    "node_modules/@vueuse/shared": {
-      "version": "10.11.1",
-      "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
-      "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
-      "dependencies": {
-        "vue-demi": ">=0.14.8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      }
-    },
-    "node_modules/@vueuse/shared/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
-      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
-      "hasInstallScript": true,
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/acorn": {
-      "version": "8.12.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
-      "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
-      "devOptional": true,
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/acorn-walk": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
-      "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/agent-base": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
-      "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/ajv": {
-      "version": "6.12.6",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/ansi-escapes": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
-      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
-      "dev": true,
-      "dependencies": {
-        "type-fest": "^0.21.3"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/ansi-regex": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
-      "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/ansi-styles": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
-      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/any-promise": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
-      "dev": true
-    },
-    "node_modules/anymatch": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
-      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
-      "dependencies": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/arg": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
-      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
-      "dev": true
-    },
-    "node_modules/argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
-      "dependencies": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "node_modules/array-union": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
-      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
-    },
-    "node_modules/babel-jest": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
-      "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
-      "dev": true,
-      "dependencies": {
-        "@jest/transform": "^29.7.0",
-        "@types/babel__core": "^7.1.14",
-        "babel-plugin-istanbul": "^6.1.1",
-        "babel-preset-jest": "^29.6.3",
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.8.0"
-      }
-    },
-    "node_modules/babel-plugin-istanbul": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
-      "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.0.0",
-        "@istanbuljs/load-nyc-config": "^1.0.0",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-instrument": "^5.0.4",
-        "test-exclude": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
-      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.12.3",
-        "@babel/parser": "^7.14.7",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^6.3.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/babel-plugin-jest-hoist": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
-      "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/template": "^7.3.3",
-        "@babel/types": "^7.3.3",
-        "@types/babel__core": "^7.1.14",
-        "@types/babel__traverse": "^7.0.6"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/babel-preset-current-node-syntax": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
-      "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/plugin-syntax-async-generators": "^7.8.4",
-        "@babel/plugin-syntax-bigint": "^7.8.3",
-        "@babel/plugin-syntax-class-properties": "^7.8.3",
-        "@babel/plugin-syntax-import-meta": "^7.8.3",
-        "@babel/plugin-syntax-json-strings": "^7.8.3",
-        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
-        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
-        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
-        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
-        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
-        "@babel/plugin-syntax-top-level-await": "^7.8.3"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/babel-preset-jest": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
-      "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
-      "dev": true,
-      "dependencies": {
-        "babel-plugin-jest-hoist": "^29.6.3",
-        "babel-preset-current-node-syntax": "^1.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
-    },
-    "node_modules/binary-extensions": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
-      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "dependencies": {
-        "fill-range": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/browserslist": {
-      "version": "4.23.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
-      "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "dependencies": {
-        "caniuse-lite": "^1.0.30001646",
-        "electron-to-chromium": "^1.5.4",
-        "node-releases": "^2.0.18",
-        "update-browserslist-db": "^1.1.0"
-      },
-      "bin": {
-        "browserslist": "cli.js"
-      },
-      "engines": {
-        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
-      }
-    },
-    "node_modules/bs-logger": {
-      "version": "0.2.6",
-      "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
-      "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
-      "dev": true,
-      "dependencies": {
-        "fast-json-stable-stringify": "2.x"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/bser": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
-      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
-      "dev": true,
-      "dependencies": {
-        "node-int64": "^0.4.0"
-      }
-    },
-    "node_modules/buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "devOptional": true
-    },
-    "node_modules/bundle-require": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.1.0.tgz",
-      "integrity": "sha512-FeArRFM+ziGkRViKRnSTbHZc35dgmR9yNog05Kn0+ItI59pOAISGvnnIwW1WgFZQW59IxD9QpJnUPkdIPfZuXg==",
-      "dev": true,
-      "dependencies": {
-        "load-tsconfig": "^0.2.3"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "peerDependencies": {
-        "esbuild": ">=0.17"
-      }
-    },
-    "node_modules/cac": {
-      "version": "6.7.14",
-      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
-      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/caniuse-lite": {
-      "version": "1.0.30001660",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
-      "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ]
-    },
-    "node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/chalk/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/char-regex": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
-      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
-      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
-      "dev": true,
-      "dependencies": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "engines": {
-        "node": ">= 8.10.0"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.2"
-      }
-    },
-    "node_modules/ci-info": {
-      "version": "3.9.0",
-      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
-      "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/sibiraj-s"
-        }
-      ],
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cjs-module-lexer": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
-      "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
-      "dev": true
-    },
-    "node_modules/cliui": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
-      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
-      "dev": true,
-      "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.1",
-        "wrap-ansi": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/cliui/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/cliui/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
-    },
-    "node_modules/cliui/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
-      "dev": true,
-      "engines": {
-        "iojs": ">= 1.0.0",
-        "node": ">= 0.12.0"
-      }
-    },
-    "node_modules/collect-v8-coverage": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
-      "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
-      "dev": true
-    },
-    "node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
-    },
-    "node_modules/combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/commander": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
-      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
-      "dev": true
-    },
-    "node_modules/convert-source-map": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
-      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
-      "dev": true
-    },
-    "node_modules/cose-base": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
-      "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
-      "dev": true,
-      "dependencies": {
-        "layout-base": "^2.0.0"
-      }
-    },
-    "node_modules/create-jest": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
-      "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "exit": "^0.1.2",
-        "graceful-fs": "^4.2.9",
-        "jest-config": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "prompts": "^2.0.1"
-      },
-      "bin": {
-        "create-jest": "bin/create-jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/create-require": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
-      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
-      "dev": true
-    },
-    "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
-      "dev": true,
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/cssstyle": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
-      "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
-      "dev": true,
-      "dependencies": {
-        "rrweb-cssom": "^0.7.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/csstype": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
-      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
-    },
-    "node_modules/cytoscape": {
-      "version": "3.30.2",
-      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.2.tgz",
-      "integrity": "sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/cytoscape-cose-bilkent": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-3.0.4.tgz",
-      "integrity": "sha512-oAh/ga1fxJ7j9bBjjfGRlpTAKVFddYu+HfwJaebLpZTc0LUnnnHe/Ng3aiWj1Ammc301+RDTSXT6ecpIz4dSMQ==",
-      "dev": true,
-      "dependencies": {
-        "linkedlist-js": "1.3.0"
-      },
-      "peerDependencies": {
-        "cytoscape": "^2.4.0 || ^3.0.0"
-      }
-    },
-    "node_modules/cytoscape-fcose": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
-      "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
-      "dev": true,
-      "dependencies": {
-        "cose-base": "^2.2.0"
-      },
-      "peerDependencies": {
-        "cytoscape": "^3.2.0"
-      }
-    },
-    "node_modules/d3": {
-      "version": "7.9.0",
-      "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
-      "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
-      "dependencies": {
-        "d3-array": "3",
-        "d3-axis": "3",
-        "d3-brush": "3",
-        "d3-chord": "3",
-        "d3-color": "3",
-        "d3-contour": "4",
-        "d3-delaunay": "6",
-        "d3-dispatch": "3",
-        "d3-drag": "3",
-        "d3-dsv": "3",
-        "d3-ease": "3",
-        "d3-fetch": "3",
-        "d3-force": "3",
-        "d3-format": "3",
-        "d3-geo": "3",
-        "d3-hierarchy": "3",
-        "d3-interpolate": "3",
-        "d3-path": "3",
-        "d3-polygon": "3",
-        "d3-quadtree": "3",
-        "d3-random": "3",
-        "d3-scale": "4",
-        "d3-scale-chromatic": "3",
-        "d3-selection": "3",
-        "d3-shape": "3",
-        "d3-time": "3",
-        "d3-time-format": "4",
-        "d3-timer": "3",
-        "d3-transition": "3",
-        "d3-zoom": "3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-array": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
-      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
-      "dependencies": {
-        "internmap": "1 - 2"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-axis": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
-      "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-brush": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
-      "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
-      "dependencies": {
-        "d3-dispatch": "1 - 3",
-        "d3-drag": "2 - 3",
-        "d3-interpolate": "1 - 3",
-        "d3-selection": "3",
-        "d3-transition": "3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-chord": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
-      "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
-      "dependencies": {
-        "d3-path": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-color": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
-      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-contour": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
-      "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
-      "dependencies": {
-        "d3-array": "^3.2.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-delaunay": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
-      "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
-      "dependencies": {
-        "delaunator": "5"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-dispatch": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
-      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-drag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
-      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
-      "dependencies": {
-        "d3-dispatch": "1 - 3",
-        "d3-selection": "3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-dsv": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
-      "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
-      "dependencies": {
-        "commander": "7",
-        "iconv-lite": "0.6",
-        "rw": "1"
-      },
-      "bin": {
-        "csv2json": "bin/dsv2json.js",
-        "csv2tsv": "bin/dsv2dsv.js",
-        "dsv2dsv": "bin/dsv2dsv.js",
-        "dsv2json": "bin/dsv2json.js",
-        "json2csv": "bin/json2dsv.js",
-        "json2dsv": "bin/json2dsv.js",
-        "json2tsv": "bin/json2dsv.js",
-        "tsv2csv": "bin/dsv2dsv.js",
-        "tsv2json": "bin/dsv2json.js"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-dsv/node_modules/commander": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
-      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/d3-dsv/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/d3-ease": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
-      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-fetch": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
-      "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
-      "dependencies": {
-        "d3-dsv": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-force": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
-      "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
-      "dependencies": {
-        "d3-dispatch": "1 - 3",
-        "d3-quadtree": "1 - 3",
-        "d3-timer": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-format": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
-      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-geo": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
-      "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
-      "dependencies": {
-        "d3-array": "2.5.0 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-hierarchy": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
-      "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-interpolate": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
-      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
-      "dependencies": {
-        "d3-color": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-path": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
-      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-polygon": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
-      "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-quadtree": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
-      "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-random": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
-      "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-scale": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
-      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
-      "dependencies": {
-        "d3-array": "2.10.0 - 3",
-        "d3-format": "1 - 3",
-        "d3-interpolate": "1.2.0 - 3",
-        "d3-time": "2.1.1 - 3",
-        "d3-time-format": "2 - 4"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-scale-chromatic": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
-      "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
-      "dependencies": {
-        "d3-color": "1 - 3",
-        "d3-interpolate": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-selection": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
-      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-shape": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
-      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
-      "dependencies": {
-        "d3-path": "^3.1.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-time": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
-      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
-      "dependencies": {
-        "d3-array": "2 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-time-format": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
-      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
-      "dependencies": {
-        "d3-time": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-timer": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
-      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/d3-transition": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
-      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
-      "dependencies": {
-        "d3-color": "1 - 3",
-        "d3-dispatch": "1 - 3",
-        "d3-ease": "1 - 3",
-        "d3-interpolate": "1 - 3",
-        "d3-timer": "1 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "peerDependencies": {
-        "d3-selection": "2 - 3"
-      }
-    },
-    "node_modules/d3-zoom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
-      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
-      "dependencies": {
-        "d3-dispatch": "1 - 3",
-        "d3-drag": "2 - 3",
-        "d3-interpolate": "1 - 3",
-        "d3-selection": "2 - 3",
-        "d3-transition": "2 - 3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/dagrejs": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/dagrejs/-/dagrejs-0.2.1.tgz",
-      "integrity": "sha512-4bb1y+4aM1xtkK7ieP0V7Xn/34GQfnCapl0yubrOMX8Qb/PIwM1Dii2uUBv/KtuzrQtxPliSP4r5MQBsnP6gNg==",
-      "dev": true,
-      "dependencies": {
-        "graphlib": "^2.1.8",
-        "lodash": "^4.17.19"
-      }
-    },
-    "node_modules/data-urls": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
-      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
-      "dev": true,
-      "dependencies": {
-        "whatwg-mimetype": "^4.0.0",
-        "whatwg-url": "^14.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/data-urls/node_modules/tr46": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
-      "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
-      "dev": true,
-      "dependencies": {
-        "punycode": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/data-urls/node_modules/webidl-conversions": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
-      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/data-urls/node_modules/whatwg-url": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
-      "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
-      "dev": true,
-      "dependencies": {
-        "tr46": "^5.0.0",
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/debug": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
-      "devOptional": true,
-      "dependencies": {
-        "ms": "2.1.2"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/decimal.js": {
-      "version": "10.4.3",
-      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
-      "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
-      "dev": true
-    },
-    "node_modules/dedent": {
-      "version": "1.5.3",
-      "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
-      "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
-      "dev": true,
-      "peerDependencies": {
-        "babel-plugin-macros": "^3.1.0"
-      },
-      "peerDependenciesMeta": {
-        "babel-plugin-macros": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/deepmerge": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
-      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/delaunator": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
-      "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
-      "dependencies": {
-        "robust-predicates": "^3.0.2"
-      }
-    },
-    "node_modules/delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/detect-newline": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
-      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/diff": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
-      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.3.1"
-      }
-    },
-    "node_modules/diff-sequences": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
-      "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
-      "dev": true,
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/dir-glob": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
-      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
-      "dev": true,
-      "dependencies": {
-        "path-type": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/eastasianwidth": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
-      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
-      "dev": true
-    },
-    "node_modules/electron-to-chromium": {
-      "version": "1.5.18",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz",
-      "integrity": "sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==",
-      "dev": true
-    },
-    "node_modules/emittery": {
-      "version": "0.13.1",
-      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
-      "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
-      }
-    },
-    "node_modules/emoji-regex": {
-      "version": "9.2.2",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
-      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
-      "dev": true
-    },
-    "node_modules/entities": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
-      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
-      "engines": {
-        "node": ">=0.12"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
-      "dependencies": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "node_modules/esbuild": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
-      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
-      "dev": true,
-      "hasInstallScript": true,
-      "bin": {
-        "esbuild": "bin/esbuild"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.19.12",
-        "@esbuild/android-arm": "0.19.12",
-        "@esbuild/android-arm64": "0.19.12",
-        "@esbuild/android-x64": "0.19.12",
-        "@esbuild/darwin-arm64": "0.19.12",
-        "@esbuild/darwin-x64": "0.19.12",
-        "@esbuild/freebsd-arm64": "0.19.12",
-        "@esbuild/freebsd-x64": "0.19.12",
-        "@esbuild/linux-arm": "0.19.12",
-        "@esbuild/linux-arm64": "0.19.12",
-        "@esbuild/linux-ia32": "0.19.12",
-        "@esbuild/linux-loong64": "0.19.12",
-        "@esbuild/linux-mips64el": "0.19.12",
-        "@esbuild/linux-ppc64": "0.19.12",
-        "@esbuild/linux-riscv64": "0.19.12",
-        "@esbuild/linux-s390x": "0.19.12",
-        "@esbuild/linux-x64": "0.19.12",
-        "@esbuild/netbsd-x64": "0.19.12",
-        "@esbuild/openbsd-x64": "0.19.12",
-        "@esbuild/sunos-x64": "0.19.12",
-        "@esbuild/win32-arm64": "0.19.12",
-        "@esbuild/win32-ia32": "0.19.12",
-        "@esbuild/win32-x64": "0.19.12"
-      }
-    },
-    "node_modules/escalade": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
-      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/escape-string-regexp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
-      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true,
-      "bin": {
-        "esparse": "bin/esparse.js",
-        "esvalidate": "bin/esvalidate.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/execa": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
-      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
-      "dev": true,
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.0",
-        "human-signals": "^2.1.0",
-        "is-stream": "^2.0.0",
-        "merge-stream": "^2.0.0",
-        "npm-run-path": "^4.0.1",
-        "onetime": "^5.1.2",
-        "signal-exit": "^3.0.3",
-        "strip-final-newline": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/execa?sponsor=1"
-      }
-    },
-    "node_modules/exit": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/expect": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
-      "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/expect-utils": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/fast-deep-equal": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/fast-glob": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
-      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
-      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
-      "dev": true
-    },
-    "node_modules/fastq": {
-      "version": "1.17.1",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
-      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
-      "dev": true,
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/fb-watchman": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
-      "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
-      "dev": true,
-      "dependencies": {
-        "bser": "2.1.1"
-      }
-    },
-    "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/find-up": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-      "dev": true,
-      "dependencies": {
-        "locate-path": "^5.0.0",
-        "path-exists": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/foreground-child": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
-      "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
-      "dev": true,
-      "dependencies": {
-        "cross-spawn": "^7.0.0",
-        "signal-exit": "^4.0.1"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/foreground-child/node_modules/signal-exit": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
-      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
-      "dev": true,
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/form-data": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
-      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "mime-types": "^2.1.12"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "dev": true
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "hasInstallScript": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/function-bind": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
-      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "dev": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true,
-      "engines": {
-        "node": "6.* || 8.* || >= 10.*"
-      }
-    },
-    "node_modules/get-package-type": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
-      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/get-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/glob": {
-      "version": "10.3.15",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz",
-      "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==",
-      "dev": true,
-      "dependencies": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.6",
-        "minimatch": "^9.0.1",
-        "minipass": "^7.0.4",
-        "path-scurry": "^1.11.0"
-      },
-      "bin": {
-        "glob": "dist/esm/bin.mjs"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/globals": {
-      "version": "11.12.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/globby": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
-      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
-      "dev": true,
-      "dependencies": {
-        "array-union": "^2.1.0",
-        "dir-glob": "^3.0.1",
-        "fast-glob": "^3.2.9",
-        "ignore": "^5.2.0",
-        "merge2": "^1.4.1",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/graceful-fs": {
-      "version": "4.2.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
-      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
-      "dev": true
-    },
-    "node_modules/graph-data-structure": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/graph-data-structure/-/graph-data-structure-3.5.0.tgz",
-      "integrity": "sha512-AAgjRtBZC1acIExgK2otv2LDdcYeZdQFKiEStXRDTyaVs6sUUaGUif05pCczTqAU4ny82NQtM1p5PK7AQEYgRA==",
-      "dev": true
-    },
-    "node_modules/graphlib": {
-      "version": "2.1.8",
-      "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
-      "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
-      "dev": true,
-      "dependencies": {
-        "lodash": "^4.17.15"
-      }
-    },
-    "node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/hasown": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
-      "dependencies": {
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/html-encoding-sniffer": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
-      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
-      "dev": true,
-      "dependencies": {
-        "whatwg-encoding": "^3.1.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/html-escaper": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
-      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
-      "dev": true
-    },
-    "node_modules/http-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
-      "dev": true,
-      "dependencies": {
-        "agent-base": "^7.1.0",
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/https-proxy-agent": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
-      "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
-      "dev": true,
-      "dependencies": {
-        "agent-base": "^7.0.2",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/human-signals": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
-      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.17.0"
-      }
-    },
-    "node_modules/ignore": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
-      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
-      "dev": true,
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/import-lazy": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
-      "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/import-local": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
-      "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
-      "dev": true,
-      "dependencies": {
-        "pkg-dir": "^4.2.0",
-        "resolve-cwd": "^3.0.0"
-      },
-      "bin": {
-        "import-local-fixture": "fixtures/cli.js"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.8.19"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "dev": true,
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "node_modules/internmap": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
-      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
-      "dev": true
-    },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "dependencies": {
-        "binary-extensions": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
-      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
-      "dev": true,
-      "dependencies": {
-        "hasown": "^2.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-generator-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
-      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/is-potential-custom-element-name": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
-      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
-      "dev": true
-    },
-    "node_modules/is-stream": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true
-    },
-    "node_modules/istanbul-lib-coverage": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
-      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/istanbul-lib-instrument": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz",
-      "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.23.9",
-        "@babel/parser": "^7.23.9",
-        "@istanbuljs/schema": "^0.1.3",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^7.5.4"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-lib-instrument/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-lib-report": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
-      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
-      "dev": true,
-      "dependencies": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "make-dir": "^4.0.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-lib-source-maps": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.1.1",
-        "istanbul-lib-coverage": "^3.0.0",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/istanbul-reports": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
-      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
-      "dev": true,
-      "dependencies": {
-        "html-escaper": "^2.0.0",
-        "istanbul-lib-report": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/jackspeak": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
-      "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
-      "dev": true,
-      "dependencies": {
-        "@isaacs/cliui": "^8.0.2"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      },
-      "optionalDependencies": {
-        "@pkgjs/parseargs": "^0.11.0"
-      }
-    },
-    "node_modules/jest": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
-      "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/core": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "import-local": "^3.0.2",
-        "jest-cli": "^29.7.0"
-      },
-      "bin": {
-        "jest": "bin/jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-changed-files": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
-      "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
-      "dev": true,
-      "dependencies": {
-        "execa": "^5.0.0",
-        "jest-util": "^29.7.0",
-        "p-limit": "^3.1.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-circus": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
-      "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/expect": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "co": "^4.6.0",
-        "dedent": "^1.0.0",
-        "is-generator-fn": "^2.0.0",
-        "jest-each": "^29.7.0",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "p-limit": "^3.1.0",
-        "pretty-format": "^29.7.0",
-        "pure-rand": "^6.0.0",
-        "slash": "^3.0.0",
-        "stack-utils": "^2.0.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-cli": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
-      "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
-      "dev": true,
-      "dependencies": {
-        "@jest/core": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "create-jest": "^29.7.0",
-        "exit": "^0.1.2",
-        "import-local": "^3.0.2",
-        "jest-config": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "yargs": "^17.3.1"
-      },
-      "bin": {
-        "jest": "bin/jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-config": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
-      "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@jest/test-sequencer": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "babel-jest": "^29.7.0",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "deepmerge": "^4.2.2",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "jest-circus": "^29.7.0",
-        "jest-environment-node": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-runner": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "parse-json": "^5.2.0",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@types/node": "*",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-config/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/jest-config/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/jest-config/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/jest-diff": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
-      "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
-      "dev": true,
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "diff-sequences": "^29.6.3",
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-docblock": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
-      "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
-      "dev": true,
-      "dependencies": {
-        "detect-newline": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-each": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
-      "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "jest-get-type": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-environment-node": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
-      "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-get-type": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
-      "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
-      "dev": true,
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-haste-map": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
-      "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/graceful-fs": "^4.1.3",
-        "@types/node": "*",
-        "anymatch": "^3.0.3",
-        "fb-watchman": "^2.0.0",
-        "graceful-fs": "^4.2.9",
-        "jest-regex-util": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "walker": "^1.0.8"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "optionalDependencies": {
-        "fsevents": "^2.3.2"
-      }
-    },
-    "node_modules/jest-leak-detector": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
-      "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
-      "dev": true,
-      "dependencies": {
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-matcher-utils": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
-      "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
-      "dev": true,
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "jest-diff": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-message-util": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
-      "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
-      "dev": true,
-      "dependencies": {
-        "@babel/code-frame": "^7.12.13",
-        "@jest/types": "^29.6.3",
-        "@types/stack-utils": "^2.0.0",
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "micromatch": "^4.0.4",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "stack-utils": "^2.0.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-mock": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
-      "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-pnp-resolver": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
-      "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      },
-      "peerDependencies": {
-        "jest-resolve": "*"
-      },
-      "peerDependenciesMeta": {
-        "jest-resolve": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-regex-util": {
-      "version": "29.6.3",
-      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
-      "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
-      "dev": true,
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-resolve": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
-      "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
-      "dev": true,
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-pnp-resolver": "^1.2.2",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "resolve": "^1.20.0",
-        "resolve.exports": "^2.0.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-resolve-dependencies": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
-      "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
-      "dev": true,
-      "dependencies": {
-        "jest-regex-util": "^29.6.3",
-        "jest-snapshot": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runner": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
-      "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/environment": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "emittery": "^0.13.1",
-        "graceful-fs": "^4.2.9",
-        "jest-docblock": "^29.7.0",
-        "jest-environment-node": "^29.7.0",
-        "jest-haste-map": "^29.7.0",
-        "jest-leak-detector": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-resolve": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-watcher": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "p-limit": "^3.1.0",
-        "source-map-support": "0.5.13"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runtime": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
-      "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/globals": "^29.7.0",
-        "@jest/source-map": "^29.6.3",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "cjs-module-lexer": "^1.0.0",
-        "collect-v8-coverage": "^1.0.0",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-mock": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-bom": "^4.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runtime/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/jest-runtime/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/jest-runtime/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/jest-snapshot": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
-      "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@babel/generator": "^7.7.2",
-        "@babel/plugin-syntax-jsx": "^7.7.2",
-        "@babel/plugin-syntax-typescript": "^7.7.2",
-        "@babel/types": "^7.3.3",
-        "@jest/expect-utils": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "babel-preset-current-node-syntax": "^1.0.0",
-        "chalk": "^4.0.0",
-        "expect": "^29.7.0",
-        "graceful-fs": "^4.2.9",
-        "jest-diff": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "natural-compare": "^1.4.0",
-        "pretty-format": "^29.7.0",
-        "semver": "^7.5.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-snapshot/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/jest-util": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
-      "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "graceful-fs": "^4.2.9",
-        "picomatch": "^2.2.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-validate": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
-      "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
-      "dev": true,
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "camelcase": "^6.2.0",
-        "chalk": "^4.0.0",
-        "jest-get-type": "^29.6.3",
-        "leven": "^3.1.0",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-validate/node_modules/camelcase": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
-      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/jest-watcher": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
-      "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
-      "dev": true,
-      "dependencies": {
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^4.0.0",
-        "emittery": "^0.13.1",
-        "jest-util": "^29.7.0",
-        "string-length": "^4.0.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-worker": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
-      "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
-      "dev": true,
-      "dependencies": {
-        "@types/node": "*",
-        "jest-util": "^29.7.0",
-        "merge-stream": "^2.0.0",
-        "supports-color": "^8.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-worker/node_modules/supports-color": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/jju": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
-      "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/joycon": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
-      "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "dev": true
-    },
-    "node_modules/js-yaml": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
-      "dev": true,
-      "dependencies": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
-    "node_modules/jsdom": {
-      "version": "24.1.3",
-      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz",
-      "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==",
-      "dev": true,
-      "dependencies": {
-        "cssstyle": "^4.0.1",
-        "data-urls": "^5.0.0",
-        "decimal.js": "^10.4.3",
-        "form-data": "^4.0.0",
-        "html-encoding-sniffer": "^4.0.0",
-        "http-proxy-agent": "^7.0.2",
-        "https-proxy-agent": "^7.0.5",
-        "is-potential-custom-element-name": "^1.0.1",
-        "nwsapi": "^2.2.12",
-        "parse5": "^7.1.2",
-        "rrweb-cssom": "^0.7.1",
-        "saxes": "^6.0.0",
-        "symbol-tree": "^3.2.4",
-        "tough-cookie": "^4.1.4",
-        "w3c-xmlserializer": "^5.0.0",
-        "webidl-conversions": "^7.0.0",
-        "whatwg-encoding": "^3.1.1",
-        "whatwg-mimetype": "^4.0.0",
-        "whatwg-url": "^14.0.0",
-        "ws": "^8.18.0",
-        "xml-name-validator": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "canvas": "^2.11.2"
-      },
-      "peerDependenciesMeta": {
-        "canvas": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jsdom/node_modules/tr46": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
-      "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
-      "dev": true,
-      "dependencies": {
-        "punycode": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/jsdom/node_modules/webidl-conversions": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
-      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/jsdom/node_modules/whatwg-url": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
-      "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
-      "dev": true,
-      "dependencies": {
-        "tr46": "^5.0.0",
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/jsdom/node_modules/xml-name-validator": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
-      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
-      "dev": true,
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/jsesc": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
-      "dev": true,
-      "bin": {
-        "jsesc": "bin/jsesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
-    },
-    "node_modules/json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/json5": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
-      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
-      "dev": true,
-      "bin": {
-        "json5": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/kleur": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
-      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/layout-base": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
-      "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
-      "dev": true
-    },
-    "node_modules/leven": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
-      "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/lilconfig": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
-      "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antonk52"
-      }
-    },
-    "node_modules/line-intersect": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/line-intersect/-/line-intersect-3.0.0.tgz",
-      "integrity": "sha512-MAkwgBwwpyJhTWZ9EppTTFD6JxzYgh3BZxgI41CA2vSlANu4s5CXyTEJxKU3LRHitcF2vObHvE/s/lHxvKl2Kw=="
-    },
-    "node_modules/lines-and-columns": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
-      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
-      "dev": true
-    },
-    "node_modules/linkedlist-js": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/linkedlist-js/-/linkedlist-js-1.3.0.tgz",
-      "integrity": "sha512-YwgG4Et8dJF04nsn9YuyrydUJvwmJHOQo7PzxvkT09NTgQ1yC+vXHGBolo48rTjAItIYR7YWIXh6xZsavCBSvQ==",
-      "dev": true
-    },
-    "node_modules/load-tsconfig": {
-      "version": "0.2.5",
-      "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz",
-      "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==",
-      "dev": true,
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      }
-    },
-    "node_modules/locate-path": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-      "dev": true,
-      "dependencies": {
-        "p-locate": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
-    },
-    "node_modules/lodash-es": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
-      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
-    },
-    "node_modules/lodash.get": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/lodash.isequal": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-      "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
-      "dev": true,
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/lodash.memoize": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
-      "dev": true
-    },
-    "node_modules/lodash.sortby": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
-      "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
-      "dev": true
-    },
-    "node_modules/lru-cache": {
-      "version": "10.2.2",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
-      "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
-      "dev": true,
-      "engines": {
-        "node": "14 || >=16.14"
-      }
-    },
-    "node_modules/magic-string": {
-      "version": "0.30.11",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
-      "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
-      "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.5.0"
-      }
-    },
-    "node_modules/make-dir": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
-      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
-      "dev": true,
-      "dependencies": {
-        "semver": "^7.5.3"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/make-dir/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/make-error": {
-      "version": "1.3.6",
-      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
-      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
-      "dev": true
-    },
-    "node_modules/makeerror": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
-      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
-      "dev": true,
-      "dependencies": {
-        "tmpl": "1.0.5"
-      }
-    },
-    "node_modules/merge-stream": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
-      "dev": true
-    },
-    "node_modules/merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/micromatch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
-      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
-      "dev": true,
-      "dependencies": {
-        "braces": "^3.0.2",
-        "picomatch": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/mime-db": {
-      "version": "1.52.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mime-types": {
-      "version": "2.1.35",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
-      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "dev": true,
-      "dependencies": {
-        "mime-db": "1.52.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/minimatch": {
-      "version": "9.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
-      "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/minipass": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz",
-      "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==",
-      "dev": true,
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      }
-    },
-    "node_modules/ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "devOptional": true
-    },
-    "node_modules/mz": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
-      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
-      "dev": true,
-      "dependencies": {
-        "any-promise": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "thenify-all": "^1.0.0"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.7",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
-      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
-      "dev": true
-    },
-    "node_modules/node-int64": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
-      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
-      "dev": true
-    },
-    "node_modules/node-releases": {
-      "version": "2.0.18",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
-      "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
-      "dev": true
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/npm-run-path": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
-      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
-      "dev": true,
-      "dependencies": {
-        "path-key": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/nwsapi": {
-      "version": "2.2.12",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz",
-      "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==",
-      "dev": true
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/onetime": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-      "dev": true,
-      "dependencies": {
-        "mimic-fn": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-limit": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-      "dev": true,
-      "dependencies": {
-        "yocto-queue": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-locate": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-      "dev": true,
-      "dependencies": {
-        "p-limit": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/p-locate/node_modules/p-limit": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "dev": true,
-      "dependencies": {
-        "p-try": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/parse-json": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
-      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/code-frame": "^7.0.0",
-        "error-ex": "^1.3.1",
-        "json-parse-even-better-errors": "^2.3.0",
-        "lines-and-columns": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/parse5": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
-      "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
-      "dev": true,
-      "dependencies": {
-        "entities": "^4.4.0"
-      },
-      "funding": {
-        "url": "https://github.com/inikulin/parse5?sponsor=1"
-      }
-    },
-    "node_modules/path-exists": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/path-key": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
-    "node_modules/path-scurry": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
-      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
-      "dev": true,
-      "dependencies": {
-        "lru-cache": "^10.2.0",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/path-type": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
-      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/picocolors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
-      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
-    },
-    "node_modules/picomatch": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/pirates": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
-      "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/pkg-dir": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
-      "dev": true,
-      "dependencies": {
-        "find-up": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.4.45",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
-      "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/postcss"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "dependencies": {
-        "nanoid": "^3.3.7",
-        "picocolors": "^1.0.1",
-        "source-map-js": "^1.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/postcss-load-config": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
-      "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "dependencies": {
-        "lilconfig": "^3.0.0",
-        "yaml": "^2.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "postcss": ">=8.0.9",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "postcss": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/prettier": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
-      "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
-      "dev": true,
-      "bin": {
-        "prettier": "bin/prettier.cjs"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/prettier/prettier?sponsor=1"
-      }
-    },
-    "node_modules/pretty-format": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
-      "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
-      "dev": true,
-      "dependencies": {
-        "@jest/schemas": "^29.6.3",
-        "ansi-styles": "^5.0.0",
-        "react-is": "^18.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/pretty-format/node_modules/ansi-styles": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
-      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/prompts": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
-      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
-      "dev": true,
-      "dependencies": {
-        "kleur": "^3.0.3",
-        "sisteransi": "^1.0.5"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/psl": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
-      "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
-      "dev": true
-    },
-    "node_modules/punycode": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
-      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/pure-rand": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
-      "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://github.com/sponsors/dubzzz"
-        },
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/fast-check"
-        }
-      ]
-    },
-    "node_modules/querystringify": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
-      "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
-      "dev": true
-    },
-    "node_modules/queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
-    },
-    "node_modules/react-is": {
-      "version": "18.3.1",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
-      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
-      "dev": true
-    },
-    "node_modules/readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/requires-port": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
-      "dev": true
-    },
-    "node_modules/resolve": {
-      "version": "1.22.8",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
-      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "dev": true,
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/resolve-cwd": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
-      "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
-      "dev": true,
-      "dependencies": {
-        "resolve-from": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/resolve-from": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/resolve.exports": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
-      "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/roboto-fontface": {
-      "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
-      "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
-    },
-    "node_modules/robust-predicates": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
-      "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
-    },
-    "node_modules/rollup": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz",
-      "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==",
-      "devOptional": true,
-      "dependencies": {
-        "@types/estree": "1.0.5"
-      },
-      "bin": {
-        "rollup": "dist/bin/rollup"
-      },
-      "engines": {
-        "node": ">=18.0.0",
-        "npm": ">=8.0.0"
-      },
-      "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.21.2",
-        "@rollup/rollup-android-arm64": "4.21.2",
-        "@rollup/rollup-darwin-arm64": "4.21.2",
-        "@rollup/rollup-darwin-x64": "4.21.2",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.21.2",
-        "@rollup/rollup-linux-arm-musleabihf": "4.21.2",
-        "@rollup/rollup-linux-arm64-gnu": "4.21.2",
-        "@rollup/rollup-linux-arm64-musl": "4.21.2",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2",
-        "@rollup/rollup-linux-riscv64-gnu": "4.21.2",
-        "@rollup/rollup-linux-s390x-gnu": "4.21.2",
-        "@rollup/rollup-linux-x64-gnu": "4.21.2",
-        "@rollup/rollup-linux-x64-musl": "4.21.2",
-        "@rollup/rollup-win32-arm64-msvc": "4.21.2",
-        "@rollup/rollup-win32-ia32-msvc": "4.21.2",
-        "@rollup/rollup-win32-x64-msvc": "4.21.2",
-        "fsevents": "~2.3.2"
-      }
-    },
-    "node_modules/rrweb-cssom": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
-      "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
-      "dev": true
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/rw": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
-      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
-    },
-    "node_modules/safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
-    },
-    "node_modules/sax": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
-      "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
-    },
-    "node_modules/saxes": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
-      "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
-      "dev": true,
-      "dependencies": {
-        "xmlchars": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=v12.22.7"
-      }
-    },
-    "node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/signal-exit": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "dev": true
-    },
-    "node_modules/sisteransi": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
-      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
-      "dev": true
-    },
-    "node_modules/slash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
-      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/source-map": {
-      "version": "0.8.0-beta.0",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
-      "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
-      "dev": true,
-      "dependencies": {
-        "whatwg-url": "^7.0.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/source-map-js": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
-      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-support": {
-      "version": "0.5.13",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
-      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
-      "dev": true,
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/source-map-support/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
-      "dev": true
-    },
-    "node_modules/stack-utils": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
-      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
-      "dev": true,
-      "dependencies": {
-        "escape-string-regexp": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/string-argv": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
-      "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.6.19"
-      }
-    },
-    "node_modules/string-length": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
-      "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
-      "dev": true,
-      "dependencies": {
-        "char-regex": "^1.0.2",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/string-length/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-length/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-width": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
-      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
-      "dev": true,
-      "dependencies": {
-        "eastasianwidth": "^0.2.0",
-        "emoji-regex": "^9.2.2",
-        "strip-ansi": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/string-width-cjs": {
-      "name": "string-width",
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-width-cjs/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-width-cjs/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
-    },
-    "node_modules/string-width-cjs/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
-      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
-      }
-    },
-    "node_modules/strip-ansi-cjs": {
-      "name": "strip-ansi",
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-bom": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
-      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-final-newline": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
-      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/sucrase": {
-      "version": "3.35.0",
-      "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
-      "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "commander": "^4.0.0",
-        "glob": "^10.3.10",
-        "lines-and-columns": "^1.1.6",
-        "mz": "^2.7.0",
-        "pirates": "^4.0.1",
-        "ts-interface-checker": "^0.1.9"
-      },
-      "bin": {
-        "sucrase": "bin/sucrase",
-        "sucrase-node": "bin/sucrase-node"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/symbol-tree": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
-      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
-      "dev": true
-    },
-    "node_modules/terser": {
-      "version": "5.32.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz",
-      "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@jridgewell/source-map": "^0.3.3",
-        "acorn": "^8.8.2",
-        "commander": "^2.20.0",
-        "source-map-support": "~0.5.20"
-      },
-      "bin": {
-        "terser": "bin/terser"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/terser/node_modules/commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
-      "optional": true,
-      "peer": true
-    },
-    "node_modules/terser/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/terser/node_modules/source-map-support": {
-      "version": "0.5.21",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/test-exclude": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
-      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
-      "dev": true,
-      "dependencies": {
-        "@istanbuljs/schema": "^0.1.2",
-        "glob": "^7.1.4",
-        "minimatch": "^3.0.4"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/test-exclude/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/test-exclude/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/test-exclude/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/thenify": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
-      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
-      "dev": true,
-      "dependencies": {
-        "any-promise": "^1.0.0"
-      }
-    },
-    "node_modules/thenify-all": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
-      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
-      "dev": true,
-      "dependencies": {
-        "thenify": ">= 3.1.0 < 4"
-      },
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/tmpl": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
-      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
-      "dev": true
-    },
-    "node_modules/to-fast-properties": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/tough-cookie": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
-      "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
-      "dev": true,
-      "dependencies": {
-        "psl": "^1.1.33",
-        "punycode": "^2.1.1",
-        "universalify": "^0.2.0",
-        "url-parse": "^1.5.3"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/tough-cookie/node_modules/universalify": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
-      "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/tr46": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-      "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
-      "dev": true,
-      "dependencies": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "node_modules/tree-kill": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
-      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
-      "dev": true,
-      "bin": {
-        "tree-kill": "cli.js"
-      }
-    },
-    "node_modules/ts-interface-checker": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
-      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
-      "dev": true
-    },
-    "node_modules/ts-jest": {
-      "version": "29.1.2",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz",
-      "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==",
-      "dev": true,
-      "dependencies": {
-        "bs-logger": "0.x",
-        "fast-json-stable-stringify": "2.x",
-        "jest-util": "^29.0.0",
-        "json5": "^2.2.3",
-        "lodash.memoize": "4.x",
-        "make-error": "1.x",
-        "semver": "^7.5.3",
-        "yargs-parser": "^21.0.1"
-      },
-      "bin": {
-        "ts-jest": "cli.js"
-      },
-      "engines": {
-        "node": "^16.10.0 || ^18.0.0 || >=20.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": ">=7.0.0-beta.0 <8",
-        "@jest/types": "^29.0.0",
-        "babel-jest": "^29.0.0",
-        "jest": "^29.0.0",
-        "typescript": ">=4.3 <6"
-      },
-      "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
-        "@jest/types": {
-          "optional": true
-        },
-        "babel-jest": {
-          "optional": true
-        },
-        "esbuild": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/ts-jest/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/ts-node": {
-      "version": "10.9.2",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
-      "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
-      "dev": true,
-      "dependencies": {
-        "@cspotcode/source-map-support": "^0.8.0",
-        "@tsconfig/node10": "^1.0.7",
-        "@tsconfig/node12": "^1.0.7",
-        "@tsconfig/node14": "^1.0.0",
-        "@tsconfig/node16": "^1.0.2",
-        "acorn": "^8.4.1",
-        "acorn-walk": "^8.1.1",
-        "arg": "^4.1.0",
-        "create-require": "^1.1.0",
-        "diff": "^4.0.1",
-        "make-error": "^1.1.1",
-        "v8-compile-cache-lib": "^3.0.1",
-        "yn": "3.1.1"
-      },
-      "bin": {
-        "ts-node": "dist/bin.js",
-        "ts-node-cwd": "dist/bin-cwd.js",
-        "ts-node-esm": "dist/bin-esm.js",
-        "ts-node-script": "dist/bin-script.js",
-        "ts-node-transpile-only": "dist/bin-transpile.js",
-        "ts-script": "dist/bin-script-deprecated.js"
-      },
-      "peerDependencies": {
-        "@swc/core": ">=1.2.50",
-        "@swc/wasm": ">=1.2.50",
-        "@types/node": "*",
-        "typescript": ">=2.7"
-      },
-      "peerDependenciesMeta": {
-        "@swc/core": {
-          "optional": true
-        },
-        "@swc/wasm": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/tsup": {
-      "version": "8.0.2",
-      "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz",
-      "integrity": "sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==",
-      "dev": true,
-      "dependencies": {
-        "bundle-require": "^4.0.0",
-        "cac": "^6.7.12",
-        "chokidar": "^3.5.1",
-        "debug": "^4.3.1",
-        "esbuild": "^0.19.2",
-        "execa": "^5.0.0",
-        "globby": "^11.0.3",
-        "joycon": "^3.0.1",
-        "postcss-load-config": "^4.0.1",
-        "resolve-from": "^5.0.0",
-        "rollup": "^4.0.2",
-        "source-map": "0.8.0-beta.0",
-        "sucrase": "^3.20.3",
-        "tree-kill": "^1.2.2"
-      },
-      "bin": {
-        "tsup": "dist/cli-default.js",
-        "tsup-node": "dist/cli-node.js"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "@microsoft/api-extractor": "^7.36.0",
-        "@swc/core": "^1",
-        "postcss": "^8.4.12",
-        "typescript": ">=4.5.0"
-      },
-      "peerDependenciesMeta": {
-        "@microsoft/api-extractor": {
-          "optional": true
-        },
-        "@swc/core": {
-          "optional": true
-        },
-        "postcss": {
-          "optional": true
-        },
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/type-detect": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
-      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/type-fest": {
-      "version": "0.21.3",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
-      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/typescript": {
-      "version": "5.4.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
-      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
-      "devOptional": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
-    "node_modules/undici-types": {
-      "version": "5.26.5",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
-      "devOptional": true
-    },
-    "node_modules/upath": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
-      "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">=4",
-        "yarn": "*"
-      }
-    },
-    "node_modules/update-browserslist-db": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
-      "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "dependencies": {
-        "escalade": "^3.1.2",
-        "picocolors": "^1.0.1"
-      },
-      "bin": {
-        "update-browserslist-db": "cli.js"
-      },
-      "peerDependencies": {
-        "browserslist": ">= 4.21.0"
-      }
-    },
-    "node_modules/uri-js": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
-      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "node_modules/url-parse": {
-      "version": "1.5.10",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
-      "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
-      "dev": true,
-      "dependencies": {
-        "querystringify": "^2.1.1",
-        "requires-port": "^1.0.0"
-      }
-    },
-    "node_modules/uuid": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
-      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
-      "funding": [
-        "https://github.com/sponsors/broofa",
-        "https://github.com/sponsors/ctavan"
-      ],
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
-    "node_modules/v8-compile-cache-lib": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
-      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
-      "dev": true
-    },
-    "node_modules/v8-to-istanbul": {
-      "version": "9.2.0",
-      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
-      "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==",
-      "dev": true,
-      "dependencies": {
-        "@jridgewell/trace-mapping": "^0.3.12",
-        "@types/istanbul-lib-coverage": "^2.0.1",
-        "convert-source-map": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10.12.0"
-      }
-    },
-    "node_modules/validator": {
-      "version": "13.12.0",
-      "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
-      "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
-    "node_modules/vite": {
-      "version": "5.4.3",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz",
-      "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "esbuild": "^0.21.3",
-        "postcss": "^8.4.43",
-        "rollup": "^4.20.0"
-      },
-      "bin": {
-        "vite": "bin/vite.js"
-      },
-      "engines": {
-        "node": "^18.0.0 || >=20.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/vitejs/vite?sponsor=1"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.3"
-      },
-      "peerDependencies": {
-        "@types/node": "^18.0.0 || >=20.0.0",
-        "less": "*",
-        "lightningcss": "^1.21.0",
-        "sass": "*",
-        "sass-embedded": "*",
-        "stylus": "*",
-        "sugarss": "*",
-        "terser": "^5.4.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        },
-        "less": {
-          "optional": true
-        },
-        "lightningcss": {
-          "optional": true
-        },
-        "sass": {
-          "optional": true
-        },
-        "sass-embedded": {
-          "optional": true
-        },
-        "stylus": {
-          "optional": true
-        },
-        "sugarss": {
-          "optional": true
-        },
-        "terser": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/vite-plugin-vuetify": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.4.tgz",
-      "integrity": "sha512-A4cliYUoP/u4AWSRVRvAPKgpgR987Pss7LpFa7s1GvOe8WjgDq92Rt3eVXrvgxGCWvZsPKziVqfHHdCMqeDhfw==",
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "@vuetify/loader-shared": "^2.0.3",
-        "debug": "^4.3.3",
-        "upath": "^2.0.1"
-      },
-      "engines": {
-        "node": "^18.0.0 || >=20.0.0"
-      },
-      "peerDependencies": {
-        "vite": ">=5",
-        "vue": "^3.0.0",
-        "vuetify": "^3.0.0"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
-      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
-      "cpu": [
-        "ppc64"
-      ],
-      "optional": true,
-      "os": [
-        "aix"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-arm": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
-      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
-      "cpu": [
-        "arm"
-      ],
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-arm64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
-      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
-      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
-      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
-      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
-      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
-      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
-      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
-      "cpu": [
-        "arm"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
-      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
-      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
-      "cpu": [
-        "ia32"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
-      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
-      "cpu": [
-        "loong64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
-      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
-      "cpu": [
-        "mips64el"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
-      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
-      "cpu": [
-        "ppc64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
-      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
-      "cpu": [
-        "riscv64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
-      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
-      "cpu": [
-        "s390x"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
-      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
-      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "netbsd"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
-      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "openbsd"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
-      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "sunos"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
-      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
-      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
-      "cpu": [
-        "ia32"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-x64": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
-      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "peer": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/esbuild": {
-      "version": "0.21.5",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
-      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
-      "hasInstallScript": true,
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "esbuild": "bin/esbuild"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.21.5",
-        "@esbuild/android-arm": "0.21.5",
-        "@esbuild/android-arm64": "0.21.5",
-        "@esbuild/android-x64": "0.21.5",
-        "@esbuild/darwin-arm64": "0.21.5",
-        "@esbuild/darwin-x64": "0.21.5",
-        "@esbuild/freebsd-arm64": "0.21.5",
-        "@esbuild/freebsd-x64": "0.21.5",
-        "@esbuild/linux-arm": "0.21.5",
-        "@esbuild/linux-arm64": "0.21.5",
-        "@esbuild/linux-ia32": "0.21.5",
-        "@esbuild/linux-loong64": "0.21.5",
-        "@esbuild/linux-mips64el": "0.21.5",
-        "@esbuild/linux-ppc64": "0.21.5",
-        "@esbuild/linux-riscv64": "0.21.5",
-        "@esbuild/linux-s390x": "0.21.5",
-        "@esbuild/linux-x64": "0.21.5",
-        "@esbuild/netbsd-x64": "0.21.5",
-        "@esbuild/openbsd-x64": "0.21.5",
-        "@esbuild/sunos-x64": "0.21.5",
-        "@esbuild/win32-arm64": "0.21.5",
-        "@esbuild/win32-ia32": "0.21.5",
-        "@esbuild/win32-x64": "0.21.5"
-      }
-    },
-    "node_modules/vue": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.4.tgz",
-      "integrity": "sha512-3yAj2gkmiY+i7+22A1PWM+kjOVXjU74UPINcTiN7grIVPyFFI0lpGwHlV/4xydDmobaBn7/xmi+YG8HeSlCTcg==",
-      "dependencies": {
-        "@vue/compiler-dom": "3.5.4",
-        "@vue/compiler-sfc": "3.5.4",
-        "@vue/runtime-dom": "3.5.4",
-        "@vue/server-renderer": "3.5.4",
-        "@vue/shared": "3.5.4"
-      },
-      "peerDependencies": {
-        "typescript": "*"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/vuetify": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.1.tgz",
-      "integrity": "sha512-N1XlczbgeGt/O+JUk72QPrqcDaRIXUdptUciJqGyTvZ9cfMoSlEWs6TZO+dOOfXbKvmIMFMycYg4dgSHDpCPhg==",
-      "engines": {
-        "node": "^12.20 || >=14.13"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/johnleider"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.7",
-        "vite-plugin-vuetify": ">=1.0.0",
-        "vue": "^3.3.0",
-        "webpack-plugin-vuetify": ">=2.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        },
-        "vite-plugin-vuetify": {
-          "optional": true
-        },
-        "webpack-plugin-vuetify": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/w3c-xmlserializer": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
-      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
-      "dev": true,
-      "dependencies": {
-        "xml-name-validator": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
-      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
-      "dev": true,
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/walker": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
-      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
-      "dev": true,
-      "dependencies": {
-        "makeerror": "1.0.12"
-      }
-    },
-    "node_modules/webfontloader": {
-      "version": "1.6.28",
-      "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz",
-      "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ=="
-    },
-    "node_modules/webidl-conversions": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-      "dev": true
-    },
-    "node_modules/whatwg-encoding": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
-      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
-      "dev": true,
-      "dependencies": {
-        "iconv-lite": "0.6.3"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/whatwg-encoding/node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "dev": true,
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/whatwg-mimetype": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
-      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
-      "dev": true,
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/whatwg-url": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
-      "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
-      "dev": true,
-      "dependencies": {
-        "lodash.sortby": "^4.7.0",
-        "tr46": "^1.0.1",
-        "webidl-conversions": "^4.0.2"
-      }
-    },
-    "node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/wrap-ansi": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
-      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^6.1.0",
-        "string-width": "^5.0.1",
-        "strip-ansi": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi-cjs": {
-      "name": "wrap-ansi",
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
-    },
-    "node_modules/wrap-ansi-cjs/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true
-    },
-    "node_modules/write-file-atomic": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
-      "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
-      "dev": true,
-      "dependencies": {
-        "imurmurhash": "^0.1.4",
-        "signal-exit": "^3.0.7"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/ws": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
-      "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": ">=5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/xml-js": {
-      "version": "1.6.11",
-      "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
-      "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
-      "dev": true,
-      "dependencies": {
-        "sax": "^1.2.4"
-      },
-      "bin": {
-        "xml-js": "bin/cli.js"
-      }
-    },
-    "node_modules/xml2js": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
-      "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
-      "dependencies": {
-        "sax": ">=0.6.0",
-        "xmlbuilder": "~11.0.0"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/xmlbuilder": {
-      "version": "11.0.1",
-      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
-      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/xmlchars": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
-      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
-      "dev": true
-    },
-    "node_modules/y18n": {
-      "version": "5.0.8",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
-      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yallist": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-      "dev": true
-    },
-    "node_modules/yaml": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz",
-      "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==",
-      "dev": true,
-      "bin": {
-        "yaml": "bin.mjs"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/yargs": {
-      "version": "17.7.2",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
-      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
-      "dev": true,
-      "dependencies": {
-        "cliui": "^8.0.1",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.3",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^21.1.1"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/yargs-parser": {
-      "version": "21.1.1",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
-      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/yargs/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yargs/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
-    },
-    "node_modules/yargs/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yargs/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yn": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
-      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/yocto-queue": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
-      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/z-schema": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
-      "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "dependencies": {
-        "lodash.get": "^4.4.2",
-        "lodash.isequal": "^4.5.0",
-        "validator": "^13.7.0"
-      },
-      "bin": {
-        "z-schema": "bin/z-schema"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      },
-      "optionalDependencies": {
-        "commander": "^9.4.1"
-      }
-    },
-    "node_modules/z-schema/node_modules/commander": {
-      "version": "9.5.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
-      "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
-      "dev": true,
-      "optional": true,
-      "peer": true,
-      "engines": {
-        "node": "^12.20.0 || >=14"
-      }
-    }
-  }
-}
diff --git a/package.json b/package.json
index 949cd365bf059eaaccfa7385a4a46c54a9008d3d..0b5b2238d5c22bcad80936bdeee620a0c6d7bcdf 100644
--- a/package.json
+++ b/package.json
@@ -1,31 +1,25 @@
 {
-  "name": "@metabohub/metabolic-layout",
-  "version": "0.0.0",
+  "name": "@metabohub/viz-layout",
+  "version": "0.2.1",
   "private": false,
   "scripts": {
     "build": "tsup",
-    "test": "jest --coverage"
+    "test": "jest"
   },
   "dependencies": {
     "@mdi/font": "^7.4.47",
-    "@metabohub/viz-context-menu": "^0.0.2",
-    "@metabohub/viz-core": "^0.6.1",
-    "cytoscape": "^3.30.2",
-    "line-intersect": "^3.0.0",
-    "xml2js": "^0.6.2"
+    "@types/d3": "^7.4.3",
+    "d3": "^7.9.0",
+    "line-intersect": "^3.0.0"
   },
   "devDependencies": {
     "@eslint/js": "^9.10.0",
+    "@swc/core": "^1.7.26",
     "@types/eslint__js": "^8.42.3",
     "@types/jest": "^29.5.12",
     "@types/jsdom": "^21.1.6",
     "@types/node": "^20.11.14",
-    "@types/xml2js": "^0.4.14",
     "@viz-js/viz": "^3.4.0",
-    "cytoscape": "^3.30.2",
-    "cytoscape-cose-bilkent": "^3.0.0",
-    "cytoscape-fcose": "^2.2.0",
-    "dagrejs": "^0.2.1",
     "eslint": "^9.10.0",
     "graph-data-structure": "^3.5.0",
     "jest": "^29.7.0",
diff --git a/src/composables/AlgorithmBFS.ts b/src/composables/AlgorithmBFS.ts
index fad45dc605ec60514970fc3f525e7529bc6dd1e9..dfdd1a53bca9398359aa8689d4e16ddb6d88c094 100644
--- a/src/composables/AlgorithmBFS.ts
+++ b/src/composables/AlgorithmBFS.ts
@@ -1,6 +1,6 @@
 // Types imports
 import { StartNodesType } from "../types/EnumArgs";
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Network } from "../types/TypeVizCore";
 
 // Composable imports
 import { getStartNodes } from "./CalculateStartNodes";
diff --git a/src/composables/AlgorithmDFS.ts b/src/composables/AlgorithmDFS.ts
index 9534b34c17b9addc70aa83ba23403fb9dbde9210..1748b83d6ac29af7db9c01cf437a2ac3e511e31b 100644
--- a/src/composables/AlgorithmDFS.ts
+++ b/src/composables/AlgorithmDFS.ts
@@ -1,5 +1,5 @@
 // Type imports
-import { Network } from '@metabohub/viz-core/src/types/Network';
+import { Network } from "../types/TypeVizCore";
 import { StartNodesType } from '../types/EnumArgs';
 
 // Composable imports
@@ -66,18 +66,19 @@ export async function DFSWithSources(network:Network, sources:Array<string>|Star
  * @param sources to use as staring node for DFS
  * @returns the reverse dfs order (topological sort) and a graph object without the cycles accessible from the sources 
  */
-export async function DFSsourceDAG(network:Network, sources:Array<string>):Promise<{dfs:Array<string>, graph:{[key:string]:Function} }> {
+export async function DFSsourceDAG(network:Network, sources:Array<string>):Promise<{dfs:Array<string>, graph:{[key:string]:Function}}> {
     let DFS:DFS=await createGraphForDFS(network);
 
-    sources.forEach(async sourceID =>{
-        const nodesID:string[]=DFS.nodesID;
-        if (nodesID.length===0) return; // no nodes 
+    const nodesID:string[]=DFS.nodesID;
+    if (nodesID.length===0) return { dfs:[],graph:DFS.GDSgraph }; // no nodes 
+
+    for (const sourceID of sources){
         const sourceIndex=nodesID.indexOf(sourceID);
         // if the source exist in the network and it's not already visited : dfs from this source
         if (sourceIndex!==-1 && !DFS.visited[sourceIndex]){
             DFS= await nodeDagDFS(DFS,sourceIndex,[]);           
         }
-    });
+    };
 
     return { dfs:DFS.dfsOrder.reverse(),graph:DFS.GDSgraph };
 }
@@ -115,8 +116,8 @@ async function nodeDagDFS(DFS:DFS,nodeIndex:number,currentPath:number[]):Promise
     path.push(nodeIndex)
 
     // loop through the children of the node
-    DFS.GDSgraph.adjacent(DFS.nodesID[nodeIndex]).sort().forEach(async (childID:string) => {
-
+    const childrenOfNode=DFS.GDSgraph.adjacent(DFS.nodesID[nodeIndex]).sort();
+    for (const childID of childrenOfNode){
         // get the index of the child
         const childIndex = DFS.nodesID.indexOf(childID);
         if(childIndex!==-1){
@@ -136,7 +137,7 @@ async function nodeDagDFS(DFS:DFS,nodeIndex:number,currentPath:number[]):Promise
                 
             }
         }
-    });
+    };
     
     // add the node to the dfs order
     DFS.dfsOrder.push(DFS.nodesID[nodeIndex]); 
diff --git a/src/composables/CalculateOverlaps.ts b/src/composables/CalculateOverlaps.ts
new file mode 100644
index 0000000000000000000000000000000000000000..973b7348b835c37a3790390b0a901fc7f564a9bd
--- /dev/null
+++ b/src/composables/CalculateOverlaps.ts
@@ -0,0 +1,320 @@
+// Type import
+import { Network,GraphStyleProperties } from "../types/TypeVizCore";
+import { Coordinate, Size } from '../types/CoordinatesSize';
+
+
+// Composable imports
+import { getSizeNodePixel } from './CalculateSize';
+
+// General imports
+import { checkIntersection } from 'line-intersect';
+
+/**
+ * This file contains the functions to count the number of intersections in a network and the number of overlapping nodes and edges.
+ * 
+ * *********************************
+ * 
+ * 0. Edge intersection
+ * 
+ * -> isIntersectionGraph : 
+ *      check if there is any intersection between edges in the network
+ * 
+ * -> edgesIntersection :
+ *     check if there is any intersection between edges
+ * 
+ * -> commonEndBetween2Edges :
+ *      check if there is a common end between two edges
+ * 
+ * -> sameCoordinates :
+ *      check if two nodes have the same coordinates
+ * 
+ * *********************************
+ * 
+ * 1. Nodes overlap
+ * 
+ * -> isOverlapNodes :
+ *      check if there is any overlap between nodes in the network
+ * 
+ * -> nodeOverlap :
+ *      check if two nodes overlap
+ * 
+ * *********************************
+ * 
+ * 2. Node Edge overlap
+ * 
+ * -> isOverlapNodesEdges :
+ *      check if there is any overlap between nodes and edges in the network
+ * 
+ * -> nodeEdgeOverlap :
+ *      check if a node overlaps with an edge
+ * 
+ * -> isPointInsideRect :
+ *      check if a point is inside a rectangle
+ * 
+ */
+
+
+
+
+
+/*******************************************************************************************************************************************************/
+//_________________________________________________________0.  Edge intersection ________________________________________________________________________
+
+
+
+/**
+ * Determines if a given graph has any intersecting edges.
+ *
+ * @param nodes - An object where keys are node identifiers and values are coordinates of the center of the nodes.
+ * @param links - An array of link objects, each containing a source and target node identifier.
+ * @returns A boolean indicating whether any edges in the graph intersect.
+ */
+export function isIntersectionGraph(nodes: {[key:string]:Coordinate},links:{source:string,target:string}[]): boolean {
+    for (let i=0 ; i<links.length ; i++) {
+        for (let j=i+1 ; j<links.length ; j++) {
+            const link1=links[i];
+            const link2=links[j];
+             // check if intersection
+             let node1Link1=nodes[link1.source];
+             let node2Link1=nodes[link1.target];
+             let node1Link2=nodes[link2.source];
+             let node2Link2=nodes[link2.target];
+            if (edgesIntersection(node1Link1,node2Link1,node1Link2,node2Link2)){
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+
+/**
+ * Determines if two edges intersect. If the edges share a common end, they are not considered to intersect.
+ *
+ * @param node1Link1 - The coordinates of the first point of the first edge.
+ * @param node2Link1 - The coordinates of the second point of the first edge.
+ * @param node1Link2 - The coordinates of the first point of the second edge.
+ * @param node2Link2 - The coordinates of the second point of the second edge.
+ * @returns `true` if the edges intersect, otherwise `false`.
+ */
+function edgesIntersection(node1Link1:{x:number,y:number},node2Link1:{x:number,y:number},node1Link2:{x:number,y:number},node2Link2:{x:number,y:number}): boolean {
+    // case node in common
+    if (commonEndBetween2Edges(node1Link1,node2Link1,node1Link2,node2Link2)) {
+        return false;
+    }
+    const result = checkIntersection(node1Link1.x, node1Link1.y, node2Link1.x, node2Link1.y, node1Link2.x, node1Link2.y, node2Link2.x, node2Link2.y);
+    if (result.type == "intersecting") {
+        return true;
+    }else{
+        return false;
+    }
+}
+
+/**
+ * Determines if there is a common end (same end coordinates) between two edges.
+ *
+ * @param node1Link1 - The coordinates of the first node of the first edge.
+ * @param node2Link1 - The coordinates of the second node of the first edge.
+ * @param node1Link2 - The coordinates of the first node of the second edge.
+ * @param node2Link2 - The coordinates of the second node of the second edge.
+ * @returns `true` if there is a common node between the two edges, otherwise `false`.
+ */
+function commonEndBetween2Edges(node1Link1:{x:number,y:number},node2Link1:{x:number,y:number},node1Link2:{x:number,y:number},node2Link2:{x:number,y:number}): boolean {
+    if (sameCoordinates(node1Link1,node1Link2) || sameCoordinates(node1Link1,node2Link2) || sameCoordinates(node2Link1,node1Link2) || sameCoordinates(node2Link1,node2Link2)) {
+        return true;
+    }else {
+        return false;
+    }
+}
+
+/**
+ * Determines if two nodes have the same coordinates.
+ *
+ * @param node1 - The first node with x and y coordinates.
+ * @param node2 - The second node with x and y coordinates.
+ * @returns `true` if both nodes have the same x and y coordinates, otherwise `false`.
+ */
+function sameCoordinates(node1: {x:number,y:number},node2: {x:number,y:number}): boolean {
+    if (!node1 || !node2 ||  node1.x==null ||  node1.y==null  || node2.x==null || node2.y==null) {
+        return false;
+    }
+    return node1.x===node2.x && node1.y===node2.y;
+}
+
+
+/*******************************************************************************************************************************************************/
+//____________________________________________________________1. Nodes overlap __________________________________________________________________________
+
+
+
+/**
+ * Checks if any nodes in the given network overlap based on their positions and sizes.
+ *
+ * @param nodesPosition - An object where keys are node IDs and values are their coordinates.
+ * @param network - The network containing nodes and their properties.
+ * @param networkStyle - The style properties of the graph, used to determine node sizes.
+ * @returns `true` if any nodes overlap, otherwise `false`.
+ */
+export function isOverlapNodes(nodesPosition: {[key:string]:Coordinate},network:Network,networkStyle:GraphStyleProperties):boolean{
+    const nodesID=Object.keys(nodesPosition);
+    for (let i=0 ; i<nodesID.length ; i++) {
+        for (let j=i+1 ; j<nodesID.length ; j++) {
+            // info about node1
+            const node1=network.nodes[nodesID[i]];
+            const posNode1=nodesPosition[nodesID[i]];
+            const sizeNode1=getSizeNodePixel(node1,networkStyle);
+            // info about node2
+            const node2=network.nodes[nodesID[j]];
+            const posNode2=nodesPosition[nodesID[j]];
+            const sizeNode2=getSizeNodePixel(node2,networkStyle);
+
+            if (nodeOverlap(posNode1,sizeNode1,posNode2,sizeNode2)){
+                return true;
+            }
+
+        }
+    }
+    return false;
+}
+
+/**
+ * Determines if two nodes overlap based on their coordinates and sizes.
+ *
+ * @param coord1 - The coordinates of the first node.
+ * @param size1 - The size (width and height) of the first node.
+ * @param coord2 - The coordinates of the second node.
+ * @param size2 - The size (width and height) of the second node.
+ * @returns `true` if the nodes overlap, `false` otherwise.
+ *
+ * @remarks
+ * This function checks if the bounding rectangles of two nodes overlap.
+ * It handles cases where any of the input parameters are null or undefined by returning `false`.
+ *
+ */
+function nodeOverlap(coord1: Coordinate, size1: Size, coord2: Coordinate, size2: Size): boolean {
+    if ( !coord1 || !size1 || !coord2 || !size2 || size1.width == null || size1.height == null || size2.width == null
+        || size2.height == null || coord1.x == null || coord1.y == null || coord2.x == null || coord2.y == null ||
+        size1.width == undefined || size1.height == undefined || size2.width == undefined || size2.height == undefined ||
+        coord1.x == undefined || coord1.y == undefined || coord2.x == undefined || coord2.y == undefined) {
+        // Handle null or undefined inputs appropriately
+        return false;
+    }
+
+    // rectangle 1
+    const left1 = coord1.x - size1.width / 2;
+    const right1 = coord1.x + size1.width / 2;
+    const top1 = coord1.y - size1.height / 2;
+    const bottom1 = coord1.y + size1.height / 2;
+
+    // rectangle 2
+    const left2 = coord2.x - size2.width / 2;
+    const right2 = coord2.x + size2.width / 2;
+    const top2 = coord2.y - size2.height / 2;
+    const bottom2 = coord2.y + size2.height / 2;
+
+    // overlap?
+    const overlapX = left1 < right2 && right1 > left2;
+    const overlapY = top1 < bottom2 && bottom1 > top2;
+
+    return overlapX && overlapY;
+}
+
+
+/*******************************************************************************************************************************************************/
+//________________________________________________________2.  Node Edge overlap ________________________________________________________________________
+
+
+
+/**
+ * Checks if any node overlaps with any edge in the network.
+ *
+ * @param nodesPosition - An object where keys are node IDs and values are objects containing x and y coordinates of the nodes.
+ * @param links - An array of link objects, each containing a source and target node ID.
+ * @param network - The network object containing nodes and their properties.
+ * @param networkStyle - The style properties of the graph.
+ * @returns A boolean indicating whether any node overlaps with any edge.
+ */
+export function isOverlapNodesEdges(nodesPosition: {[key:string]:{x:number,y:number}},links:{source:string,target:string}[],network:Network,networkStyle:GraphStyleProperties):boolean{
+    const nodesID=Object.keys(nodesPosition);
+    for (let i=0 ; i<nodesID.length ; i++) {
+        // info about node
+        const node=network.nodes[nodesID[i]];
+        const posNode=nodesPosition[nodesID[i]];
+        const sizeNode=getSizeNodePixel(node,networkStyle);
+
+        for (let j=0 ; j<links.length ; j++) {        
+            // info about link
+            const link=links[j];
+            // if node is linked to the edge : continue
+            if(link.source==nodesID[i] || link.target==nodesID[i]){
+                continue;
+            }else{
+                let posLink1=nodesPosition[link.source];
+                let posLink2=nodesPosition[link.target];
+                if (nodeEdgeOverlap(posNode,sizeNode,posLink1,posLink2)){
+                    return true;
+                }
+            }
+
+        }
+    }
+    return false;
+}
+
+
+
+/**
+ * Determines if a node, represented as a rectangle, overlaps with an edge defined by two points.
+ *
+ * @param centerCoordNode - The center coordinates of the node.
+ * @param sizeNode - The size of the node, including width and height.
+ * @param posLink1 - The first endpoint of the edge.
+ * @param posLink2 - The second endpoint of the edge.
+ * @returns `true` if the node overlaps with the edge, `false` otherwise.
+ */
+function nodeEdgeOverlap(centerCoordNode: Coordinate, sizeNode:Size, posLink1: Coordinate, posLink2: Coordinate): boolean {
+    
+    // Treat the node as a rectangle 
+    const rect = {
+        left: centerCoordNode.x - sizeNode.width / 2,
+        right: centerCoordNode.x + sizeNode.width / 2,
+        top: centerCoordNode.y - sizeNode.height / 2,
+        bottom: centerCoordNode.y + sizeNode.height / 2
+    };
+
+    // Check if any of the edge's endpoints is inside the rectangle
+    if (isPointInsideRect(posLink1,rect) || isPointInsideRect(posLink2,rect)) {
+        return true; // One of the endpoints is inside the rectangle
+    }
+
+   // Check for overlap between the edge and the sides of the rectangle
+    // Convert the sides of the rectangle into line segments
+    const edges = [
+        { start: { x: rect.left, y: rect.top }, end: { x: rect.right, y: rect.top } }, // Top
+        { start: { x: rect.right, y: rect.top }, end: { x: rect.right, y: rect.bottom } }, // Right
+        { start: { x: rect.left, y: rect.bottom }, end: { x: rect.right, y: rect.bottom } }, // Bottom
+        { start: { x: rect.left, y: rect.top }, end: { x: rect.left, y: rect.bottom } } // Left
+    ];
+
+    // Use checkIntersection function to check if two line segments intersect
+    for (const edge of edges) {
+        const result = checkIntersection(edge.start.x,edge.start.y, edge.end.x,edge.end.y, posLink1.x, posLink1.y,posLink2.x,posLink2.y);
+        if (result.type == "intersecting") {
+            return true; // There is an overlap
+        }
+    }
+
+    return false; // No overlap detected
+}
+
+
+/**
+ * Determines if a given point is inside a specified rectangle.
+ *
+ * @param point - The coordinate of the point to check.
+ * @param rectangle - The boundaries of the rectangle, defined by its left, right, top, and bottom edges.
+ * @returns `true` if the point is inside the rectangle, `false` otherwise.
+ */
+function isPointInsideRect(point: Coordinate, rectangle:{left:number,right:number,top:number,bottom:number}):boolean{ 
+    return point.x >= rectangle.left && point.x <= rectangle.right && point.y >= rectangle.top && point.y <= rectangle.bottom;
+}
diff --git a/src/composables/CalculateRelationCycle.ts b/src/composables/CalculateRelationCycle.ts
index 9be5928a4c24a9af4bcba55dd9b6b96938e90f5a..805295cfd63bd5a7404986d075172e8e60322912 100644
--- a/src/composables/CalculateRelationCycle.ts
+++ b/src/composables/CalculateRelationCycle.ts
@@ -1,6 +1,6 @@
 // Type imports
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
-import { Link } from "@metabohub/viz-core/src/types/Link";
+import { Link } from "../types/TypeVizCore";
 import { Ordering } from "../types/EnumArgs";
 import { TypeSubgraph } from "../types/Subgraph";
 import { LinkLayout, NetworkLayout } from "../types/NetworkLayout";
@@ -29,7 +29,7 @@ import { inCycle } from "./GetSetAttributsNodes";
  * -> getNodesIDPlacedInGroupCycle
  *     retrieves the IDs of nodes placed (with x and y define) in a specific group cycle
  * 
- * -> getNodesPlacedInGroupCycle
+ * -> getNodesPlacedInGroupCycleAsArray
  *    retrieves the nodes (id, x, y) placed in a specific group cycle. If position is fixed it's (id, fx, fy). If there is no posisiton, it's only the id
  * 
  * -> getNodesPlacedInGroupCycleAsObject
@@ -59,7 +59,7 @@ import { inCycle } from "./GetSetAttributsNodes";
  * *********************************
  * 2. Get graph
  * 
- * -> getListNodeLinksForCycleGroup
+ * -> getListNodeLinksForCycleGroupAsArray
  *       retrieves the list of node and links for a specific cycle group. Position of nodes can be fixed
  * 
  * -> getListNodeLinksForCycleGroupAsObject
@@ -89,31 +89,33 @@ import { inCycle } from "./GetSetAttributsNodes";
  * @param parentOrChild - Specifies whether to retrieve the parent cycles or the child of the cycle group. Can be either "parent" or "child".
  * @param xSort - Optional. Specifies whether to sort the nodes of the group cycle and their parent/child by their x position. Defaults to true. 
  * The neighboring nodes are added in the order of the x position of the nodes inside the group cycle.
- * @returns An array of cycle IDs representing the neighboring cycles.
+ * @returns Promise of an array of cycle IDs representing the neighboring cycles.
  */
-export function neighborsGroupCycle(subgraphNetwork:SubgraphNetwork,cycleGroupId:string, parentOrChild:"parent"|"child",xSort:boolean=true):string[]{
+export async function neighborsGroupCycle(subgraphNetwork:SubgraphNetwork,cycleGroupId:string, parentOrChild:"parent"|"child",xSort:boolean=true):Promise<string[]>{
     if (subgraphNetwork[TypeSubgraph.CYCLEGROUP] && cycleGroupId in subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
         const cycleGroup=subgraphNetwork[TypeSubgraph.CYCLEGROUP][cycleGroupId];   
         if (cycleGroup.precalculatedNodesPosition){
             const positionNodesCycleGroup=cycleGroup.precalculatedNodesPosition;
             // get the id of nodes in group cycle
-            const nodes=getNodesIDPlacedInGroupCycle(subgraphNetwork,cycleGroupId);
+            const nodes=await getNodesIDPlacedInGroupCycle(subgraphNetwork,cycleGroupId);
             // sort nodes of the group cycle by x
             if (xSort){
                 nodes.sort((nodeIdA, nodeIdB) => {
-                    if(!(nodeIdA in positionNodesCycleGroup) || !(nodeIdB in positionNodesCycleGroup)) throw new Error("Node not plaed inside groupe cycle");
-                    const nodeA = positionNodesCycleGroup[nodeIdA];
-                    const nodeB = positionNodesCycleGroup[nodeIdB];
+                    if(!(nodeIdA in positionNodesCycleGroup) || !(nodeIdB in positionNodesCycleGroup)) throw new Error("Node not placed inside groupe cycle");
+                    const nodeA = positionNodesCycleGroup[nodeIdA] as Coordinate;
+                    const nodeB = positionNodesCycleGroup[nodeIdB] as Coordinate;
                     return nodeA.x - nodeB.x;
                 });
             }
             if (parentOrChild==="parent"){
                 // get parent nodes
-                const parentCycles = Array.from(new Set(parentNodeNotInCycle(subgraphNetwork, nodes,xSort).flat()));
+                const parentsDoubleArray = await parentNodeNotInCycle(subgraphNetwork, nodes,xSort);
+                const parentCycles = Array.from(new Set(parentsDoubleArray.flat()));
                 return parentCycles;
             } else {
                 // get child nodes
-                const childCycles = Array.from(new Set(childNodeNotInCycle(subgraphNetwork, nodes,xSort).flat()));
+                const childrenDoubleArray = await childNodeNotInCycle(subgraphNetwork, nodes,xSort);
+                const childCycles = Array.from(new Set(childrenDoubleArray.flat()));
                 return childCycles;
             }
         }else{
@@ -131,9 +133,9 @@ export function neighborsGroupCycle(subgraphNetwork:SubgraphNetwork,cycleGroupId
  * @param subgraphNetwork - The subgraph network object.
  * @param listNodes - The list of nodes to check for parent nodes.
  * @param sort - Optional. Specifies whether to sort the parent nodes (for each node) by their x position. Defaults to false.
- * @returns An array of arrays : each array are the parents of a node in the lists
+ * @returns Promise of an array of arrays : each array are the parents of a node in the lists
  */
-export function parentNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes: string[],sort:boolean=false): string[][] {
+export async function parentNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes: string[],sort:boolean=false): Promise<string[][]> {
     const parentNodes = listNodes.map((node: string) => {
         let parentNodesI = subgraphNetwork.network.links
             .filter(link => link.target.id === node) // get link with those node as child
@@ -158,9 +160,9 @@ export function parentNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes
  * @param subgraphNetwork - The subgraph network object.
  * @param listNodes - The list of nodes to check for child nodes.
  * @param sort - Optional. Specifies whether to sort the child nodes (for each node) by their x position. Defaults to false.
- * @returns An array of arrays containing the child nodes that are not part of any cycle.
+ * @returns Promise of an array of arrays containing the child nodes that are not part of any cycle.
  */
-export function childNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes: string[],sort:boolean=false): string[][] {
+export async function childNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes: string[],sort:boolean=false): Promise<string[][]> {
     const childNodes = listNodes.map((node: string) => {
         let childNodesI = subgraphNetwork.network.links
             .filter(link => link.source.id === node) // get link with those node as parent
@@ -187,9 +189,9 @@ export function childNodeNotInCycle(subgraphNetwork: SubgraphNetwork, listNodes:
  * 
  * @param subgraphNetwork - The subgraph network containing the group cycles.
  * @param groupCycleID - The ID of the group cycle to retrieve the nodes from.
- * @returns An array of strings representing the IDs of the nodes placed in the group cycle.
+ * @returns Promise of an array of strings representing the IDs of the nodes placed in the group cycle.
  */
-export function getNodesIDPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycleID:string):string[]{
+export async function getNodesIDPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycleID:string):Promise<string[]>{
     if (subgraphNetwork[TypeSubgraph.CYCLEGROUP] && groupCycleID in subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
         const groupCycle =subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleID];
         if (groupCycle.precalculatedNodesPosition){
@@ -215,7 +217,7 @@ export function getNodesIDPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,gro
  * @returns An array of objects representing the nodes placed in the group cycle. Each object contains the node ID and optionally the x and y coordinates if positionAsFixed is true.
  *          If the group cycle ID is not found or the precalculated node positions are not available, null is returned.
  */
-export function getNodesPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycleID:string,positionAsFixed:boolean=false):{ id: string,x?:number, y?:number, fx?:number, fy?:number }[]{
+export function getNodesPlacedInGroupCycleAsArray(subgraphNetwork:SubgraphNetwork,groupCycleID:string,positionAsFixed:boolean=false):{ id: string,x?:number, y?:number, fx?:number, fy?:number }[]{
     if (subgraphNetwork[TypeSubgraph.CYCLEGROUP] && groupCycleID in subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
         const groupCycle =subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleID];
         if (groupCycle.precalculatedNodesPosition){
@@ -224,8 +226,8 @@ export function getNodesPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,group
                     .filter(([_, item]) => { return item.x !== undefined && item.y !== undefined })
                     .map(([key, item]) => { 
                         if (item.x!==null || item.y!==null){
-                            if (positionAsFixed) return { id: key,fx:item.x, fy:item.y } 
-                            else return { id: key,x:item.x, y:item.y } 
+                            if (positionAsFixed) return { id: key,fx:item.x as number, fy:item.y as number} 
+                            else return { id: key,x:item.x as number, y:item.y as number } 
                             
                         }else{
                             return { id: key }
@@ -240,7 +242,7 @@ export function getNodesPlacedInGroupCycle(subgraphNetwork:SubgraphNetwork,group
 }
 
 /**
- * Retrieves the nodes (id, x, y) placed in a specific group cycle. If position is fixed it's (id, fx, fy). 
+ * Retrieves the nodes (id:{x, y}) placed in a specific group cycle. 
  * If there is no posisiton, it's only the id
  * 
  * @param subgraphNetwork - The subgraph network object.
@@ -258,7 +260,7 @@ export function getNodesPlacedInGroupCycleAsObject(subgraphNetwork:SubgraphNetwo
                         .filter(([_, item]) => { return item.x !== undefined && item.y !== undefined })
                         .reduce<{ [key:string]:Coordinate}>((acc, node) => { 
                             if (node[1].x!==null || node[1].y!==null){
-                                 acc[node[0]]={ x:node[1].x, y:node[1].y } 
+                                 acc[node[0]]={ x:node[1].x as number, y:node[1].y as number } 
                             }
                             return acc;
                         },{});
@@ -287,7 +289,6 @@ export function getNodesPlacedInGroupCycleAsObject(subgraphNetwork:SubgraphNetwo
  * @throws {Error} If tail or head is undefined.
  */
 export function cycleMetanodeLink(link:LinkLayout, cycle:boolean=true):{inCycle:string[],tail:string,head:string}{
-    
     let inCycle:string[]=[];
     let tail:string;
     let head:string;
@@ -323,19 +324,21 @@ export function cycleMetanodeLink(link:LinkLayout, cycle:boolean=true):{inCycle:
  * 
  * @param subgraphNetwork - The subgraph network.
  * @param orderChange - A boolean indicating whether to change the order with group cycle. Default is false.
- * @returns The subgraphNetwork and an array of sorted links.
+ * @returns Promise of the subgraphNetwork and an array of sorted links.
  */
-export function sortLinksWithAllGroupCycle(subgraphNetwork:SubgraphNetwork,orderChange:boolean=false):{subgraphNetwork:SubgraphNetwork,linksOrdered:LinkLayout[]}{
+export async function sortLinksWithAllGroupCycle(subgraphNetwork:SubgraphNetwork,orderChange:boolean=false):Promise<{subgraphNetwork:SubgraphNetwork,linksOrdered:LinkLayout[]}>{
     let links:Link[]=[];
 
     // change ordre with group cycle
     if (orderChange  && subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
         // adding edge in right order for each group cycle
-        Object.keys(subgraphNetwork[TypeSubgraph.CYCLEGROUP]).forEach((groupCycle) => {
-            const resultSorting=sortLinksWithGroupCycle(subgraphNetwork,groupCycle);
-            subgraphNetwork=resultSorting.subgraphNetwork;
-            links=links.concat(resultSorting.linksOrdered);
-        });
+        const allGroupCycle=Object.keys(subgraphNetwork[TypeSubgraph.CYCLEGROUP]);
+        for (const groupCycle of allGroupCycle) {
+            const resultSorting = await sortLinksWithGroupCycle(subgraphNetwork, groupCycle);
+            subgraphNetwork = resultSorting.subgraphNetwork;
+            links = links.concat(resultSorting.linksOrdered);
+        }
+
         // add other links
         Object.values(subgraphNetwork.network.links).forEach((link) => {
             if (!links.includes(link)){
@@ -356,15 +359,15 @@ export function sortLinksWithAllGroupCycle(subgraphNetwork:SubgraphNetwork,order
  * 
  * @param subgraphNetwork - The subgraph network containing the cycles group.
  * @param groupCycle - The group cycle id to sort the links.
- * @returns The subgraphNetwork and an array of sorted links.
+ * @returns Promise of the subgraphNetwork and an array of sorted links.
  */
-function sortLinksWithGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycle:string):{subgraphNetwork:SubgraphNetwork,linksOrdered:LinkLayout[]}{
+async function sortLinksWithGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycle:string):Promise<{subgraphNetwork:SubgraphNetwork,linksOrdered:LinkLayout[]}>{
     let links:LinkLayout[]=[];
     if( subgraphNetwork[TypeSubgraph.CYCLEGROUP] && groupCycle in subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
         // sort parent of cycle by x of the child in the cycle
         // (first : parent of the left node of group cycle)
-        const parents=neighborsGroupCycle(subgraphNetwork,groupCycle,"parent",true);
-        const children=neighborsGroupCycle(subgraphNetwork,groupCycle,"child",true);
+        const parents= await neighborsGroupCycle(subgraphNetwork,groupCycle,"parent",true);
+        const children= await neighborsGroupCycle(subgraphNetwork,groupCycle,"child",true);
 
         let nodeOrder:string[]=[];
         let source:"node"|"groupCycle";
@@ -382,15 +385,15 @@ function sortLinksWithGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycle:stri
         }
 
         // get links between the parent (or children) and the group cycle in the right order
-        nodeOrder.forEach((nodeId) => {
+        for (const nodeId of nodeOrder){
             // get links for each node
-            const newLinksOrder = getLinksNodeGroupCycle(subgraphNetwork,nodeId,groupCycle,source);
+            const newLinksOrder = await getLinksNodeGroupCycle(subgraphNetwork,nodeId,groupCycle,source);
             // add links
             newLinksOrder.forEach((newLink) => {
                 links.push(newLink);
             });
 
-        });
+        };
         return { subgraphNetwork:subgraphNetwork,linksOrdered:links };
     }else{
         return { subgraphNetwork:subgraphNetwork,linksOrdered:[] };
@@ -408,7 +411,7 @@ function sortLinksWithGroupCycle(subgraphNetwork:SubgraphNetwork,groupCycle:stri
  * @param source "node" if the parent links are needed, "groupCycle" if the child links are needed
  * @returns links that are child or parent of a group cycle
  */
-function getLinksNodeGroupCycle(subgraphNetwork:SubgraphNetwork,nodeId:string,groupCycleId:string,source:"node"|"groupCycle"):LinkLayout[]{
+async function getLinksNodeGroupCycle(subgraphNetwork:SubgraphNetwork,nodeId:string,groupCycleId:string,source:"node"|"groupCycle"):Promise<LinkLayout[]>{
     if (source==="node"){
         // node to group cycle
         return Object.values(subgraphNetwork.network.links).filter((link) => {
@@ -452,9 +455,9 @@ export function getLinksForListNodes(network: NetworkLayout, nodes: string[]): {
  * 
  * @returns An object containing the list of nodes and links for the cycle group.
  */
-export function getListNodeLinksForCycleGroup(subgraphNetwork:SubgraphNetwork,groupCycleName:string,positionAsFixed:boolean=false)
+export function getListNodeLinksForCycleGroupAsArray(subgraphNetwork:SubgraphNetwork,groupCycleName:string,positionAsFixed:boolean=false)
 :{nodes:{ id: string,fx?:number, fy?:number,x?:number,y?:number }[],links:{source:string,target:string}[]}{
-    const nodesGroupCycle=getNodesPlacedInGroupCycle(subgraphNetwork,groupCycleName,positionAsFixed);
+    const nodesGroupCycle=getNodesPlacedInGroupCycleAsArray(subgraphNetwork,groupCycleName,positionAsFixed);
     const nodesGroupCycleName=Object.values(nodesGroupCycle).map(node=>node.id);
     const linksGroupCycle=getLinksForListNodes(subgraphNetwork.network,nodesGroupCycleName);
     return {nodes:nodesGroupCycle,links:linksGroupCycle};
@@ -469,7 +472,7 @@ export function getListNodeLinksForCycleGroup(subgraphNetwork:SubgraphNetwork,gr
  * @returns An object containing the list of nodes and links for the cycle group.
  */
 export function getListNodeLinksForCycleGroupAsObject(subgraphNetwork:SubgraphNetwork,groupCycleName:string)
-:{nodes:{[key:string]:{ x:number,y:number }},links:{source:string,target:string}[]}{
+:{nodes:{[key:string]:Coordinate},links:{source:string,target:string}[]}{
     const nodesGroupCycle=getNodesPlacedInGroupCycleAsObject(subgraphNetwork,groupCycleName);
     const nodesGroupCycleName=Object.keys(nodesGroupCycle);
     const linksGroupCycle=getLinksForListNodes(subgraphNetwork.network,nodesGroupCycleName);
diff --git a/src/composables/CalculateSize.ts b/src/composables/CalculateSize.ts
index c564f7c5c9d59965487d614c786ef572becfc993..24f3957b4e8c889b98c82833487f8bef79f6a67f 100644
--- a/src/composables/CalculateSize.ts
+++ b/src/composables/CalculateSize.ts
@@ -1,17 +1,12 @@
 // Type imports
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Node } from "@metabohub/viz-core/src/types/Node";
+import { Network, Node, GraphStyleProperties } from  "../types/TypeVizCore";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
 import { Subgraph, TypeSubgraph } from "../types/Subgraph";
 import { Coordinate, Size } from "../types/CoordinatesSize";
 
 // Composable imports
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
 import { inCycle, isSideCompound } from "./GetSetAttributsNodes";
 
-// General imports
-
-
 
 /**
  * This file contains functions to calculate the size of nodes, edges and subgraphs. Anf function to shift coordinates depending on node size.
@@ -123,7 +118,6 @@ export function getSizeNodePixel(node:Node,styleNetwork:GraphStyleProperties):Si
             }
         });
     }
-
     return {height:height,width:width};
 }
 
@@ -365,8 +359,8 @@ function getSizeGroupCycles(subgraphNetwork:SubgraphNetwork,groupCycle:Subgraph)
     if (groupCycle.precalculatedNodesPosition){
         // get all nodes with x and y coordinates
         const listNodesMetadata = Object.entries(groupCycle.precalculatedNodesPosition)
-                        .filter(([_,item]) => item.x !== undefined && item.y !== undefined);
-        const listCoordinates = listNodesMetadata.map(([_,item]) => {return {x:item.x,y:item.y}});
+                        .filter(([_,item]) => item.x !== undefined && item.y !== undefined && item.x!==null && item.y!==null);
+        const listCoordinates = listNodesMetadata.map(([_,item]) => {return {x:item.x as number,y:item.y as number}});
         const listID = listNodesMetadata.map(([id,_]) => {return id});
         // get the size of the rectangle
         const {width,height,center}=rectangleSize(listCoordinates,listID,subgraphNetwork);
@@ -389,7 +383,7 @@ function getSizeGroupCycles(subgraphNetwork:SubgraphNetwork,groupCycle:Subgraph)
  * @param style - The style properties used to calculate the top left coordinate.
  * @param moveCycleToo - Optional parameter indicating whether to move nodes in cycles as well. Defaults to true.
  */
-export function shiftAllToGetTopLeftCoord(network:Network,style:GraphStyleProperties,moveCycleToo:boolean=true) {
+export function shiftAllToGetTopLeftCoord(network:Network,style:GraphStyleProperties,moveCycleToo:boolean=true):void {
     Object.values(network.nodes).forEach(node=>{
         if( moveCycleToo || !inCycle(network,node.id)){
             const {x,y}=getTopLeftCoordFromCenter(node,style);
@@ -408,7 +402,11 @@ export function shiftAllToGetTopLeftCoord(network:Network,style:GraphStyleProper
  */
 export function getTopLeftCoordFromCenter(node:Node,style:GraphStyleProperties):Coordinate{
     const size = getSizeNodePixel(node,style);
-    return {x:node.x-size.width/2,y:node.y-size.height/2}
+    let x = node.x-size.width/2;
+    let y = node.y-size.height/2;
+    x=parseFloat(x.toFixed(2));
+    y=parseFloat(y.toFixed(2));
+    return {x:x,y:y}
 }
 
 /**
@@ -420,5 +418,9 @@ export function getTopLeftCoordFromCenter(node:Node,style:GraphStyleProperties):
  */
 export function getCenterCoordFromTopLeft(node:Node,style:GraphStyleProperties):Coordinate{
     const size = getSizeNodePixel(node,style);
-    return {x:node.x+size.width/2,y:node.y+size.height/2}
+    let x = node.x+size.width/2;
+    let y = node.y+size.height/2;
+    x=parseFloat(x.toFixed(2));
+    y=parseFloat(y.toFixed(2));
+    return {x:x,y:y}
 }
\ No newline at end of file
diff --git a/src/composables/CalculateStartNodes.ts b/src/composables/CalculateStartNodes.ts
index c56a1f854f2afc810df6a1869b99c63e041b9d72..c37bb57289201e628db72dbb7832c3febd1a8fc5 100644
--- a/src/composables/CalculateStartNodes.ts
+++ b/src/composables/CalculateStartNodes.ts
@@ -1,6 +1,6 @@
 // Types imports
 import { StartNodesType } from "../types/EnumArgs";
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Network } from  "../types/TypeVizCore";
 import { NetworkLayout, NodeLayout } from "../types/NetworkLayout";
 
 // Composable imports
diff --git a/src/composables/CheckNetwork.ts b/src/composables/CheckNetwork.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa98a7b859f99cc3e75a488cf1d8cbd1681b02ac
--- /dev/null
+++ b/src/composables/CheckNetwork.ts
@@ -0,0 +1,302 @@
+// Type imports
+import {Network, Node, Link, GraphStyleProperties} from '../types/TypeVizCore';
+
+/**
+ * This file contains functions to validate the format of network objects, nodes, and links.
+ * 
+ * -> checkNetworkFormat: 
+ *      Validates the format of a network object.
+ * 
+ * -> checkNodeFormat:
+ *      Validates the format of a node object.
+ * 
+ * -> checkValidIdForViz:
+ *      Validates the given string `id` against several patterns to determine if it is a valid identifier for visualization.
+ * 
+ * -> checkLinkFormat:
+ *      Validates the format of a link within a network.
+ *
+ * -> checkNetworkStyleFormat:
+ *      Checks if the provided network style format is consistent with the network structure.
+ */
+
+/**
+ * Validates the format of a network object.
+ *
+ * @param network - The network object to validate.
+ * @param checkExactKeysInterface - If true, ensures the network object contains only the exact keys defined in the interface.
+ * @param checkIDForViz - If true, checks the ID for visualization. Defaults to true.
+ * 
+ * @throws Will throw an error if the network is not an object.
+ * @throws Will throw an error if the network lacks mandatory keys.
+ * @throws Will throw an error if the network has invalid types for mandatory keys.
+ * @throws Will throw an error if the network contains invalid keys when `checkExactKeysInterface` is true.
+ * @throws Will throw an error if the invalid nodes or links.
+ */
+export async function checkNetworkFormat(network: Network, checkExactKeysInterface: boolean,checkIDForViz:boolean=true): Promise<void> {
+
+     // Check if object
+     if(typeof network !== 'object') {
+        throw new Error(`Network is not an object.`);
+    }
+
+    // Check mandatory keys
+    const mandatoryInterfaceKeys :string[]= ['id', 'nodes', 'links'];
+    const mandatoryInterfaceType : string[] = ['string', 'object', 'object'];
+    for (const i in mandatoryInterfaceKeys){
+        const key:string = mandatoryInterfaceKeys[i];
+        if (!Object.keys(network).includes(key)) {
+            throw new Error(`Network lacks key: ${key}`);
+        }
+        const type = mandatoryInterfaceType[i];
+        if (typeof (network as any)[key] !== type) {
+            throw new Error(`Network has an invalid type for key ${key}: expected ${type}, got ${typeof (network as any)[key] }`);
+        }
+    }
+
+    // Check if the network object respects exactly the interface
+    if (checkExactKeysInterface) {
+        const allInterfaceKeys=['id','nodes','links','label', 'type', 'rescale'];
+        Object.keys(network).forEach(key => {
+            if (!allInterfaceKeys.includes(key)) {
+                throw new Error(`Network has an invalid key: ${key}`);
+            }
+        });
+
+    }
+
+    // Check nodes
+    Object.entries(network.nodes).forEach(([nodeId, node]) => {
+        checkNodeFormat(nodeId, node, checkExactKeysInterface,checkIDForViz);
+    });
+
+
+	// Check links
+	network.links.forEach(link => {
+        checkLinkFormat(network, link, checkExactKeysInterface);
+    });
+
+}
+
+/**
+ * Validates the format of a node object based on specified criteria.
+ *
+ * @param nodeId - The identifier of the node.
+ * @param node - The node object to be validated.
+ * @param checkExactKeysInterface - If true, ensures the node has exactly the keys defined in the interface.
+ * @param checkIDForViz - If true, validates the format of the node ID for visualization purposes.
+ * 
+ * @throws Will throw an error if the node is not an object.
+ * @throws Will throw an error if the node lacks mandatory keys or has keys with invalid types.
+ * @throws Will throw an error if the node ID does not match the provided nodeId.
+ * @throws Will throw an error if the node contains invalid keys when `checkExactKeysInterface` is true.
+ * @throws Will throw an error if the node contains the `metadataLayout` key when `checkExactKeysInterface` is false.
+ * @throws Will throw an error if the node ID is invalid for Viz when `checkIDForViz` is true.
+ */
+export function checkNodeFormat(nodeId:string, node: Node, checkExactKeysInterface: boolean,checkIDForViz:boolean=false):void {
+    
+    // Check if object
+    if(typeof node !== 'object') {
+        throw new Error(`Node ${nodeId} is not an object.`);
+    }
+
+    // Check mandatory keys
+    const mandatoryInterfaceKeys :string[]= ['id', 'x', 'y'];
+    const mandatoryInterfaceType : string[] = ['string', 'number', 'number'];
+    for (const i in mandatoryInterfaceKeys){
+        const key:string = mandatoryInterfaceKeys[i];
+        if (!Object.keys(node).includes(key)) {
+            throw new Error(`Node ${nodeId} lacks key: ${key}`);
+        }
+        const type = mandatoryInterfaceType[i];
+        if (typeof (node as any)[key] !== type) {
+            throw new Error(`Node ${nodeId} has an invalid type for key ${key}: expected ${type}, got ${typeof (node as any)[key] }`);
+        }
+    }
+    
+    // Check if the node id matches the key
+    if (node.id !== nodeId) {
+        throw new Error(`Node id and key mismatch: expected ${nodeId} for id, got ${node.id}`);
+    }
+
+    // Check format of id 
+    if(checkIDForViz){
+        checkValidIdForViz(nodeId);
+    }
+
+    // Check if the node respects exactly the interface
+    if (checkExactKeysInterface) {
+        const allInterfaceKeys=['id','x','y','label', 'classes', 'hidden', 'selected', 'metadata'];
+        Object.keys(node).forEach(key => {
+            if (!allInterfaceKeys.includes(key)) {
+                throw new Error(`Node ${nodeId} has an invalid key: ${key}`);
+            }
+        });
+    }
+
+    // If checkExactInterface is false, check if the node has metadataLayout (not allowed has used for algorithm)
+    if (!checkExactKeysInterface &&  Object.keys(node).includes('metadataLayout')) {
+        throw new Error(`Node ${nodeId} contains metadataLayout, which is not allowed.`);
+    }
+}
+
+
+/**
+ * Validates the given string `id` against several patterns to determine if it is a valid identifier for visualization.
+ * 
+ * The function checks if the `id` matches any of the following patterns:
+ * - Alphabetic string with underscores or digits, not starting with a digit.
+ * - Numeral (integer, float, or negative float).
+ * - Double-quoted string with possible escaped quotes.
+ * - HTML string <...>.
+ * 
+ * If the `id` does not match any of these patterns, an error is thrown.
+ * 
+ * @param id - The string to be validated.
+ * @throws {Error} If the `id` does not match any of the valid patterns.
+ */
+export function checkValidIdForViz(id: string): void {
+    // Regular expression for alphabetic string with underscores or digits, not starting with a digit
+    const alphabeticPattern = /^[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*$/;
+    
+    // Regular expression for numeral (integer, float, or negative float)
+    const numeralPattern = /^-?(\d+(\.\d+)?|\.\d+)$/;
+    
+    // Regular expression for double-quoted string with possible escaped quotes
+    const doubleQuotedStringPattern = /^"([^"\\]*(\\["\\])*[^"]*)*"$/;
+    
+    // Regular expression for HTML string
+    const htmlPattern = /^<[^>]*>$/;
+    
+    // Test the input string against all patterns
+    if (
+        !alphabeticPattern.test(id) && 
+        !numeralPattern.test(id) &&
+        !doubleQuotedStringPattern.test(id) &&
+        !htmlPattern.test(id)
+    ) {
+        throw new Error(`Invalid string: ${id}`);
+    }
+}
+
+
+/**
+ * Validates the format of a link within a network.
+ *
+ * @param network - The network object containing nodes.
+ * @param link - The link object to be validated.
+ * @param checkExactKeysInterface - Flag to check if the link respects exactly the interface.
+ * 
+ * @throws Will throw an error if the link is not an object.
+ * @throws Will throw an error if the link lacks mandatory keys.
+ * @throws Will throw an error if the link has an invalid type for any mandatory key.
+ * @throws Will throw an error if the link's source node is not in the network.
+ * @throws Will throw an error if the link's source node pointer is invalid.
+ * @throws Will throw an error if the link's target node is not in the network.
+ * @throws Will throw an error if the link's target node pointer is invalid.
+ * @throws Will throw an error if the link has any invalid keys when `checkExactKeysInterface` is true.
+ */
+export function checkLinkFormat(network:Network,link: Link, checkExactKeysInterface: boolean):void {
+
+    // Check if object
+    if(typeof link !== 'object') {
+        throw new Error(`Link is not an object.`);
+    }
+
+    // Check mandatory keys
+    const mandatoryInterfaceKeys :string[]= ['id', 'source', 'target'];
+    const mandatoryInterfaceType : string[] = ['string', 'object', 'object'];
+    for (const i in mandatoryInterfaceKeys){
+        const key:string = mandatoryInterfaceKeys[i];
+        if (!Object.keys(link).includes(key)) {
+            throw new Error(`Link lacks key: ${key}`);
+        }
+        const type = mandatoryInterfaceType[i];
+        if (typeof (link as any)[key] !== type) {
+            throw new Error(`Link has an invalid type for key ${key}: expected ${type}, got ${typeof (link as any)[key] }`);
+        }
+    }
+
+    // Check source
+    if (!(link.source.id in network.nodes)) {
+        throw new Error(`Link ${link.id} has no source node in the network.`);
+    }
+    if (link.source !== network.nodes[link.source.id]) {
+        throw new Error(`Link ${link.id} has an invalid source node (no pointer).`);
+    }
+
+    // Check target
+    if (!(link.target.id in network.nodes)) {
+        throw new Error(`Link ${link.id} has no target node in the network.`);
+    }
+    if (link.target !== network.nodes[link.target.id]) {
+        throw new Error(`Link ${link.id} has an invalid target node (no pointer).`);
+    }
+    
+
+    // Check if the link respects exactly the interface
+    if (checkExactKeysInterface) {
+        const allInterfaceKeys=['id','source','target','label', 'classes', 'type', 'relation', 'directed','metadata'];
+        Object.keys(link).forEach(key => {
+            if (!allInterfaceKeys.includes(key)) {
+                throw new Error(`Link has an invalid key: ${key}`);
+            }
+        });
+  
+    }
+}
+
+
+
+/**
+ * Checks if the provided network style format is consistent with the network structure.
+ * 
+ * This function verifies that all node and link classes present in the network have corresponding styles
+ * defined in the network style properties. If any class is missing a style, an error is thrown.
+ * 
+ * @param network - The network object containing nodes and links.
+ * @param networkStyle - The style properties for the network, including nodeStyles and linkStyles.
+ * 
+ * @throws Will throw an error if node classes are present in the network but no nodeStyles are provided.
+ * @throws Will throw an error if any node class is missing in networkStyle.nodeStyles.
+ * @throws Will throw an error if link classes are present in the network but no linkStyles are provided.
+ * @throws Will throw an error if any link class is missing in networkStyle.linkStyles.
+ */
+export function checkNetworkStyleFormat(network: Network, networkStyle: GraphStyleProperties):void {
+
+    // Get unique node classes
+	const nodeClasses = new Set<string>();
+	Object.values(network.nodes).forEach(node => {
+		if (node.classes) {
+			node.classes.forEach(nodeClass => nodeClasses.add(nodeClass));
+		}
+	});
+
+	// Check if all node classes are present in nodeStyles
+    if (!networkStyle.nodeStyles && nodeClasses.size > 0) {
+        throw new Error('Node classes are present in the network but no nodeStyles are provided.');
+    }
+	nodeClasses.forEach(nodeClass => {
+		if (networkStyle.nodeStyles && !(nodeClass in networkStyle.nodeStyles)) {
+			throw new Error(`Node class ${nodeClass} is missing in networkStyle.nodeStyles.`);
+		}
+	});
+
+	// Get unique links classes
+	const linkClasses = new Set<string>();
+	network.links.forEach(link => {
+		if (link.classes) {
+			link.classes.forEach(linkClass => linkClasses.add(linkClass));
+		}
+	});
+
+	// Check if all link classes are present in linkStyles
+    if (!networkStyle.linkStyles && linkClasses.size > 0) {
+        throw new Error('Link classes are present in the network but no linkStyles are provided.');
+    }
+	for (const linkClass of linkClasses) {
+		if (networkStyle.linkStyles && !(linkClass in networkStyle.linkStyles)) {
+			throw new Error(`Link class ${linkClass} is missing in networkStyle.linkStyles.`);
+		}
+	}
+}
diff --git a/src/composables/ConvertFromNetwork.ts b/src/composables/ConvertFromNetwork.ts
index 6f3f86420981a476d4c609fae1a3952859177933..861e6448369075b490f239412f29aaaa123165d7 100644
--- a/src/composables/ConvertFromNetwork.ts
+++ b/src/composables/ConvertFromNetwork.ts
@@ -1,10 +1,9 @@
 // Type imports
-import { Network } from '@metabohub/viz-core/src/types/Network';
-import { Subgraph, TypeSubgraph } from '../types/Subgraph';
+import { Network } from  "../types/TypeVizCore";
+import { TypeSubgraph } from '../types/Subgraph';
 import { SubgraphNetwork } from '../types/SubgraphNetwork';
-import { Node } from '@metabohub/viz-core/src/types/Node';
 import { LinkLayout, NetworkLayout, NodeLayout } from '../types/NetworkLayout';
-import { Ordering } from '../types/EnumArgs';
+import { AttributesViz, SubgraphViz } from '../types/TypeViz';
 
 // Composable imports
 import { addMainChainForViz,subgraphDot } from './SubgraphForViz';
@@ -17,13 +16,7 @@ import { cycleMetanodeLink, sortLinksWithAllGroupCycle } from './CalculateRelati
 //import  dagre  from 'dagrejs/dist/dagre.js';
 import { Graph } from "@viz-js/viz";
 import * as GDS from 'graph-data-structure';
-import { h } from 'vue';
-import { link } from 'fs';
-//import { s } from 'vitest/dist/reporters-1evA5lom';
-import { get } from 'http';
-//import cytoscape, { ElementDefinition,Stylesheet } from 'cytoscape';
-import { layout } from 'dagrejs';
-import { dot } from 'node:test/reporters';
+
 
 
 /**
@@ -47,12 +40,6 @@ import { dot } from 'node:test/reporters';
  * *********************************
  * 1.  Layout library
  * 
- * -> networkToDagre
- *     take a network object and return a dagre.graphlib.Graph object containing the same nodes and edge
- * 
- * -> networkToCytoscape
- *      take a network object and return a cytoscape object containing the same nodes and edge
- * 
  * -> networkToDOT
  *      take a network object and return a DOT string representation
  * 
@@ -167,88 +154,6 @@ export function networkToAdjacentObject(network:Network):{[key : string]:string[
 //___________________________________________________1.  Layout library __________________________________________________________________________
 
 
-
-/** 
- * Take a network object and return a dagre.graphlib.Graph object containing the same nodes and edge 
- * @param {Network}  Network object 
- * @param  graphAttributes for dagre layout (see https://github.com/dagrejs/dagre/wiki)
- * @returns {dagre.graphlib.Graph} Return dagre.graphlib.Graph object 
- */
-// export function networkToDagre(network: Network,graphAttributes={}): dagre.graphlib.Graph{
-
-//     // initialisation dagre graph
-//     var g = new dagre.graphlib.Graph();
-//     g.setGraph(graphAttributes);
-//     g.setDefaultEdgeLabel(() => ({}));
-
-//     // insert nodes into graph
-//     Object.values(network.nodes).forEach((node) => {
-//         const { id, label, x, y } = node;
-//         g.setNode(id, { label, width: 100, height: 100, x, y });
-//     });
-
-//     // insert edges into graph
-//     network.links.forEach((link) => {
-//         const { source, target } = link;
-//         g.setEdge(source.id, target.id);
-//     });
-
-//     return g;
-
-// }
-
-
-  
-/**
- * Converts a network object to a Cytoscape object.
- * 
- * @param network - The network object to convert.
- * @param initialPosition - Optional. Specifies whether to initialize the position of the nodes. Default is false.
- * @returns The converted Cytoscape object.
- */
-// export function networkToCytoscape(network: Network, initialPosition:boolean=false): cytoscape.Core {
-
-//     // Convert nodes
-//     const nodes: ElementDefinition[] = Object.values(network.nodes).map(node => ({
-//         data: {
-//           id: node.id,
-//         },
-//         position: {
-//           x: node.x,
-//           y: node.y,
-//         },
-//       }));
-  
-//     // Convert links
-//     const edges: ElementDefinition[] = [];
-//     network.links.forEach(link => {
-//         edges.push({
-//         data: {
-//           id: link.id,
-//           source: link.source.id,
-//           target: link.target.id,
-//         }
-//       });
-//     });
-
-
-//     if (initialPosition){
-//         return cytoscape({
-//             container: undefined, 
-//             elements: {nodes:nodes, edges:edges},
-//             layout: { 
-//               name: 'preset', // to initialize the position of the nodes
-//             },
-//     });
-//     }else{
-//         return cytoscape({
-//         container: undefined, 
-//         elements: {nodes:nodes, edges:edges},
-//         });
-//   }
-// }
-
-
 /**
  * Converts a SubgraphNetwork object to a DOT string representation.
  * 
@@ -257,10 +162,10 @@ export function networkToAdjacentObject(network:Network):{[key : string]:string[
  * @param addNodes - Optional. Specifies whether to include nodes in the DOT string. Default is true.
  * @param groupOrCluster - Optional. Specifies whether to use "group" or "cluster" for grouping nodes. Default is "cluster".
  * @param orderChange - Optional. Specifies whether to change the order of links in the DOT string. Default is false.
- * @returns The DOT string representation of the SubgraphNetwork.
+ * @returns Promise of the DOT string representation of the SubgraphNetwork.
  */
-export function networkToDOT(subgraphNetwork:SubgraphNetwork,cycle:boolean=true, addNodes:boolean=true,groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false): string{
-    const graphViz=networkToViz(subgraphNetwork,cycle,addNodes,groupOrCluster,orderChange);
+export async function networkToDOT(subgraphNetwork:SubgraphNetwork,cycle:boolean=true, addNodes:boolean=true,groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false): Promise<string>{
+    const graphViz=await networkToViz(subgraphNetwork,cycle,addNodes,groupOrCluster,orderChange);
     const dotString=graphVizToDot(graphViz);
     return dotString;
 }
@@ -270,9 +175,9 @@ export function networkToDOT(subgraphNetwork:SubgraphNetwork,cycle:boolean=true,
  * @param {Network}  Network object 
  * @param  graphAttributes for viz dot layout (see https://graphviz.org/docs/layouts/dot/)
  * @param clusters clusters for viz
- * @returns {Graph} Return graph object for viz
+ * @returns {Graph} Return promise of graph object for viz
  */
-export function networkToViz(subgraphNetwork:SubgraphNetwork,cycle:boolean=true, addNodes:boolean=true,groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false): Graph{
+export async function networkToViz(subgraphNetwork:SubgraphNetwork,cycle:boolean=true, addNodes:boolean=true,groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false):Promise<Graph>{
 
     if (groupOrCluster==="group" && !addNodes){
         console.warn('Group without nodes in the file not taken into account'); 
@@ -303,7 +208,7 @@ export function networkToViz(subgraphNetwork:SubgraphNetwork,cycle:boolean=true,
     
     // insert link (but with cycle metanode if cycle is true) 
     let links:LinkLayout[]=[];
-    const resultOrdering=sortLinksWithAllGroupCycle(subgraphNetwork,orderChange);   // order of link changed  for cycle group
+    const resultOrdering=await sortLinksWithAllGroupCycle(subgraphNetwork,orderChange);   // order of link changed  for cycle group
     links=resultOrdering.linksOrdered;
     subgraphNetwork=resultOrdering.subgraphNetwork; // BEWARE: do this before adding cycle metanode (because of attribut ordering)
     links.forEach((link)=>{   
@@ -471,7 +376,7 @@ export function graphVizToDot(vizGraph:Graph, subgraphFirst:boolean=true):string
  * @param obj - The object to convert.
  * @returns A string representation of the object with attribute-value pairs.
  */
-function customStringify(obj:AttributesViz|undefined) {
+function customStringify(obj:AttributesViz|undefined):string{
     if (!obj || Object.keys(obj).length === 0) {
         return "[]";
     }
diff --git a/src/composables/ConvertToNetwork.ts b/src/composables/ConvertToNetwork.ts
index 4b0f600e6726d5ca270fd69ac471afd2ef582e6c..56702f7a4897feedcdcd2286cfff25b8cb7eb7df 100644
--- a/src/composables/ConvertToNetwork.ts
+++ b/src/composables/ConvertToNetwork.ts
@@ -1,20 +1,15 @@
 // Type imports
 import { JsonViz } from '../types/FormatJsonViz';
-import { Coordinate } from '../types/CoordinatesSize';
-import { Network } from '@metabohub/viz-core/src/types/Network';
-import type { Node } from "@metabohub/viz-core/src/types/Node";
+import { Network } from  "../types/TypeVizCore";
 import { NetworkLayout } from '../types/NetworkLayout';
 import { SubgraphNetwork } from '../types/SubgraphNetwork';
+import { TypeSubgraph } from '../types/Subgraph';
+
 
 // Composable imports
 import { assignRankOrder } from './CalculateStartNodes';
-import { getSizeNodePixel } from './CalculateSize';
 
-// General imports
-// import cytoscape, { ElementDefinition } from 'cytoscape';
-// import  dagre  from 'dagrejs/dist/dagre.js';
-import { type } from 'os';
-import { TypeSubgraph } from '../types/Subgraph';
+
 
 /**
  * This file contains functions to get or update a network from another format.
@@ -22,9 +17,6 @@ import { TypeSubgraph } from '../types/Subgraph';
  * -> networkLayoutToNetwork :
  *      Convert a network layout object (network with information to calculate layout) into a network object
  * 
- * -> changeNetworkFromDagre :
- *      Take dagre.graphlib.Graph object and the network associated (with the graph) : change the position and metadata (rank and order) of network's node by the one of the graph.
- * 
  * -> changeNetworkFromViz :
  *      Take a json of a viz graph and the network associated (with the json) : change the position and metadata (rank and order) of network's node by the one of the json.
  * 
@@ -53,33 +45,6 @@ export function networkLayoutToNetwork(networkLayout: NetworkLayout): Network {
 }
 
 
-// /**
-//  * Take dagre.graphlib.Graph object and the network associated (with the graph) : change the position and metadata (rank and order) of network's node by the one of the graph.
-//  * The graph and network need to have the same nodes !
-//  * @param {dagre.graphlib.Graph}  dagre.graphlib.Graph object 
-//  * @param {Network} Network object (value of pointer)
-//  */
-// export async function changeNetworkFromDagre(graph: dagre.graphlib.Graph,network: NetworkLayout): Promise<void>{
-//     Object.entries(graph["_nodes"]).forEach(([node, nodeData]:[string, dagre.graphlib.Node]) => {
-//         if (!network.nodes[node].metadataLayout) {
-//             network.nodes[node].metadataLayout = {};
-//         }
-//         const { x, y, _order,_rank  } = nodeData;
-//         // if there is some position x and y : network is updated
-//         if (x !== undefined && y !== undefined){
-//             if (network.nodes[node]) {
-//                 network.nodes[node].x = x;
-//                 network.nodes[node].y = y;
-//             } else {
-//                 throw new Error(`Node '${node}' not found in the network.`);
-//             }
-//             network.nodes[node].metadataLayout.order = _order;
-//             network.nodes[node].metadataLayout.rank = _rank / 2; // because rank 0 is 0, and the next is 2, 4, ...
-//         }
-//     });
-// }
-
-
 /**
  * Take a json of a viz graph and the network associated (with the json) : change the position and metadata (rank and order) of network's node by the one of the json.
  * The json and network need to have the same nodes !
@@ -93,7 +58,6 @@ export async function changeNetworkFromViz(json: JsonViz, subgraphNetwork: Subgr
     json["objects"].forEach((node) => {
        
         const nodeId = node.name;
-
         // if node is a 'classic' node
         if (nodeId in network.nodes && Object.keys(node).includes("pos")){
                 const pos = node["pos"]?.split(',') ?? [];
@@ -116,9 +80,10 @@ export async function changeNetworkFromViz(json: JsonViz, subgraphNetwork: Subgr
           
             subgraphNetwork[TypeSubgraph.CYCLEGROUP][nodeId].position={x:x,y:y};
             
-        }else{
-            throw new Error(`Node ou group cycle  '${nodeId}' not found in the network.`);
+        }else if (!node.nodes || node.nodes.length===1){
+            throw new Error(`Node (or group cycle)  '${nodeId}' not found in the network. Or subgraph of one node`);
         }
+        // else it has several nodes, so cluster/subgraph
     });               
 
     if(assignRank){
@@ -129,24 +94,5 @@ export async function changeNetworkFromViz(json: JsonViz, subgraphNetwork: Subgr
 }
 
 
-// /**
-//  * Updates the positions of nodes in the network based on the provided Cytoscape JSON.
-//  * 
-//  * @param jsonCytoscape - The Cytoscape JSON containing the elements and their positions.
-//  * @param network - The network to update.
-//  */
-// export function changeNetworkFromCytoscape(jsonCytoscape: {elements:  { nodes:ElementDefinition[] } }, network:Network) : void {
-
-//     jsonCytoscape.elements.nodes.forEach((node: any) => {
-//         const idNode= node.data.id;
-//         if (Object.keys(network.nodes).includes(idNode)) {
-//             network.nodes[idNode].x = node.position.x;
-//             network.nodes[idNode].y = node.position.y;
-//         }else{
-//             throw new Error(`Node '${idNode}' not found in the network.`);
-//         }
-//     });
-// }
-
 
 
diff --git a/src/composables/GetSetAttributsNodes.ts b/src/composables/GetSetAttributsNodes.ts
index 711f6d37c5a53f08b87b3335b5181e855008575f..0b94c7944cce3c35dbbf65e1cad4e3fe44787fbe 100644
--- a/src/composables/GetSetAttributsNodes.ts
+++ b/src/composables/GetSetAttributsNodes.ts
@@ -1,8 +1,6 @@
 // Type imports
 import { TypeSubgraph } from "../types/Subgraph";
-import { Node } from "@metabohub/viz-core/src/types/Node";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Link } from "@metabohub/viz-core/src/types/Link";
+import { Node, Network, Link } from  "../types/TypeVizCore";
 import { NetworkLayout } from "../types/NetworkLayout";
 
 /**
@@ -28,9 +26,6 @@ import { NetworkLayout } from "../types/NetworkLayout";
  * *********************************
  * 2. Reversible
  * 
- * -> addMetadataReversibleWithClass :
- *         Adds the reversible attribute to the given node in the network.
- * 
  * -> addReversibleNetwork :
  *       Adds the reversible attribute to the given node in the network.
  * 
@@ -53,6 +48,8 @@ import { NetworkLayout } from "../types/NetworkLayout";
  * *********************************
  * 3.  Cycle
  * 
+ * -> inCycle :
+ *     Checks if a node is part of a cycle in the network.
  * 
  */
 
@@ -111,22 +108,9 @@ export function isDuplicate(network:Network,nodeID:string):boolean{
 //___________________________________________________2.  Reversible __________________________________________________________________________
 
 export const classReversible="reversible";
-export const reversibleAttribute="reversible";
+export const reversibleAttribute="isReversible";
 export const reactionClass="reaction";
 
-/**
- * Checks if a node in the network is reversible by checking its classes.
- * => done for my type of file, not necessaty if reversible information already in the metadata
- * @param network - The network object.
- */
-export async function addMetadataReversibleWithClass(network:Network):Promise<void>{
-    Object.values(network.nodes).forEach((node) => {
-      if(node.classes && node.classes.includes(classReversible)){
-        addReversibleNetwork(network,node.id);
-      }
-    });
-  }
-
 
 /**
  * Adds the reversible attribute to the given node in the network.
diff --git a/src/composables/LayoutDrawCycle.ts b/src/composables/LayoutDrawCycle.ts
index 8ab2523e15475edd97c59dd83227f0911fe06336..958977730ddc2bc18b981de473a17929d730eb75 100644
--- a/src/composables/LayoutDrawCycle.ts
+++ b/src/composables/LayoutDrawCycle.ts
@@ -1,36 +1,128 @@
 // Type imports
 import { Subgraph, TypeSubgraph } from "../types/Subgraph";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-import { Link } from "@metabohub/viz-core/src/types/Link";
-import { Node } from "@metabohub/viz-core/src/types/Node";
+import { Network ,GraphStyleProperties} from "../types/TypeVizCore";
+import { Coordinate, CoordinateNull } from "../types/CoordinatesSize";
+import { NodeLayout } from "../types/NetworkLayout";
+
+
 
 // Composable imports
 import { getMeanNodesSizePixel, medianEdgeLength, rectangleSize } from "./CalculateSize";
-import { isIntersectionGraph, isOverlapNodes, isOverlapNodesEdges } from "./countIntersections";
-import { inCycle } from "./GetSetAttributsNodes";
+import { isIntersectionGraph, isOverlapNodes, isOverlapNodesEdges } from "./CalculateOverlaps";
+import { childNodeNotInCycle, getListNodeLinksForCycleGroupAsArray, getListNodeLinksForCycleGroupAsObject, getNodesPlacedInGroupCycleAsObject, parentNodeNotInCycle } from "./CalculateRelationCycle";
+import { updateNodeMetadataSubgraph } from "./SubgraphForSubgraphNetwork";
 
 
 // General imports
-import { group } from "console";
-import { start } from "repl";
-import { a } from "vitest/dist/suite-ghspeorC";
 import * as d3 from 'd3';
-import { link } from "fs";
-import { emit } from "process";
-import { childNodeNotInCycle, getListNodeLinksForCycleGroup, getListNodeLinksForCycleGroupAsObject, parentNodeNotInCycle } from "./CalculateRelationCycle";
 
 
+/**
+ * This file contains functions to calculate the coordinates of nodes to draw them as circle.
+ * 
+ * *********************************
+ * 
+ * 0. Calculate coordinates for nodes in cycles
+ * 
+ * -> coordinateAllCycles :
+ *      give precalculated coordinates for all cycles in the subgraphNetwork, there are not in the nodes network.
+ * 
+ * -> coordinateCycle :
+ *     give coordinates for a cycle in a group of cycles. A group of cycle is cycles with common nodes. All the group of cycle are independent (no common nodes) from each other.
+ * 
+ * -> independentCycleCoordinates :
+ *    Calculate position of a cycle as circle, it will be centered at the origin (0,0) and its nodes are positioned in a circular layout.
+ * 
+ * -> tangentCycleCoordinates :
+ *      Calculate position of a cycle as circle, it will be tangent to the circle of the fixed node.
+ * 
+ * -> lineCycleCoordinates :
+ *      Calculate position of a cycle as line(s). A line for each unfixed interval in the cycle.
+ * 
+ * -> cycleNodesCoordinates :
+ *      Calculate position of a cycle as circle, it will be centered at the origin (0,0) and its nodes are positioned in a circular layout.
+ * 
+ * -> lineNodesCoordinates :
+ *      Calculate position of a cycle as line(s). A line for each unfixed interval in the cycle.
+ * 
+ * -> forceGroupCycle :
+ *      Place node already placed in a cycle group with a force layout
+ * 
+ * 
+ * *********************************
+ * 
+ * 1. Find top node for cycle coordinate
+ * 
+ * -> findTopCycleNode :
+ *     Find the index of the top cycle node based on the given subgraph network and cycle nodes.
+ * 
+ * -> getIndexNodesAssociatedMinY :
+ *       Returns indices of lists containing minimum y-coordinate nodes.
+ * 
+ * -> getIndexNodesAssociatedMaxY :
+ *       Returns indices of lists containing minimum y-coordinate nodes.
+ * 
+ * -> nodeMedianX :
+ *      Get the name of the node with the median x-coordinate from a list of nodes.
+ * 
+ * *********************************
+ * 
+ * 2. Utilitary functions for cycle coordinate
+ * 
+ * -> scoreSortingCycleForDrawing :
+ *     calculate score for sorting function to know order of cycle drawing.
+ * 
+ * -> calculateAllScoresForSubgraphs :
+ *      Calculate the scores for all subgraphs in the network.
+ * 
+ * -> sortingCycleForDrawing :
+ *      Sorting function for knowing order of cycle drawing. 
+ * 
+ * -> cycleGroupName :
+ *     Create a name for a group of cycle.
+ * 
+ * -> addNewCycleGroup :
+ *      Add a new group of cycle to the subgraph network.
+ * 
+ * -> updateGroupCycles :
+ *      Update the group of cycles based on the remaining cycles to draw.
+ * 
+ * -> getRadiusSize :
+ *     Calculate the radius size of a cycle based on the nodes in the cycle.
+ *  * 
+ * -> assignCoordinateToNode :
+ *   Assigns coordinates to a node in the subgraph network.
+ * 
+ * -> undoIfCreateOverlap :
+ *      Undo the node placement if it creates an overlap with other nodes.
+ * 
+ * -> isOverlapInCycleGroupDrawing :
+ *      Check if there is an overlap in the drawing of the cycle group.
+ * 
+ *  * -> getUnfixedIntervals :
+ *    Get the unfixed intervals in a cycle.
+ * 
+ * *********************************
+ * 
+ * 3.  Drawing cycle : shift position and placement in network
+ * 
+ * -> drawAllCyclesGroup :
+ *     Draw all the cycles in the group of cycles.
+ * 
+ * -> drawCycleGroup :
+ *     Draw a group of cycles.
+ * 
+ */
 
-//------------------------------------------------------------------------------------------------------------
-//__________________________________Calculate coordinates for nodes in cycles______________________________________________________
-//------------------------------------------------------------------------------------------------------------
+
+/*******************************************************************************************************************************************************/
+//___________________________________________________0.  Calculate coordinates for nodes in cycles__________________________________________________________________________
 
 
 
 /**
- * give coordinates for all cycles in the subgraph network.
+ * give precalculated coordinates for all cycles in the subgraphNetwork, there are not in the nodes network.
  * @param subgraphNetwork - The subgraph network containing cycles.
  * @param allowInterncircle - Whether to allow internal circles within cycles. Default is false.
  * @param radiusFactor - The factor to determine the radius of the cycles. Default is 15.
@@ -39,25 +131,26 @@ import { childNodeNotInCycle, getListNodeLinksForCycleGroup, getListNodeLinksFor
 
 export async function coordinateAllCycles(subgraphNetwork:SubgraphNetwork,allowInterncircle:boolean=false):Promise<SubgraphNetwork> {
     const network = subgraphNetwork.network;
-    const cycles = subgraphNetwork.cycles? Object.values(subgraphNetwork.cycles):undefined;
-    let i=0
+    const cycles = subgraphNetwork[TypeSubgraph.CYCLE]? Object.values(subgraphNetwork[TypeSubgraph.CYCLE]):undefined;
+    let i=0;
     let newGroup=true;
     if (cycles && cycles.length > 0) {
         // creation first cycle group
         let group=0;
         let groupName=cycleGroupName(String(group));
         subgraphNetwork=addNewCycleGroup(subgraphNetwork,groupName);
+        if(!subgraphNetwork[TypeSubgraph.CYCLEGROUP] || !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupName]) throw new Error(" cycle group not in subgraph network : error in addNewCycleGroup");
+        const cycleGroups=subgraphNetwork[TypeSubgraph.CYCLEGROUP];
 
         // Find the first cycle to draw : it shouldn't have a 'forgraph' of type cycle as it should be a parent -------------------
         const parentCycles = cycles.filter(cycle => !cycle.parentSubgraph || cycle.parentSubgraph.type !== TypeSubgraph.CYCLE);
-        if (parentCycles.length === 0) {
-            console.error("No cycle found without a forSubgraph of type cycle");
-            return;
-        }
-        parentCycles.sort((a, b) => sortingCycleForDrawing(subgraphNetwork,a,b,true));
+        if (parentCycles.length === 0) throw new Error("No cycle found without a parent subgraph of type cycle : review implementation");
+            
+        const scores = await calculateAllScoresForSubgraphs(subgraphNetwork, parentCycles, true);
+        parentCycles.sort( (a, b) => sortingCycleForDrawing(scores[a.name],scores[b.name],true));
         const largestParentCycle = parentCycles[0]; // get largest cycle
-        subgraphNetwork.cyclesGroup[groupName].nodes.push(largestParentCycle.name); // add it to the current group of cycle
-        coordinateCycle(subgraphNetwork, largestParentCycle.name,groupName); // give coordinate for largest cycle
+        cycleGroups[groupName].nodes.push(largestParentCycle.name); // add it to the current group of cycle
+        await coordinateCycle(subgraphNetwork, largestParentCycle.name,groupName); // give coordinate for largest cycle
 
         // Drawing the others : --------------------------------------------------------------------------------------------
 
@@ -67,6 +160,7 @@ export async function coordinateAllCycles(subgraphNetwork:SubgraphNetwork,allowI
         // If group of connected cycle is drawn : update group cycle
         const updateGroupCycle=await updateGroupCycles(remainingCycles,subgraphNetwork,group,groupName);
         subgraphNetwork=updateGroupCycle.subgraphNetwork;
+        // new cycle group ?
         if(updateGroupCycle.group!==group){
             group=updateGroupCycle.group;
             groupName=cycleGroupName(String(group));
@@ -79,17 +173,18 @@ export async function coordinateAllCycles(subgraphNetwork:SubgraphNetwork,allowI
         while (remainingCycles.length > 0 ) {
 
             // sort cycles by number of fixed node (and then by size)
-            remainingCycles.sort((a, b) => sortingCycleForDrawing(subgraphNetwork,a,b,newGroup));
+            const scores = await calculateAllScoresForSubgraphs(subgraphNetwork, remainingCycles, newGroup);
+            remainingCycles.sort( (a, b) => sortingCycleForDrawing(scores[a.name],scores[b.name],newGroup));
 
-            const cycleToDraw = remainingCycles[0]; // the cycle with the most fixed nodes
+            const cycleToDraw = remainingCycles[0]; // the cycle with the most fixed nodes (or constraints)
             // if groupcycle do not exist : add one
-            if (!(groupName in subgraphNetwork.cyclesGroup)){
+            if (!(groupName in cycleGroups)){
                 subgraphNetwork=addNewCycleGroup(subgraphNetwork,groupName);
             }
             // add the cycle to the current group of cycle
-            subgraphNetwork.cyclesGroup[groupName].nodes.push(cycleToDraw.name); 
+            cycleGroups[groupName].nodes.push(cycleToDraw.name); 
             // give coordinate to cycle node
-            coordinateCycle(subgraphNetwork, cycleToDraw.name,groupName,allowInterncircle); 
+            await coordinateCycle(subgraphNetwork, cycleToDraw.name,groupName,allowInterncircle); 
             // remove cycle from the list of cycle to process
             remainingCycles.shift(); 
 
@@ -109,71 +204,6 @@ export async function coordinateAllCycles(subgraphNetwork:SubgraphNetwork,allowI
     return subgraphNetwork;
 }
 
-/**
- * Returns the cycle group name by appending the given name with a prefix.
- * 
- * @param name - The name to be appended with the prefix.
- * @returns The cycle group name.
- */
-function cycleGroupName(name:string):string{
-    return "cycle_group_"+name;
-}
-/**
- * Adds a new cycle group to the subgraph network.
- * 
- * @param subgraphNetwork - The subgraph network to add the cycle group to.
- * @param groupName - The name of the cycle group.
- * @returns The updated subgraph network with the added cycle group.
- */
-function addNewCycleGroup(subgraphNetwork:SubgraphNetwork, groupName:string):SubgraphNetwork{
-    if(!subgraphNetwork.cyclesGroup){
-        subgraphNetwork.cyclesGroup={};
-    }
-    subgraphNetwork.cyclesGroup[groupName]={name:groupName,nodes:[],metadata:{}};
-    return subgraphNetwork;
-}
-
-/**
- * Sorting function for knowing order of cycle drawing. 
- * First sort by number of circle fixed nodes (nodes fixed in a circle drawing), then by size, by number of parent nodes (of the cycle),  and finally by number of child nodes (of the cycle) .
- * @param subgraphNetwork - The subgraph network.
- * @param a - The first cycle to compare.
- * @param b - The second cycle to compare.
- * @returns A number indicating the sorting order.
- */
-function sortingCycleForDrawing(subgraphNetwork:SubgraphNetwork,a:Subgraph,b:Subgraph,fullConstraint:boolean=false):number{
-    const network=subgraphNetwork.network;
-
-    // first sort by number of fixed nodes
-    const fixedNodesA = a.nodes.filter(node => network.nodes[node].metadata && network.nodes[node].metadata.fixedCycle).length;
-    const fixedNodesB = b.nodes.filter(node => network.nodes[node].metadata && network.nodes[node].metadata.fixedCycle).length;
-    if (fixedNodesA !== fixedNodesB){
-        return fixedNodesB - fixedNodesA;
-    }else{
-        // sort by size
-        if ( !fullConstraint || b.nodes.length !== a.nodes.length ){
-            return b.nodes.length - a.nodes.length;
-        }else{
-            // then by number of parent nodes
-            const totalParentNodesA = parentNodeNotInCycle(subgraphNetwork, a.nodes)
-                .flat().length;
-            const totalParentNodesB = parentNodeNotInCycle(subgraphNetwork, b.nodes)
-                .flat().length;
-            if (totalParentNodesA !== totalParentNodesB){
-                return totalParentNodesB - totalParentNodesA;
-            }else{
-                // then by number of child nodes
-                const totalChildNodesA = childNodeNotInCycle(subgraphNetwork, a.nodes)
-                    .flat().length;
-                const totalChildNodesB = childNodeNotInCycle(subgraphNetwork, b.nodes)
-                    .flat().length;
-               
-                return totalChildNodesB - totalChildNodesA;
-            }
-        }                   
-    }
-}
-
 
 
 /**
@@ -189,103 +219,134 @@ function sortingCycleForDrawing(subgraphNetwork:SubgraphNetwork,a:Subgraph,b:Sub
  */
 async function coordinateCycle(subgraphNetwork:SubgraphNetwork, cycleToDrawID:string,groupCycleName:string,allowInterncircle:boolean=true):Promise<SubgraphNetwork>{
     const network = subgraphNetwork.network;
-    
-    // Get nodes to place
-    let cycle:string[]=[];
-    if (cycleToDrawID in subgraphNetwork.cycles){
-        cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
-        subgraphNetwork.cycles[cycleToDrawID].metadata={};
-    }else{  
-        console.log('cycle not in subgraph network');
-    }
 
+    if(!subgraphNetwork[TypeSubgraph.CYCLE] || !subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID]) throw new Error("cycle not in subgraph network");
+    const nodesCycle=subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID].nodes;
+    if (nodesCycle.length===0) throw new Error("no node in cycle");
 
     // Check existence of all nodes
-    const cycleExist = cycle.every(node => node in network.nodes);
+    const cycleExist = nodesCycle.every(node => node in network.nodes);
+    if (!cycleExist) throw new Error("Some nodes in the cycle do not exist in the network");
 
-    // Nodes with attribute 'fixedCycle' : if a node had been fixed in a line, his position can change, if fixed in a circle , no change
-    const nodesFixedCircle = cycle.filter(node => 
-        network.nodes[node].metadata && network.nodes[node].metadata.fixedCycle
+    // Nodes with attribute 'fixedInCircle' : if a node had been fixed in a line, his position can change, if fixed in a circle , no change
+    const nodesFixedInCircle = nodesCycle.filter(node => 
+        network.nodes[node].metadataLayout && network.nodes[node].metadataLayout.fixedInCircle
     );
-    const nodesFixed = cycle.filter(node => 
-        network.nodes[node].metadata && network.nodes[node].metadata.fixedInCycle
+    const nodesFixed = nodesCycle.filter(node => 
+        network.nodes[node].metadataLayout && network.nodes[node].metadataLayout.isFixed
     );
 
 
     // Update node metadata to place them in cycleGroup
-    cycle.forEach(node=>{
-        network.nodes[node].metadata[TypeSubgraph.CYCLEGROUP]=groupCycleName;
+    nodesCycle.forEach(node=>{
+        updateNodeMetadataSubgraph(network,node,groupCycleName,TypeSubgraph.CYCLEGROUP);
     });
 
-    // If cycle exist: place his nodes
-    if (cycleExist && cycle.length>0){
-        if (nodesFixedCircle.length===0){ // if independant cycle (first of a group cycle)----------------------------------------------------------------------------------
+    // Nodes placement
+    
+    if (nodesFixedInCircle.length===0){ // if independant cycle (first of a group cycle)----------------------------------------------------------------------------------
 
-             subgraphNetwork=await independentCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName);
+        subgraphNetwork=await independentCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName);
 
-        } else if (nodesFixedCircle.length===1){ // if cycle linked to another cycle by one node ----------------------------------------------------------------------------------
-            
-            const tangentNode=network.nodes[nodesFixedCircle[0]];
-            subgraphNetwork=await tangentCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName,tangentNode,nodesFixed,allowInterncircle);
+    } else if (nodesFixedInCircle.length===1){ // if cycle linked to another cycle by one node ----------------------------------------------------------------------------------
+        
+        const tangentNode=network.nodes[nodesFixedInCircle[0]];
+        subgraphNetwork=await tangentCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName,tangentNode,nodesFixed,allowInterncircle);
+        
             
-             
-        } else { // several node in common with other cycle(s) ----------------------------------------------------------------------------------
-
-            subgraphNetwork=lineCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName);
-
-        }
+    } else { // several node in common with other cycle(s) ----------------------------------------------------------------------------------
+        subgraphNetwork= await lineCycleCoordinates(subgraphNetwork,cycleToDrawID,groupCycleName);
     }
 
-
     return subgraphNetwork;
 }
 
+/**
+ * Calculates and assigns coordinates for the nodes in a independant cycle within a subgraphNetwork.
+ * Independant means that the cycle do not countains nodes already placed, i.e with coordinates.
+ * The cycle is centered at the origin (0,0) and its nodes are positioned in a circular layout.
+ * 
+ * @param subgraphNetwork - The subgraph network containing the cycle to be drawn.
+ * @param cycleToDrawID - The identifier of the cycle to be drawn.
+ * @param groupCycleName - The name of the group to which the cycle belongs.
+ * @returns A promise that resolves to the updated subgraph network with the cycle's nodes positioned.
+ */
 async function independentCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,groupCycleName:string):Promise<SubgraphNetwork>{
     const network = subgraphNetwork.network;
-    const cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
+    if (!subgraphNetwork[TypeSubgraph.CYCLE] || !subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID]) throw new Error("cycle not in subgraphNetwork");
+    const cycle=subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID]
+    const cycleNodes=cycle.nodes;
     
     // radius and centroid
-    const radius = await getRadiusSize(cycle,network,subgraphNetwork.networkStyle);
-        // first cycle centered at 0,0
-    const centroidX=0;
-    const centroidY=0;
-    subgraphNetwork.cycles[cycleToDrawID].metadata.radius=radius;   
-    subgraphNetwork.cycles[cycleToDrawID].metadata.centroid={x:centroidX,y:centroidY};         
+    const radius = await getRadiusSize(cycleNodes,network,subgraphNetwork.networkStyle);
+    cycle.radiusCycle=radius;    
+    cycle.centroidCycle={x:0,y:0}; // first cycle (of group cycle) centered at 0,0
     
     // Shift cycle 
-    const topIndex = findTopCycleNode(subgraphNetwork,cycle); // first node of list is the top 
-    const cycleCopy= cycle.slice();
+    const topIndex = await findTopCycleNode(subgraphNetwork,cycleNodes); // first node of list is the top 
+    const cycleCopy= cycleNodes.slice();
     const shiftedCycle = cycleCopy.splice(topIndex).concat(cycleCopy);
 
     // Give position to each node (no nedd to check if overlap with other cycle, it is the first of the group)
-    subgraphNetwork=cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,-Math.PI/2,groupCycleName).subgraphNetwork;
+    const coordinates=await cycleNodesCoordinates(cycleToDrawID,shiftedCycle, cycle.centroidCycle.x, cycle.centroidCycle.y, cycle.radiusCycle,subgraphNetwork,-Math.PI/2,groupCycleName)
+    subgraphNetwork=coordinates.subgraphNetwork;
     return subgraphNetwork;
 }    
 
-async function tangentCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,groupCycleName:string,nodeFixed:Node,nodesPlaced:string[],allowInterncircle:boolean=false):Promise<SubgraphNetwork>{
+
+
+/**
+ * Calculate position of a cycle as circle, it will be tangent to the circle of the fixed node.
+ * @param subgraphNetwork  - The subgraph network containing the cycle to be drawn.
+ * @param cycleToDrawID  - The identifier of the cycle to be drawn.
+ * @param groupCycleName - The name of the group to which the cycle belongs.
+ * @param nodeFixed - The node that is fixed in a circle, the common node with the other cycle.
+ * @param nodesPlaced - The nodes that are already placed in the cycle.
+ * @param allowInterncircle - A flag indicating whether to allow internal circles for fixed nodes (default: false).
+ * @returns A promise that resolves to the updated subgraph network with the cycle's nodes positioned.
+ */
+async function tangentCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,groupCycleName:string,nodeFixed:NodeLayout,nodesPlaced:string[],allowInterncircle:boolean=false):Promise<SubgraphNetwork>{
     const network = subgraphNetwork.network;
-    const cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
+
+    // if cycle to draw not in object or fixed nodes lack information : throw error
+    if (!subgraphNetwork[TypeSubgraph.CYCLE] || !subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID]) throw new Error("cycle not in subgraphNetwork");
+    const cycleNodes=subgraphNetwork.cycles[cycleToDrawID].nodes;
+    if (!nodeFixed.metadataLayout || !nodeFixed.metadataLayout.fixedInCircle) throw new Error("fixed node for tangent drawing not in a circle");
+
 
     // get fixed node coordinates (in the group cycle)
-    const groupCycleFixed=nodeFixed.metadata[TypeSubgraph.CYCLEGROUP] as string;
-    const coordNodeFixed=subgraphNetwork.cyclesGroup[groupCycleFixed].metadata[nodeFixed.id] as {x:number,y:number};
+    if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP] || !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName] ||
+         !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName].precalculatedNodesPosition || 
+         !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName].precalculatedNodesPosition[nodeFixed.id]) throw new Error("fixed node not in group cycle, or no precalculated position");
+    const getCoordNodeFixed=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName].precalculatedNodesPosition[nodeFixed.id];
+    
+    if (getCoordNodeFixed.x==null || getCoordNodeFixed.y==null) throw new Error("fixed node position not defined");
+    const coordNodeFixed={x:getCoordNodeFixed.x,y:getCoordNodeFixed.y};
 
     // first node of cycle is the one fixed :
-    const cycleCopy= cycle.slice();
-    const firstIndex=cycle.indexOf(nodeFixed.id);
+    const cycleCopy= cycleNodes.slice();
+    const firstIndex=cycleNodes.indexOf(nodeFixed.id);
     const shiftedCycle = cycleCopy.splice(firstIndex).concat(cycleCopy);
 
     // radius
-    const radius = await getRadiusSize(cycle,network,subgraphNetwork.networkStyle);
-    subgraphNetwork.cycles[cycleToDrawID].metadata.radius=radius;
+    const radius = await getRadiusSize(cycleNodes,network,subgraphNetwork.networkStyle);
+    subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID].radiusCycle=radius;
 
     //centroid depending on fixed cycle
-    const radiusFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.radius as number;
-    const centroidFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.centroid;
-    const fixedAngle = Math.atan2(coordNodeFixed.y - centroidFixedCycle["y"], coordNodeFixed.x - centroidFixedCycle["x"]);
+    const circleAlreadyDrawn=nodeFixed.metadataLayout.fixedInCircle;
+    if (!subgraphNetwork[TypeSubgraph.CYCLE] || !subgraphNetwork[TypeSubgraph.CYCLE][circleAlreadyDrawn]) throw new Error("fixed node for tangent drawing not in a circle");
+
+    const radiusFixedCycle=subgraphNetwork[TypeSubgraph.CYCLE][circleAlreadyDrawn].radiusCycle;
+    if (!radiusFixedCycle) throw new Error("no radius for circle of fixed node");
+
+    const centroidFixedCycle=subgraphNetwork[TypeSubgraph.CYCLE][circleAlreadyDrawn].centroidCycle;
+    if (!centroidFixedCycle) throw new Error("no centroid for circle of fixed node");
+
+    const fixedAngle = Math.atan2(coordNodeFixed.y - centroidFixedCycle.y, coordNodeFixed.x - centroidFixedCycle["x"]);
     let centroidX:number;
     let centroidY:number;
     let d:number;
+
     // if has some node fixed in a line :
     if(allowInterncircle && nodesPlaced.length >1){
         d = radiusFixedCycle - radius; // circle draw internally
@@ -293,61 +354,76 @@ async function tangentCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDr
     }else{ // completely indep of fixed nodes but for the common node
         d = radius + radiusFixedCycle; // circle draw externally
     }
-    centroidX = centroidFixedCycle["x"] + d * Math.cos(fixedAngle);
-    centroidY = centroidFixedCycle["y"] + d * Math.sin(fixedAngle);
-    subgraphNetwork.cycles[cycleToDrawID].metadata.centroid={x:centroidX,y:centroidY};
+    centroidX = centroidFixedCycle.x + d * Math.cos(fixedAngle);
+    centroidY = centroidFixedCycle.y + d * Math.sin(fixedAngle);
+    centroidX=Number(centroidX.toFixed(2));
+    centroidY=Number(centroidY.toFixed(2));
+    subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID].centroidCycle={x:centroidX,y:centroidY};
     
 
     // shift of start angle (default:pi/2) : angle of fixed node in the new cycle (with centroid calculted before)
-    const positionFixedNode=subgraphNetwork.cyclesGroup[groupCycleName].metadata[nodeFixed.id] as {x:number,y:number};
-    const shiftAngle = Math.atan2(positionFixedNode.y - centroidY, positionFixedNode.x - centroidX);
+    const shiftAngle = Math.atan2(coordNodeFixed.y - centroidY, coordNodeFixed.x - centroidX);
     
     // Give position to each node, and get position before drawing cycle
-    const cycleCoord=cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,shiftAngle,groupCycleName);
+    const cycleCoord=await cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,shiftAngle,groupCycleName);
     subgraphNetwork=cycleCoord.subgraphNetwork;
     const positionBefore=cycleCoord.positionBefore;
-    subgraphNetwork=undoIfOverlap(subgraphNetwork,groupCycleName,positionBefore);
+    subgraphNetwork=undoIfCreateOverlap(subgraphNetwork,groupCycleName,positionBefore);
 
     return subgraphNetwork;
 }
 
-function lineCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,groupCycleName:string){
-    const cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
+/**
+ * Calculates the coordinates for drawing a cycle as line(s). A line for each unfixed interval in the cycle.
+ * An unfixed interval is a sequence of nodes in the cycle that are not already fixed in a circle.
+ * 
+ * @param subgraphNetwork - The subgraph network containing the cycles and cycle groups.
+ * @param cycleToDrawID - The ID of the cycle to draw.
+ * @param groupCycleName - The name of the cycle group.
+ * @returns The updated subgraph network with the calculated coordinates.
+ * @throws Will throw an error if the cycle or cycle group is not found in the subgraph network.
+ * @throws Will throw an error if there are no precalculated node positions in the cycle group.
+ * @throws Will throw an error if the start or end node positions are not defined.
+ */
+async function lineCycleCoordinates(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,groupCycleName:string):Promise<SubgraphNetwork>{
+    if (!subgraphNetwork[TypeSubgraph.CYCLE] || !subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID]) throw new Error("cycle not in subgraphNetwork");
+    if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP] || !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName]) throw new Error("group cycle not in subgraphNetwork");
+    const cycleGroup=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName];
+    if (!cycleGroup.precalculatedNodesPosition) throw new Error("no precalculatedNodesPosition in group cycle");
+    const precalculatedNodesPosition=cycleGroup.precalculatedNodesPosition;
+
+    // get cycle and the intervals to draw : the one not already fixed in a circle
+    const cycleNodes=subgraphNetwork[TypeSubgraph.CYCLE][cycleToDrawID].nodes;
+    const unfixedInterval= await getUnfixedIntervals(cycleNodes,subgraphNetwork);
 
-    const unfixedInterval=getUnfixedIntervals(cycle,subgraphNetwork);
     let cycleAsLine:string[]=[];
-    unfixedInterval.forEach(interval=>{
-        const startNode=cycle[(interval[0]-1+ cycle.length) % cycle.length];
-        const endNode=cycle[(interval[1]+1+ cycle.length) % cycle.length];
-        const startNodePosition=subgraphNetwork.cyclesGroup[groupCycleName].metadata[startNode] as {x:number,y:number};
-        const endNodePosition=subgraphNetwork.cyclesGroup[groupCycleName].metadata[endNode] as {x:number,y:number};
+
+    for (const interval of unfixedInterval){
+        const startNode=cycleNodes[(interval[0]-1+ cycleNodes.length) % cycleNodes.length];
+        const endNode=cycleNodes[(interval[1]+1+ cycleNodes.length) % cycleNodes.length];
+
+        if (!precalculatedNodesPosition[startNode] || !precalculatedNodesPosition[endNode]) throw new Error("no precalculatedNodesPosition for start and end node of line");
+        const startNodePosition=precalculatedNodesPosition[startNode];
+        const endNodePosition=precalculatedNodesPosition[endNode];
+
+        if (startNodePosition.x==null || startNodePosition.y==null || endNodePosition.x==null || endNodePosition.y==null) throw new Error("start or end node position not defined");
+
         if (interval[0]>interval[1]){
             // if begginning of the cycle at the end, and the end at the beginning
-            cycleAsLine=cycle.slice(interval[0],cycle.length).concat(cycle.slice(0,interval[1]+1));
+            cycleAsLine=cycleNodes.slice(interval[0],cycleNodes.length).concat(cycleNodes.slice(0,interval[1]+1));
         }else{
-            cycleAsLine=cycle.slice(interval[0],interval[1]+1);
+            cycleAsLine=cycleNodes.slice(interval[0],interval[1]+1);
         }
+
         // Give position to each node
-        const lineCoord=lineNodesCoordinates(startNodePosition,endNodePosition,cycleAsLine,subgraphNetwork,groupCycleName);
+        const lineCoord=await lineNodesCoordinates(startNodePosition as Coordinate,endNodePosition as Coordinate,cycleAsLine,subgraphNetwork,groupCycleName);
         subgraphNetwork=lineCoord.subgraphNetwork;
         const positionBefore=lineCoord.positionBefore;
-        subgraphNetwork=undoIfOverlap(subgraphNetwork,groupCycleName,positionBefore);
-    });
+        subgraphNetwork=undoIfCreateOverlap(subgraphNetwork,groupCycleName,positionBefore);
+    };
     return subgraphNetwork;
 }
 
-/**
- * Calculates the radius size based on the length of the cycle array and the radius factor.
- * @param cycle - The array of strings representing the cycle.
- * @param radiusFactor - The factor to multiply the length of the cycle by to calculate the radius size. Default value is 15.
- * @returns The calculated radius size.
- */
-async function getRadiusSize(cycle:string[],network:Network,styleNetwork:GraphStyleProperties):Promise<number>{
-    const nodes=Object.values(network.nodes).filter(node=>cycle.includes(node.id));
-    const meanSize=await getMeanNodesSizePixel(nodes,styleNetwork);
-    return cycle.length*(meanSize.height+meanSize.width)/2;
-}
-
 
 /**
  * Calculates and assigns the coordinates for nodes to draw it as a circle.
@@ -359,57 +435,36 @@ async function getRadiusSize(cycle:string[],network:Network,styleNetwork:GraphSt
  * @param radius - The radius of the cycle.
  * @param subgraphNetwork - The subgraph network.
  * @param shiftAngle - The angle by which to shift the cycle from the first node (optional, default is -Math.PI/2 to put the first node at the top).
- * @param groupcycle - The name of the group cycle (optional).
- * @returns The updated subgraph network.
+ * @param groupCycleName - The name of the group cycle (optional).
+ * @returns The updated subgraph network and the position of nodes before the change.
  */
-function cycleNodesCoordinates(cycleName:string,cycle:string[],centroidX:number,centroidY:number,radius:number,subgraphNetwork:SubgraphNetwork,
-    shiftAngle:number=-Math.PI/2,groupcycle?:string,):{subgraphNetwork:SubgraphNetwork,positionBefore:{[key:string]:{x:number,y:number}}}{
+async function cycleNodesCoordinates(cycleName:string,cycle:string[],centroidX:number,centroidY:number,radius:number,subgraphNetwork:SubgraphNetwork,
+    shiftAngle:number=-Math.PI/2,groupCycleName?:string,):Promise<{subgraphNetwork:SubgraphNetwork,positionBefore:{[key:string]:CoordinateNull}}>{
 
     const network=subgraphNetwork.network;
-    let positionBefore:{[key:string]:{x:number,y:number}}={};
+    let positionBefore:{[key:string]:CoordinateNull}={};
 
     cycle.forEach((node, i) => {
         const nodeNetwork=network.nodes[node];
+        // coordinates of the node
         // positive shift angle rotate cycle to the right, negative to the left
-        const x = centroidX + radius * Math.cos(2 * Math.PI * i / cycle.length + shiftAngle );
-        const y = centroidY + radius * Math.sin(2 * Math.PI * i / cycle.length  + shiftAngle );
-        
-        // Give position if not fixed
-        if(network.nodes[node].metadata && !network.nodes[node].metadata.fixedCycle){
-            if (groupcycle){
-                if (groupcycle in subgraphNetwork.cyclesGroup){
-                    if (!subgraphNetwork.cyclesGroup[groupcycle].metadata[node]){
-                        subgraphNetwork.cyclesGroup[groupcycle].metadata[node] = {};
-                        // get position before drawing cycle
-                        positionBefore[node]={x:null,y:null};
-                    }else{
-                        // get position before drawing cycle
-                        const xBefore=subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["x"];
-                        const yBefore=subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["y"];
-                        positionBefore[node]={x:xBefore,y:yBefore};
-                    }
-                    // assign new position
-                    subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["x"]=x;
-                    subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["y"]=y;
-                } else {
-                    console.error("CycleGroup not in subgraphNetwork");
-                }
-            } else if (node in subgraphNetwork.network.value.nodes) {
-                // get position before drawing cycle
-                const xBefore=subgraphNetwork.network.nodes[node].x;
-                const yBefore=subgraphNetwork.network.nodes[node].x;
-                positionBefore[node]={x:xBefore,y:yBefore};
-                // assign new position
-                subgraphNetwork.network.nodes[node].x=x;
-                subgraphNetwork.network.nodes[node].y=y;
-            } else{
-                console.error("Node not in network or groupcycle not provided")
-            }
+        let x = centroidX + radius * Math.cos(2 * Math.PI * i / cycle.length + shiftAngle );
+        let y = centroidY + radius * Math.sin(2 * Math.PI * i / cycle.length  + shiftAngle );
+        x=Number(x.toFixed(2));
+        y=Number(y.toFixed(2));
+
+        // Give position if not fixed in circle
+        if(!nodeNetwork.metadataLayout || !nodeNetwork.metadataLayout.fixedInCircle){
+
+            // assign coordinates to the node, and get the position before
+            const assignedCoordinates= assignCoordinateToNode(subgraphNetwork,node,x,y,groupCycleName);
+            subgraphNetwork=assignedCoordinates.subgraphNetwork;
+            positionBefore[node]=assignedCoordinates.positionBefore;
 
             // Fix the nodes 
-            if (!nodeNetwork.metadata) nodeNetwork.metadata={};
-            nodeNetwork.metadata.fixedInCycle= true;
-            nodeNetwork.metadata.fixedCycle= cycleName;
+            if (!nodeNetwork.metadataLayout) nodeNetwork.metadataLayout={};
+            nodeNetwork.metadataLayout.isFixed= true;
+            nodeNetwork.metadataLayout.fixedInCircle= cycleName;
         }
         
     });
@@ -418,85 +473,6 @@ function cycleNodesCoordinates(cycleName:string,cycle:string[],centroidX:number,
 }
 
 
-
-function undoIfOverlap(subgraphNetwork:SubgraphNetwork,groupCycleName:string,positionBefore:{[key:string]:{x:number,y:number}}):SubgraphNetwork{
-    // is there overlap in the group cycle ?
-    const isOverlap=isOverlapCycles(subgraphNetwork,groupCycleName);
-    // if overlap, put back the position before drawing cycle
-    if(isOverlap){
-        Object.entries(positionBefore).forEach(([nodeID,coord])=>{
-            const node=subgraphNetwork.cyclesGroup[groupCycleName].metadata[nodeID] as {x:number,y:number};
-            node.x = positionBefore[nodeID].x;
-            node.y = positionBefore[nodeID].y;
-        });
-    }
-    return subgraphNetwork;
-}
-
-function isOverlapCycles(subgraphNetwork:SubgraphNetwork,groupCycleName:string):boolean{
-    const graph =getListNodeLinksForCycleGroupAsObject(subgraphNetwork,groupCycleName);
-    // intersection of edges :
-    const intersectionEdges=isIntersectionGraph(graph.nodes ,graph.links,subgraphNetwork.network,subgraphNetwork.networkStyle.value);
-    if (intersectionEdges){
-        return true;
-    }else{
-        // overlap of nodes
-        const nodesOverlap=isOverlapNodes(graph.nodes,subgraphNetwork.network,subgraphNetwork.networkStyle.value);
-        if (nodesOverlap){
-            return true;
-        }else{
-            // overlap of node with edges
-            const edgeNodeOverlap=isOverlapNodesEdges(graph.nodes,graph.links,subgraphNetwork.network.value,subgraphNetwork.networkStyle.value);
-            if (edgeNodeOverlap){
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-
-/**
- * Retrieves the unfixed intervals from a list of nodes. An unfixed interval is a continuous range of nodes that are not fixed in a cycle.
- * An unfixed interval is a continuous range of nodes that are not fixed in the cycle.
- *
- * @param nodes - An array of node IDs.
- * @param subgraphNetwork - The subgraph network containing the nodes.
- * @returns An array of intervals representing the unfixed intervals.
- */
-function getUnfixedIntervals(nodes:string[],subgraphNetwork:SubgraphNetwork) {
-    let intervals:number[][] = [];
-    let start = null;
-    const network=subgraphNetwork.network;
-    nodes.forEach((nodeID,i) => {
-        const node=network.nodes[nodeID];
-        if (node.metadata && !node.metadata.fixedInCycle) {
-            if (start === null) {
-                start = i;
-            }
-        } else {
-            if (start !== null) {
-                intervals.push([start, i - 1]);
-                start = null;
-            }
-        }
-    });
-
-    // Handle case where the last node is fixed
-    if (start !== null) {
-        intervals.push([start, nodes.length - 1]);
-    }
-
-    // case first interval and last are linked : combine interval as one long interval
-    if (intervals.length!==0 && intervals[0][0]===0 && intervals[intervals.length-1][1]===nodes.length-1){
-        intervals[0][0]=intervals[intervals.length-1][0]; // change start of first interval
-        intervals.pop(); // remove last interval
-    }
-
-    return intervals;
-}
-
-
 /**
  * Calculates and assigns the coordinates for nodes along a line between two points.
  * @param start - The starting point coordinates (x, y).
@@ -504,12 +480,12 @@ function getUnfixedIntervals(nodes:string[],subgraphNetwork:SubgraphNetwork) {
  * @param nodes - An array of node names to place.
  * @param subgraphNetwork - The subgraph network object.
  * @param groupCycleName - Optional. The name of the group cycle in witch the line is draw.
- * @returns The updated subgraph network object.
+ * @returns The updated subgraph network object, and position of nodes before the change
  */
-function lineNodesCoordinates(start: {x: number, y: number}, end: {x: number, y: number}, nodes: string[],
-    subgraphNetwork:SubgraphNetwork,groupCycleName?:string):{subgraphNetwork:SubgraphNetwork,positionBefore:{[key:string]:{x:number,y:number}}} {
+async function lineNodesCoordinates(start: {x: number, y: number}, end: {x: number, y: number}, nodes: string[],
+    subgraphNetwork:SubgraphNetwork,groupCycleName?:string):Promise<{subgraphNetwork:SubgraphNetwork,positionBefore:{[key:string]:CoordinateNull}}> {
     const network=subgraphNetwork.network;
-    let positionBefore:{[key:string]:{x:number,y:number}}={};
+    let positionBefore:{[key:string]:CoordinateNull}={};
 
     // Calculate direction vector
     let dx = end.x - start.x;
@@ -527,104 +503,47 @@ function lineNodesCoordinates(start: {x: number, y: number}, end: {x: number, y:
 
     // Place nodes
     nodes.forEach((node, i) => {
+
+        // coordinates of the node
         let d = nodeDistance * (i + 1);
-        const x= start.x + ux * d;
-        const y = start.y + uy * d;
+        let x= start.x + ux * d;
+        let y = start.y + uy * d;
+        x=Number(x.toFixed(2));
+        y=Number(y.toFixed(2));
         
-        // Give position 
-        if (groupCycleName){
-            if (groupCycleName in subgraphNetwork.cyclesGroup){
-                if (!subgraphNetwork.cyclesGroup[groupCycleName].metadata[node]){
-                    subgraphNetwork.cyclesGroup[groupCycleName].metadata[node] = {};
-                    // get position before drawing cycle
-                    positionBefore[node]={x:null,y:null};
-                }else{
-                    // get position before drawing cycle
-                    const xBefore=subgraphNetwork.cyclesGroup[groupCycleName].metadata[node]["x"];
-                    const yBefore=subgraphNetwork.cyclesGroup[groupCycleName].metadata[node]["y"];
-                    positionBefore[node]={x:xBefore,y:yBefore};
-                }
-                // assign new position
-                subgraphNetwork.cyclesGroup[groupCycleName].metadata[node]["x"]=x;
-                subgraphNetwork.cyclesGroup[groupCycleName].metadata[node]["y"]=y;
-            } else {
-                console.error("CycleGroup not in subgraphNetwork");
-            }
-        } else if (node in subgraphNetwork.network.nodes) {
-            // get position before drawing cycle
-            const xBefore=subgraphNetwork.network.nodes[node].x;
-            const yBefore=subgraphNetwork.network.nodes[node].x;
-            positionBefore[node]={x:xBefore,y:yBefore};
-            // assign new position
-            subgraphNetwork.network.nodes[node].x=x;
-            subgraphNetwork.network.nodes[node].y=y;
-        } else{
-            console.error("Node not in network or groupcycle not provided")
-        }
+        // assign coordinates to the node, and get the position before
+        const assignedCoordinates= assignCoordinateToNode(subgraphNetwork,node,x,y,groupCycleName);
+        subgraphNetwork=assignedCoordinates.subgraphNetwork;
+        positionBefore[node]=assignedCoordinates.positionBefore;
 
         // Fix the nodes 
         const nodeNetwork=network.nodes[node];
-        if (!nodeNetwork.metadata) nodeNetwork.metadata={};
-        nodeNetwork.metadata.fixedInCycle= true;
-        nodeNetwork.metadata.fixedCycle= undefined;
+        if (!nodeNetwork.metadataLayout) nodeNetwork.metadataLayout={};
+        nodeNetwork.metadataLayout.isFixed= true;
+        nodeNetwork.metadataLayout.fixedInCircle= undefined;
         
     });    
     return {subgraphNetwork:subgraphNetwork,positionBefore:positionBefore};
 }
 
-
 /**
- * If the group cycle is drawn :
- * - Updates the group cycles in the subgraph network : add its size and center of rectangle.
- * - Change the group number.
- * 
- * @param remainingCycles - The remaining cycles to process in the subgraph.
- * @param subgraphNetwork - The subgraph network.
- * @param group - The current cycle group number.
- * @param groupCycleName - The name of the group cycle.
- * @returns An object containing the updated subgraph network and group number.
+ * Place node already placed in a cycle group with a force layout
+ * @param subgraphNetwork - The subgraph network object.
+ * @param groupCycleName - The name of the group cycle where node need to be placed
+ * @param force - The force to apply to the nodes. Default is -500.
+ * @returns A promise that resolves to the updated subgraph network with the nodes placed in the group cycle.
  */
-async function updateGroupCycles(remainingCycles: Subgraph[], subgraphNetwork: SubgraphNetwork, group: number, groupCycleName: string): Promise<{subgraphNetwork: SubgraphNetwork, group: number}> {
-    const network = subgraphNetwork.network;
-    const groupCycleIsDraw = isRemainingCycleIndepOfDrawing(remainingCycles, subgraphNetwork);
-
-    if (groupCycleIsDraw && subgraphNetwork.cyclesGroup[groupCycleName].metadata) {
-
-        
-        // force algo for node that have null position
-        subgraphNetwork = await forceGroupCycle(subgraphNetwork, groupCycleName);
-
-        // get size of group and update cycle group information
-        const listCoord = Object.values(subgraphNetwork.cyclesGroup[groupCycleName].metadata)
-                            .filter(item => item["x"] !== undefined && item["y"] !== undefined);
-        const {width, height, center} = rectangleSize(listCoord as {x: number, y: number}[]);
-        subgraphNetwork.cyclesGroup[groupCycleName].width = width;
-        subgraphNetwork.cyclesGroup[groupCycleName].height = height;
-        subgraphNetwork.cyclesGroup[groupCycleName].originalPosition = center;
-
-        // change group
-        group += 1;
-    } else if (groupCycleIsDraw) {
-        console.error('No coordinates for group cycle');
-        // change group
-        group += 1;
-    }
-
-    return {subgraphNetwork: subgraphNetwork, group: group};
-}
-
-
 async function forceGroupCycle(subgraphNetwork:SubgraphNetwork, groupCycleName:string,force:number=-500):Promise<SubgraphNetwork>{
-
-    if (!subgraphNetwork.cyclesGroup[groupCycleName] || !subgraphNetwork.cyclesGroup[groupCycleName].metadata){
-        return subgraphNetwork;
-    }
+    if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP] || !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName]) throw new Error("group cycle not in subgraphNetwork");
+    const groupCycle=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName];
+    if (!groupCycle.precalculatedNodesPosition) groupCycle.precalculatedNodesPosition={};
+    const precalculatedNodesPosition=groupCycle.precalculatedNodesPosition;
 
     // get subgraph for groupCycle
-    const graph =getListNodeLinksForCycleGroup(subgraphNetwork,groupCycleName,true);
-
+    const graph =getListNodeLinksForCycleGroupAsArray(subgraphNetwork,groupCycleName,true);
     // need to apply force ?
-    const nullNode= graph.nodes.filter(node=>node["fx"]==null || node["fy"]==null);
+    if (!graph.nodes || graph.nodes.length===0) return subgraphNetwork;
+    const nullNode= graph.nodes.filter(node=>node.fx==undefined || node.fy==undefined); // node not fixed (?)
     if (nullNode.length==0){
         return subgraphNetwork;
     }
@@ -637,9 +556,6 @@ async function forceGroupCycle(subgraphNetwork:SubgraphNetwork, groupCycleName:s
     const simulation = d3.forceSimulation(graph.nodes)
     .force('link', d3.forceLink().id((d: any) => {return d.id;}).links(graph.links).distance(distanceLinks).strength(0.3))
     .force('charge', d3.forceManyBody().strength(strengthManyBody))
-    //.force("collide", d3.forceCollide(distanceLinks/2))
-    //.force('center', d3.forceCenter(0,0))
-    //.force("radial", d3.forceRadial(0,0))
     .alphaMin(0.4)
     .stop();
     
@@ -653,98 +569,84 @@ async function forceGroupCycle(subgraphNetwork:SubgraphNetwork, groupCycleName:s
 
     // get the new position of the nodes
     graph.nodes.forEach(node => {
-        subgraphNetwork.cyclesGroup[groupCycleName].metadata[node.id] = { x: node["x"], y: node["y"] };
+        if(node.x!==undefined && isFinite(node.x) && node.y!==undefined && isFinite(node.y)){
+            // update x,y if not fixed
+            precalculatedNodesPosition[node.id] = { x: node.x, y: node.y };
+        }else if (node.fx==undefined && node.fy==undefined) {
+            // if no x,y and not fixed : problem
+            throw new Error("error in node coordinates after force layout");
+        }
     });
 
     return subgraphNetwork;
 }
 
-/**
- * Checks if all nodes in the remaining cycles are unfixed in the subgraph network.
- * 
- * @param remainingCycles - An array of remaining cycles in the subgraph.
- * @param subgraphNetwork - The subgraph network containing the nodes.
- * @returns A boolean indicating whether all nodes in the remaining cycles are unfixed.
- */
-function isRemainingCycleIndepOfDrawing(remainingCycles:Subgraph[], subgraphNetwork:SubgraphNetwork):boolean{
-
-    const network = subgraphNetwork.network;
-
-    if (remainingCycles.every(cycle => 
-        cycle.nodes.every(node => 
-            !network.nodes[node].metadata || !network.nodes[node].metadata.fixedInCycle
-        )
-    )) {
-        //  All nodes in all remaining cycles are unfixed
-        return true;
-    } else {
-        //  At least one node in the remaining cycles fixed
-       return false;
-    }
-}
-
-
 
 
-
-
-
-
-
-
-//------------------------------------------------------------------------------------------------------------
-//__________________________________ Utility functions______________________________________________________
-//------------------------------------------------------------------------------------------------------------
+/*******************************************************************************************************************************************************/
+//___________________________________________________1. Find top node for cycle coordinate ________________________________________________________________
 
 
 
 /**
  * Finds the index of the top cycle node based on the given subgraph network and cycle nodes.
  * The top cycle node is determined by the node with the highest parent (smaller y) if it exists. Or the opposite node in the cycle of the node with the lowest child (bigger y).
- * If multiple nodes have the highest parent or lowest child, the one with the median x value is chosen.
+ * If multiple nodes have the highest parent or lowest child, the one with the median x value is chosen (or lowest of the two median if odd number).
  * If no parent or child nodes are found, the first node in the cycle is chosen.
  * 
  * @param subgraphNetwork - The subgraph network object.
  * @param cycleNodes - An array of cycle node names.
  * @returns The index of the top cycle node.
  */
-function findTopCycleNode(subgraphNetwork: SubgraphNetwork, cycleNodes:string[]):number{
+async function findTopCycleNode(subgraphNetwork: SubgraphNetwork, cycleNodes:string[]):Promise<number>{
 
     // find node with the highest parent (smaller y)
         // get parent nodes of the cycle
-    const cycleNodesParent = parentNodeNotInCycle(subgraphNetwork, cycleNodes);
+    const cycleNodesParent =  await parentNodeNotInCycle(subgraphNetwork, cycleNodes);
         // get the one with highest parent
-    const withHighestParent=getNodesAssociatedMinY(subgraphNetwork, cycleNodesParent)
-                            .map(i=>cycleNodes[i]);
+    const indexAssociatedMinY= await getIndexNodesAssociatedMinY(subgraphNetwork, cycleNodesParent)
+    const withHighestParent= indexAssociatedMinY.map(i=>cycleNodes[i]);
     if (withHighestParent.length===1){
-        return cycleNodes.indexOf(withHighestParent[0]);
+        const indexNode=cycleNodes.indexOf(withHighestParent[0]);
+        if(indexNode>=0){
+            return indexNode ;
+        }else{
+            throw new Error("indexOf return -1 : parentNodeNotInCycle or getNodesAssociatedMinY not working or used appropriately");
+        }
 
     } else if (withHighestParent.length>1){
         // if several : take the one with median x
-        const nodeMedianName=nodeMedianX(subgraphNetwork, withHighestParent);
-        return cycleNodes.indexOf(nodeMedianName);
+        const nodeMedianName=await nodeMedianX(subgraphNetwork, withHighestParent);
+        const indexNode=cycleNodes.indexOf(nodeMedianName);
+        if(indexNode>=0){
+            return indexNode ;
+        }else{
+            throw new Error("indexOf return -1 : parentNodeNotInCycle, nodeMedianX or getNodesAssociatedMinY not working or used appropriately");
+        }
 
     }else{
         // if no parent : opposite node of the node with lowest child (to put the one with with to the bottom)
         let bottomNode:number;
         // find node with the lowest child (bigger y)
             // get child nodes of the cycle
-        const cycleNodesChild = childNodeNotInCycle(subgraphNetwork, cycleNodes);
-        
+        const cycleNodesChild =  await childNodeNotInCycle(subgraphNetwork, cycleNodes);
             // get the one with lowest child
-        const withLowestChild=getNodesAssociatedMaxY(subgraphNetwork, cycleNodesChild)
-                             .map(i=>cycleNodes[i]); 
-
-         if (withLowestChild.length>=1){
+        const indexAssociatedMaxY=await getIndexNodesAssociatedMaxY(subgraphNetwork, cycleNodesChild)
+        const withLowestChild = indexAssociatedMaxY.map(i=>cycleNodes[i]); 
+        if (withLowestChild.length>=1){
             if(withLowestChild.length==1){
                 bottomNode=cycleNodes.indexOf(withLowestChild[0]);
-            }else if (withLowestChild.length>1){
+            }else{
                 // if several : take the one with median x
-                const nodeMedianName=nodeMedianX(subgraphNetwork, withLowestChild);
+                const nodeMedianName=await nodeMedianX(subgraphNetwork, withLowestChild);
                 bottomNode=cycleNodes.indexOf(nodeMedianName);
             }
+            if (bottomNode>=0){
             // get the opposite node of the first (one as to be chosen) node in the 
-            return (bottomNode+Math.floor(cycleNodes.length/2))%cycleNodes.length;
+                return (bottomNode+Math.floor(cycleNodes.length/2))%cycleNodes.length;
+            }else{
+                throw new Error("indexOf return -1 : childNodeNotInCycle, nodeMedianX or getNodesAssociatedMaxY not working or used appropriately");
+            }
 
         }else{
             return 0;
@@ -753,27 +655,28 @@ function findTopCycleNode(subgraphNetwork: SubgraphNetwork, cycleNodes:string[])
 }
 
 
-
 /**
  * Returns indices of lists containing minimum y-coordinate nodes.
  * 
  * @param subgraphNetwork - The subgraph network object.
  * @param associatedListNodes - The list of list of nodes.
- * @returns An array of indices representing lists containing minimum y-coordinate nodes.
+ * @returns An array of indices representing nodes containing minimum y-coordinate nodes.
  */
-function getNodesAssociatedMinY(subgraphNetwork: SubgraphNetwork, associatedListNodes: string[][]): number[] {
+async function getIndexNodesAssociatedMinY(subgraphNetwork: SubgraphNetwork, associatedListNodes: string[][]): Promise<number[]> {
     const network=subgraphNetwork.network;
     let minY=Infinity;
     let minNodes: number[] = [];
     associatedListNodes.forEach((listNodes,i) => {
         listNodes.forEach(node => {
-            if (network.nodes[node] && network.nodes[node].y!==undefined && network.nodes[node].y!==null) {
+            if (network.nodes[node] && isFinite(network.nodes[node].y)) {
                 if (network.nodes[node].y < minY) {
                     minY = network.nodes[node].y;
                     minNodes = [i];
                 }else if (network.nodes[node].y == minY && !minNodes.includes(i)) {
                     minNodes.push(i);
                 }
+            }else{
+                throw new Error("no node in network or node without y-coordinate");
             }
         });
     });
@@ -785,21 +688,23 @@ function getNodesAssociatedMinY(subgraphNetwork: SubgraphNetwork, associatedList
  * 
  * @param subgraphNetwork - The subgraph network object.
  * @param associatedListNodes - The list of list of nodes.
- * @returns An array of indices representing lists containing maximum y-coordinate nodes.
+ * @returns An array of indices representing nodes containing maximum y-coordinate nodes.
  */
-function getNodesAssociatedMaxY(subgraphNetwork: SubgraphNetwork, associatedListNodes: string[][]): number[] {
+async function getIndexNodesAssociatedMaxY(subgraphNetwork: SubgraphNetwork, associatedListNodes: string[][]): Promise<number[]> {
     const network=subgraphNetwork.network;
     let maxY=-Infinity;
     let maxNodes:number[]=[];
     associatedListNodes.forEach((listNodes,i) => {
         listNodes.forEach(node => {
-            if (network.nodes[node] && network.nodes[node].y!==undefined && network.nodes[node].y!==null) {
+            if (network.nodes[node] && isFinite(network.nodes[node].y)) {
                 if (network.nodes[node].y > maxY) {
                     maxY = network.nodes[node].y;
                     maxNodes = [i];
                 }else if (network.nodes[node].y == maxY && !maxNodes.includes(i)) { 
                     maxNodes.push(i);
                 }
+            }else{
+                throw new Error("no node in network or node without y-coordinate");
             }
         });
     });
@@ -813,7 +718,7 @@ function getNodesAssociatedMaxY(subgraphNetwork: SubgraphNetwork, associatedList
  * @param listNodes - An array of node names.
  * @returns The name of the node with the median x-coordinate.
  */
-function nodeMedianX(subgraphNetwork: SubgraphNetwork, listNodes: string[]): string {
+async function nodeMedianX(subgraphNetwork: SubgraphNetwork, listNodes: string[]): Promise<string> {
     const network=subgraphNetwork.network;
     let xValues = listNodes.map(node => [node,network.nodes[node].x]);
     xValues.sort((a, b) =>  Number(a[1]) - Number(b[1])); // sort by x
@@ -830,548 +735,427 @@ function nodeMedianX(subgraphNetwork: SubgraphNetwork, listNodes: string[]): str
 }
 
 
+/*******************************************************************************************************************************************************/
+//___________________________________________________2. Utilitary functions for cycle coordinate ________________________________________________________________
 
+/**
+ * Score a subgraph for sorting.
+ * First get number of circle fixed nodes (nodes fixed in a circle drawing), then size,
+ * then if full counstraint is true : number of parent nodes (of the cycle), and finally number of child nodes (of the cycle).
+ * @param subgraphNetwork - The subgraph network.
+ * @param subgraph - The first cycle to compare.
+ * @param fullConstraint - A flag indicating whether to consider the full constraint (default is false).
+ * @returns The different value calculated.
+ */
+ async function scoreSortingCycleForDrawing(subgraphNetwork:SubgraphNetwork,subgraph:Subgraph,fullConstraint:boolean=false):Promise<{fixedNodes:number,size:number,numberParent?:number,numberChild?:number}>{
+    const network=subgraphNetwork.network;
+    
+    if (subgraph.nodes.length===0) throw new Error("no node in cycle");
 
+    // number of fixed nodes
+    const fixedNodesA = subgraph.nodes.filter(node => network.nodes[node] && network.nodes[node].metadataLayout && network.nodes[node].metadataLayout.fixedInCircle).length;
+    // size
+    const size= subgraph.nodes.length;
 
+    if (fullConstraint){
+        const numberParent =  (await parentNodeNotInCycle(subgraphNetwork, subgraph.nodes)).flat().length;
+        const numberChild = (await childNodeNotInCycle(subgraphNetwork, subgraph.nodes)).flat().length;
+        return {fixedNodes:fixedNodesA,size:size,numberParent:numberParent,numberChild:numberChild};
+    }else{
+        return {fixedNodes:fixedNodesA,size:size};
+    }                     
+}
 
+/**
+ * Calculates scores for all subgraphs in the given subgraphNetwork.
+ *
+ * @param subgraphNetwork - The network of subgraphs to calculate scores for.
+ * @param subgraphs - An array of subgraphs to calculate scores for.
+ * @param fullConstraint - A boolean indicating whether to apply full constraints during score calculation. Defaults to false.
+ * @returns A promise that resolves to an object where each key is a subgraph name and the value is an object containing:
+ *   - `fixedNodes`: The number of fixed nodes in the subgraph.
+ *   - `size`: The size of the subgraph.
+ *   - `numberParent` (if fullConstraint): The number of parent nodes in the subgraph.
+ *   - `numberChild` (if fullConstraint): The number of child nodes in the subgraph.
+ */
+async function calculateAllScoresForSubgraphs(
+    subgraphNetwork: SubgraphNetwork, 
+    subgraphs: Array<Subgraph>, 
+    fullConstraint: boolean = false
+): Promise<{ [key: string]: { fixedNodes: number, size: number, numberParent?: number, numberChild?: number } }> {
+
+    const scores = await Promise.all(subgraphs.map(async (subgraph: Subgraph) => {
+        const score = await scoreSortingCycleForDrawing(subgraphNetwork, subgraph, fullConstraint);
+        return { [subgraph.name]: score }; 
+    }));
+    
+    return scores.reduce((acc, score) => ({ ...acc, ...score }), {});
+}
 
-//------------------------------------------------------------------------------------------------------------
-//________________________________Drawing cycle : shift position and placement in network_______________________________________
-//------------------------------------------------------------------------------------------------------------
+/**
+ * Sorting function for knowing order of cycle drawing. 
+ * First sort by number of circle fixed nodes (nodes fixed in a circle drawing), then by size,
+ * then if full counstraint is true : by number of parent nodes (of the cycle), and finally by number of child nodes (of the cycle).
+ * @param a - The first cycle to compare.
+ * @param b - The second cycle to compare.
+ * @param fullConstraint - A flag indicating whether to consider the full constraint (default is false).
+ * @returns A number indicating the sorting order.
+ */
+function sortingCycleForDrawing(aScore:{fixedNodes:number,size:number,numberParent?:number,numberChild?:number},
+    bScore:{fixedNodes:number,size:number,numberParent?:number,numberChild?:number},
+    fullConstraint:boolean=false):number{
+        if (!aScore || !bScore) {
+            throw new Error("score of subgraph cycle group must be defined");
+        }
+    
+        // fixedNodes
+        if (aScore.fixedNodes !== bScore.fixedNodes) {
+            return bScore.fixedNodes - aScore.fixedNodes;
+        }
+    
+        //size
+        if ( !fullConstraint || aScore.size !== bScore.size) {
+            return bScore.size - aScore.size;
+        }
+    
+        // if fullConstraint 
+        if (fullConstraint) {
+            // numberParent
+            if (aScore.numberParent==undefined || bScore.numberParent==undefined)  throw new Error("numberParent must be defined if full constraint is true");
+            if (aScore.numberParent !== bScore.numberParent) {
+                return (bScore.numberParent || 0) - (aScore.numberParent || 0);
+            }
+    
+            // numberChild
+            if (aScore.numberChild==undefined || bScore.numberChild==undefined)  throw new Error("numberChild must be defined if full constraint is true");
+            if (aScore.numberChild !== bScore.numberChild) {
+                return (bScore.numberChild || 0) - (aScore.numberChild || 0);
+            }
+        }
+    
+        return 0;
 
+}
 
 
 /**
- * Draws all cycle groups in the given subgraph network, that is put the precalculated coordinates inside the network
+ * Returns the cycle group name by appending the given name with a prefix.
  * 
- * @param subgraphNetwork - The subgraph network containing the cycle groups.
+ * @param name - The name to be appended with the prefix.
+ * @returns The cycle group name.
  */
-export function drawAllCyclesGroup(subgraphNetwork:SubgraphNetwork) {
-
-    //console.log('drawing cycles group');
-    if (TypeSubgraph.CYCLEGROUP in subgraphNetwork){
-        const cycleGroups = Object.keys(subgraphNetwork.cyclesGroup);
-        cycleGroups.forEach(cycleGroup => {
-            drawCycleGroup(cycleGroup,subgraphNetwork);
-        });
-    }
+function cycleGroupName(name:string):string{
+    return "cycle_group_"+name;
 }
 
 /**
- * Shift the precalculated coordinates nodes in a cycle group to their new positions based on the given metanode position, 
- * there are put inside the network to be drawn.
- * The coordinates of nodes (to be shifted) need to be in the metadata of the cycle group.
- * @param cycleGroup - The name of the cycle group.
- * @param subgraphNetwork - The subgraph network containing the cycle group and its nodes.
+ * Adds a new cycle group to the subgraphNetwork.
+ * 
+ * @param subgraphNetwork - The subgraph network to add the cycle group to.
+ * @param groupName - The name of the cycle group.
+ * @returns The updated subgraph network with the added cycle group.
  */
-function drawCycleGroup(cycleGroup:string,subgraphNetwork:SubgraphNetwork):void{
-    const metanodePosition=subgraphNetwork.cyclesGroup[cycleGroup].position;
-    const currentCenterPosition=subgraphNetwork.cyclesGroup[cycleGroup].originalPosition;
-    const dx=metanodePosition.x-currentCenterPosition.x;
-    const dy=metanodePosition.y-currentCenterPosition.y;
-    const listNode = Object.entries(subgraphNetwork.cyclesGroup[cycleGroup].metadata)
-                            .filter(([key,item]) => item["x"] !== undefined && item["y"] !== undefined);
-    listNode.forEach(([nodeID,coord])=>{
-        const node=subgraphNetwork.network.value.nodes[nodeID];
-        node.x = coord["x"] + dx;
-        node.y = coord["y"] + dy;
-    });
+function addNewCycleGroup(subgraphNetwork:SubgraphNetwork, groupName:string):SubgraphNetwork{
+    if(!subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
+        subgraphNetwork[TypeSubgraph.CYCLEGROUP]={};
+    }
+    subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupName]={name:groupName,nodes:[],type:TypeSubgraph.CYCLEGROUP};
+    return subgraphNetwork;
 }
 
-
-// ________________________Method 2________________________________________________________
-
-// export function coordinateAllCycles(subgraphNetwork:SubgraphNetwork):Promise<SubgraphNetwork>{
-//     return new Promise((resolve) => {
-//         // creation of group cycle and placement of first cycle
-//         subgraphNetwork=firstStepGroupCycles(subgraphNetwork);
-//         // if there are group cycle : continue to place the other cycles
-//         if (subgraphNetwork.cyclesGroup && Object.keys(subgraphNetwork.cyclesGroup).length>0){
-//             Promise.all(
-//                 Object.keys(subgraphNetwork.cyclesGroup).map(groupCycleName => { 
-//                     forceGroupCycle(subgraphNetwork, groupCycleName)
-//                 })
-//             ).then(() => {
-//                 subgraphNetwork=getSizeAllGroupCycles(subgraphNetwork);
-//                 resolve(subgraphNetwork);
-//             });
-//         }
-//     });
-// }
-
-// function firstStepGroupCycles(subgraphNetwork:SubgraphNetwork):SubgraphNetwork {
-//     let cycles = subgraphNetwork.cycles? Object.values(subgraphNetwork.cycles):undefined;
-//     let i=0;
-//     let group=-1;
-//     let groupName:string="";
-//     let newGroup=true;
-
-//     while (cycles && cycles.length > 0) {
+/**
+ * If the group cycle is drawn :
+ * - Updates the group cycles in the subgraphNetwork : add its size and center of rectangle.
+ * - Change the group number.
+ * 
+ * @param remainingCycles - The remaining cycles to process in the subgraph.
+ * @param subgraphNetwork - The subgraph network.
+ * @param group - The current cycle group number.
+ * @param groupCycleName - The name of the group cycle.
+ * @returns An object containing the updated subgraph network and group number.
+ */
+async function updateGroupCycles(remainingCycles: Subgraph[], subgraphNetwork: SubgraphNetwork, group: number, groupCycleName: string): Promise<{subgraphNetwork: SubgraphNetwork, group: number}> {
+    if (! subgraphNetwork[TypeSubgraph.CYCLEGROUP] || ! subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName]) throw new Error("Group cycle not in subgraphNetwork");
+    const cycleGroup=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName];
+    // check if group cycle is drawn, that is, no fixed nodes for other cycles (other cycle independant)
+    const groupCycleIsDraw = isRemainingCycleIndepOfDrawing(remainingCycles, subgraphNetwork);
+    
+    if (groupCycleIsDraw && cycleGroup.precalculatedNodesPosition) {
         
-//         if (newGroup){
-//             // if first cycle of a group cycle
-//             // creation of a new group cycle
-//             group+=1;
-//             groupName=cycleGroupName(String(group));
-//             subgraphNetwork=addNewCycleGroup(subgraphNetwork,groupName);
-//             // find biggest cycle, or the one with most constraint
-//                 // cycles that are not subgraph of a cycle
-//             const parentCycles = cycles.filter(cycle => !cycle.forSubgraph || cycle.forSubgraph.type !== TypeSubgraph.CYCLE);
-//             if (parentCycles.length === 0) {
-//                 console.error("No cycle found without a forSubgraph of type cycle");
-//                 return;
-//             }
-//             parentCycles.sort((a, b) => sortCycleSizeRelation(subgraphNetwork,a,b,true));
-//             const largestParentCycle = parentCycles[0];
-
-//             // add it to the group cycle and remove it from the list of cycle to process
-//             subgraphNetwork.cyclesGroup[groupName].nodes.push(largestParentCycle.name); 
-//             cycles = cycles.filter(cycle => cycle.name !== largestParentCycle.name);
-//             // give it coordinates      
-//             subgraphNetwork=coordinateCycle(subgraphNetwork, largestParentCycle.name,groupName,true); 
-
-//         }else{
-//             // if not first cycle of a group cycle
-//             // find all cycles with common nodes (fixed nodes)
-//             const dependantCyclesList=dependantCycles(cycles,subgraphNetwork);
-              
-//             dependantCyclesList.forEach(dependantCycle => {
-//                 // add them to the group cycle
-//                 subgraphNetwork.cyclesGroup[groupName].nodes.push(dependantCycle); 
-//                 // fix the nodes (to (0,0))
-//                 const nodes=subgraphNetwork.cycles[dependantCycle].nodes;
-//                 subgraphNetwork=coordinateCycle(subgraphNetwork, dependantCycle,groupName,false);
-//             });
-
-//             // remove them from the list of cycle to process
-//             cycles = cycles.filter(cycle => !dependantCyclesList.includes(cycle.name));
-            
-//         }
-
-//         // check all cycles of the group cycle are processed 
-//         newGroup=isRemainingCycleIndepOfDrawing(cycles, subgraphNetwork);
+        // force algo for node that have null position
+        subgraphNetwork = await forceGroupCycle(subgraphNetwork, groupCycleName);
 
-//     }
+        // get size of group and update cycle group information
+        const nodesInCycleGroup =getNodesPlacedInGroupCycleAsObject(subgraphNetwork,groupCycleName);
+        const coordinatesNodesCycleGroup = Object.values(nodesInCycleGroup)
+        const {width, height, center} = rectangleSize(coordinatesNodesCycleGroup);
+        cycleGroup.width = width;
+        cycleGroup.height = height;
+        cycleGroup.originalPosition = center;
 
-//     return subgraphNetwork;
-// }
+        // change group
+        group += 1;
 
+    } else if (groupCycleIsDraw) {
+        throw new Error('No coordinates for group cycle');
+    }
 
+    return {subgraphNetwork: subgraphNetwork, group: group};
+}
 
 
-// function coordinateCycle(subgraphNetwork:SubgraphNetwork, cycleToDrawID:string,groupCycleName:string,asCircle:boolean=true):SubgraphNetwork{
-//     const network = subgraphNetwork.network.value;
-//     let centroidX :number=0;
-//     let centroidY :number=0;
-    
-//     // Get nodes to place
-//     let cycle:string[]=[];
-//     if (cycleToDrawID in subgraphNetwork.cycles){
-//         cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
-//         subgraphNetwork.cycles[cycleToDrawID].metadata={};
-//     }else{  
-//         console.log('cycle not in subgraph network');
-//     }
-
-//     // Check existence of all nodes
-//     const cycleExist = cycle.every(node => node in network.nodes);
-
-
-//     // If cycle exist: place his nodes
-//     if (cycleExist && cycle.length>0){
-
-//         // Update node metadata to place them in cycleGroup
-//         cycle.forEach(node=>{
-//             network.nodes[node].metadata[TypeSubgraph.CYCLEGROUP]=groupCycleName;
-//         });
-
-//         // if the cycle has to be drawn as a circle
-//         if (asCircle){
-//             // radius and centroid
-//             const radius = getRadiusSize(cycle,network,subgraphNetwork.networkStyle.value);
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.radius=radius;   
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.centroid={x:centroidX,y:centroidY};         
-         
-//             // Shift cycle 
-//             const topIndex = findTopCycleNode(subgraphNetwork,cycle); // first node of list is the top 
-//             const cycleCopy= cycle.slice();
-//             const shiftedCycle = cycleCopy.splice(topIndex).concat(cycleCopy);
-
-//             // Give position to each node
-//             subgraphNetwork=cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,-Math.PI/2,groupCycleName);
-//         } else {
-//             subgraphNetwork=fixedCycleNodesToOrigin(cycle,subgraphNetwork,groupCycleName);
-//         }
-//     }
-
-//     return subgraphNetwork;
-// }
-
-
-// function sortCycleSizeRelation(subgraphNetwork:SubgraphNetwork,a:Subgraph,b:Subgraph,byRelation:boolean=false):number{
-//     // sort by size
-//     if ( !byRelation || b.nodes.length !== a.nodes.length ){
-//         return b.nodes.length - a.nodes.length;
-//     }else{
-//         // then by number of parent nodes
-//         const totalParentNodesA = parentNodeNotInCycle(subgraphNetwork, a.nodes)
-//             .flat().length;
-//         const totalParentNodesB = parentNodeNotInCycle(subgraphNetwork, b.nodes)
-//             .flat().length;
-//         if (totalParentNodesA !== totalParentNodesB){
-//             return totalParentNodesB - totalParentNodesA;
-//         }else{
-//             // then by number of child nodes
-//             const totalChildNodesA = childNodeNotInCycle(subgraphNetwork, a.nodes)
-//                 .flat().length;
-//             const totalChildNodesB = childNodeNotInCycle(subgraphNetwork, b.nodes)
-//                 .flat().length;
-            
-//             return totalChildNodesB - totalChildNodesA;
-//         }
-//     }                   
-// }
+/**
+ * Checks if all nodes in the remaining cycles are unfixed in the subgraphNetwork.
+ * 
+ * @param remainingCycles - An array of remaining cycles in the subgraph.
+ * @param subgraphNetwork - The subgraph network containing the nodes.
+ * @returns A boolean indicating whether all nodes in the remaining cycles are unfixed.
+ */
+function isRemainingCycleIndepOfDrawing(remainingCycles:Subgraph[], subgraphNetwork:SubgraphNetwork):boolean{
 
+    const network = subgraphNetwork.network;
 
+    const isAllNodeUnfixed=remainingCycles.every(
+        cycle => (
+        cycle.nodes.every(node =>  !network.nodes[node].metadataLayout || !network.nodes[node].metadataLayout.isFixed)
+    ));
+    if (isAllNodeUnfixed) {
+        //  All nodes in all remaining cycles are unfixed
+        return true;
+    } else {
+        //  At least one node in the remaining cycles fixed
+       return false;
+    }
+}
 
 
+/**
+ * Calculates the radius size based on the length of the cycle array and the radius factor.
+ * @param cycle - The array of strings representing the cycle.
+ * @param radiusFactor - The factor to multiply the length of the cycle by to calculate the radius size. Default value is 15.
+ * @returns The calculated radius size.
+ */
+async function getRadiusSize(cycle:string[],network:Network,styleNetwork:GraphStyleProperties):Promise<number>{
+    const nodes=Object.values(network.nodes).filter(node=>cycle.includes(node.id));
+    const meanSize=await getMeanNodesSizePixel(nodes,styleNetwork);
+    const radius= cycle.length*(meanSize.height+meanSize.width)/2;
+    return Number(radius.toFixed(2));
+}
 
 
-// function fixedCycleNodesToOrigin(cycle:string[],subgraphNetwork:SubgraphNetwork,groupcycle:string,):SubgraphNetwork{
-//     const network=subgraphNetwork.network.value;
-//     cycle.forEach((node) => {
-//         const nodeNetwork=network.nodes[node];        
-//         // Give position if not fixed
-//         if(network.nodes[node].metadata && !network.nodes[node].metadata.fixedCycle){
-//             if (groupcycle in subgraphNetwork.cyclesGroup){
-//                 if (!subgraphNetwork.cyclesGroup[groupcycle].metadata[node]){
-//                     subgraphNetwork.cyclesGroup[groupcycle].metadata[node] = {};
-//                 }
-//                 subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["x"]=0;
-//                 subgraphNetwork.cyclesGroup[groupcycle].metadata[node]["y"]=0;
-
-//                 // Fix the nodes 
-//                 if (!nodeNetwork.metadata) nodeNetwork.metadata={};
-//                 nodeNetwork.metadata.fixedInCycle= true;
-//             } else {
-//                 console.error("CycleGroup not in subgraphNetwork");
-//             }
-//         } 
-//     });
-
-//     return subgraphNetwork;
-// }
-
-
-// function dependantCycles(remainingCycles:Subgraph[], subgraphNetwork:SubgraphNetwork):Array<string>{
-
-//     const network = subgraphNetwork.network.value;
-
-//     const fixedCycleIds =  remainingCycles.filter(cycle => 
-//         cycle.nodes.some(node => 
-//             network.nodes[node].metadata && network.nodes[node].metadata.fixedInCycle
-//         )
-//     ).map(cycle => cycle.name);
-
-//     return fixedCycleIds;
-// }
-
-
-// function centroidFromNodes(nodesList:string[],subgraphNetwork:SubgraphNetwork):{x:number,y:number}{
-//     if (nodesList.length>0){
-//         const network=subgraphNetwork.network.value;
-//         let centroid={x:0,y:0}
-//         nodesList.forEach(node=> {
-//             if ("x" in network.nodes[node] && "y" in network.nodes[node]){
-//                 centroid.x += network.nodes[node].x;
-//                 centroid.y += network.nodes[node].y;
-//             }    
-//         });
-//         return {x:centroid.x/nodesList.length,y:centroid.y/nodesList.length};
-//     }
-//     return {x:0,y:0};
-// }
-
-
-
-// function euclideanDistance(point1: {x: number, y: number}, point2: {x: number, y: number}): number {
-//     let dx = point2.x - point1.x;
-//     let dy = point2.y - point1.y;
-//     return Math.sqrt(dx * dx + dy * dy);
-// }
-
-
-// function totalIntervals(startArc: {x: number, y: number}, endArc: {x: number, y: number}, centroidArc: {x: number, y: number}, nodesInArc: number): number {
-//     // Calculate distances
-//     let a = euclideanDistance(startArc, endArc);
-//     let b = euclideanDistance(startArc, centroidArc);
-//     let c = euclideanDistance(endArc, centroidArc);
-
-//     // Calculate angle of arc using law of cosines
-//     let angle = Math.acos((b*b + c*c - a*a) / (2 * b * c));
-
-//     // Calculate total intervals in full circle
-//     let totalIntervals = Math.round(2 * Math.PI / angle) * nodesInArc;
-
-//     return totalIntervals;
-// }
-
-// function centroidOneFixedCycleNode(subgraphNetwork:SubgraphNetwork,nodeFixedID:string,radius:number):{x:number,y:number}{
-//     const nodeFixed=subgraphNetwork.network.value.nodes[nodeFixedID];
-//     const radiusFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.radius as number;
-//     const centroidFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.centroid;
-//     const fixedAngle = Math.atan2(nodeFixed.y - centroidFixedCycle["y"], nodeFixed.x - centroidFixedCycle["x"]);
-//     const d = radius + radiusFixedCycle; 
-//     const centroidX = centroidFixedCycle["x"] + d * Math.cos(fixedAngle);
-//     const centroidY = centroidFixedCycle["y"] + d * Math.sin(fixedAngle);
-//     return {x:centroidX,y:centroidY}
-// }
-
 
+/**
+ * Assigns coordinates to a node within a subgraphNetwork, and get previous position of the node. If a group cycle name is provided, 
+ * the coordinates are assigned in the group cycle as a precalculated position. Otherwise, 
+ * the coordinates are assigned directly to the network.
+ *
+ * @param {SubgraphNetwork} subgraphNetwork - The subgraph network containing the node.
+ * @param {string} nodeID - The ID of the node to assign coordinates to.
+ * @param {number} x - The x-coordinate to assign to the node.
+ * @param {number} y - The y-coordinate to assign to the node.
+ * @param {string} [groupCycleName] - Optional name of the group cycle to assign the coordinates to.
+ * @returns {{subgraphNetwork: SubgraphNetwork, positionBefore: CoordinateNull}} An object containing the updated subgraph network and the node's position before the assignment.
+ * @throws {Error} If the group cycle name is provided but not found in the subgraph network, or if the node is not found in the network and no group cycle name is provided.
+ */
+function assignCoordinateToNode(subgraphNetwork:SubgraphNetwork,nodeID:string,x:number,y:number,groupCycleName?:string):{subgraphNetwork:SubgraphNetwork,positionBefore:CoordinateNull}{
+    const network=subgraphNetwork.network;
+    let positionBefore:CoordinateNull;
 
-// function pushFromIndepGroupCycles(subgraphNetwork:SubgraphNetwork, groupCyclesID:string[],allGroupCycleDrawn:string[][]):void{
+    // if groupCycleName is provided, assign position to the groupCycle as precalculated position
+    if (groupCycleName){
+        if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP] || !subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName]) throw new Error("group cycle not in subgraphNetwork");
+        const groupCycle=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName];
 
-//     console.log('--------------------------------------------------------------------');
-//     // nodes in the group of cycle that won't move (others nodes will be pushed deprending on their positions)
-//     const nodesCycles = groupCyclesID
-//     .filter(cycleID => cycleID in subgraphNetwork.cycles) // Check if cycleID exists
-//     .flatMap(cycleID => subgraphNetwork.cycles[cycleID].nodes) // get all nodes from all cycles
-//     .reduce((unique, item) => unique.includes(item) ? unique : [...unique, item], []); // remove duplicates
-
-//     // centroid of the group
-//     const centroid = centroidFromNodes(nodesCycles,subgraphNetwork);
-
-//     const radius=subgraphNetwork.cycles[groupCyclesID[0]].metadata.radius as number; // TO CHANGE when several nodes 
-
-//     // get list of nodes to push : all but the cycle with its shortcut
-//     // and group of cycle already draw considered as a node
-//     const network = subgraphNetwork.network.value;
-//     const nodesNetwork = Object.keys(network.nodes).filter(node => !nodesCycles.includes(node));
-//     const nodesGroupCycleDrawn=allGroupCycleDrawn.flatMap(groupCycle=>groupCycle)
-//         .flatMap(cycleID=>subgraphNetwork.cycles[cycleID].nodes) // get all nodes from all cycles
-//         .reduce((unique, item) => unique.includes(item) ? unique : [...unique, item], []); // remove duplicates
-//     const nodesNotInDrawCycle=nodesNetwork.filter(node => !nodesGroupCycleDrawn.includes(node)); 
-//     let nodeToPush=nodesNotInDrawCycle.filter(node => !nodesGroupCycleDrawn.includes(node)); 
-//     allGroupCycleDrawn.forEach(groupCycle=>{
-//         nodeToPush.push(groupCycle[0]); // metanode of cycle group 
-//     });
-    
-    
-//     // Need to push ?
+        if (!groupCycle.precalculatedNodesPosition) groupCycle.precalculatedNodesPosition={};
+        const precalculatedNodesPosition=groupCycle.precalculatedNodesPosition;
 
-//     const needPush=nodesInsideMetanode(groupCyclesID,nodesNetwork,subgraphNetwork);
-//     console.log(needPush);
+        if (!precalculatedNodesPosition[nodeID]){
+             // get position before drawing cycle, when node never been placed
+            positionBefore={x:null,y:null};
+            // assign new position
+            precalculatedNodesPosition[nodeID]={x:x,y:y};
+        }else{
+            // get position before drawing cycle
+            const xBefore=precalculatedNodesPosition[nodeID].x;
+            const yBefore=precalculatedNodesPosition[nodeID].y;
+            positionBefore={x:xBefore,y:yBefore};
+            // assign new position
+            precalculatedNodesPosition[nodeID].x=x;
+            precalculatedNodesPosition[nodeID].y=y;
+        }
+    // else assign position to the network
+    } else if (nodeID in network.nodes) {
+        // get position before drawing cycle (in the network)
+        const xBefore=network.nodes[nodeID].x;
+        const yBefore=network.nodes[nodeID].x;
+        positionBefore={x:xBefore,y:yBefore};
+        // assign new position
+        network.nodes[nodeID].x=x;
+        network.nodes[nodeID].y=y;
+    } else{
+        throw new Error("Node not in network or groupcycle not provided")
+    }
+    return {subgraphNetwork:subgraphNetwork,positionBefore:positionBefore};
+}
 
-//     // push nodes
+/**
+ * Reverts the positions of nodes in a subgraph network to their previous positions if there is an overlap in the group cycle.
+ *
+ * @param subgraphNetwork - The subgraph network containing the group cycle.
+ * @param groupCycleName - The name of the group cycle to check for overlaps.
+ * @param positionBefore - An object mapping node IDs to their positions before the drawing cycle.
+ * @returns The updated subgraph network with node positions reverted if there was an overlap.
+ * @throws Will throw an error if the group cycle does not exist in the subgraph network.
+ */
+function undoIfCreateOverlap(subgraphNetwork:SubgraphNetwork,groupCycleName:string,positionBefore:{[key:string]:CoordinateNull}):SubgraphNetwork{
+    if ( !subgraphNetwork[TypeSubgraph.CYCLEGROUP] || ! subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName] ) throw new Error("No group cycle in subgraphNetwork");
+    const groupCycle=subgraphNetwork[TypeSubgraph.CYCLEGROUP][groupCycleName];
 
-//     if (needPush){
-//         nodeToPush.forEach(nodeID =>{
+    // is there overlap in the group cycle ?
+    const isOverlap=isOverlapInCycleGroupDrawing(subgraphNetwork,groupCycleName);
+    // if overlap, put back the position before drawing cycle
+    if(isOverlap){
+        Object.entries(positionBefore).forEach(([nodeID,coord])=>{
+            if (!groupCycle.precalculatedNodesPosition) groupCycle.precalculatedNodesPosition={};
+            const xBefore=positionBefore[nodeID].x;
+            const yBefore=positionBefore[nodeID].y;
+            if (!groupCycle.precalculatedNodesPosition[nodeID]){
+                throw new Error("node supposed to have precalculated position in group cycle, because cause overlap");
+            } else {
+                const node=groupCycle.precalculatedNodesPosition[nodeID];
+                node.x = xBefore;
+                node.y = yBefore;
+            }
             
-//             if (nodeID in subgraphNetwork.cycles){ // if in a cycle
-//                 // get connected cycle group 
-//                 const fullGroupCycle=allGroupCycleDrawn.filter(groupCycle=> groupCycle.includes(nodeID))[0];
-//                 const nodesAsMetanode=fullGroupCycle.flatMap(cycleID=>subgraphNetwork.cycles[cycleID].nodes)
-//                     .reduce((unique, item) => unique.includes(item) ? unique : [...unique, item], []); // remove duplicates
-//                 const metanodeCentroid=centroidFromNodes(nodesAsMetanode,subgraphNetwork);
-
-//                 const distanceCentroidToMetanode=euclideanDistance(centroid,metanodeCentroid);
-//                 const distanceToMoveCentroid=(distanceCentroidToMetanode/radius+1)*radius;
-//                 pushMetanode(nodesAsMetanode,centroid,distanceToMoveCentroid,subgraphNetwork);
-
-
-//             }else if (nodeID in network.nodes){ // if classic node
+        });
+    }
+    return subgraphNetwork;
+}
 
-//                 const distanceCentroidToNode=euclideanDistance(centroid,network.nodes[nodeID]);
-//                 const distanceToMove=(distanceCentroidToNode/radius+1)*radius;
-//                 pushMetanode([nodeID],centroid,distanceToMove,subgraphNetwork);
+/**
+ * Determines if there are overlapping in cycle group.
+ *
+ * This function checks for intersections and overlaps among nodes and edges
+ * within a specified cycle group in the subgraph network. It performs the following checks:
+ * 1. Intersection of edges.
+ * 2. Overlap of nodes.
+ * 3. Overlap of nodes with edges.
+ *
+ * @param subgraphNetwork - The subgraph network containing the cycle groups and network details.
+ * @param groupCycleName - The name of the cycle group to check for overlaps.
+ * @returns `true` if there are overlapping cycles, otherwise `false`.
+ */
+function isOverlapInCycleGroupDrawing(subgraphNetwork:SubgraphNetwork,groupCycleName:string):boolean{
+    const graph =getListNodeLinksForCycleGroupAsObject(subgraphNetwork,groupCycleName);
+    // intersection of edges :
+    const intersectionEdges=isIntersectionGraph(graph.nodes ,graph.links);
+    if (intersectionEdges){
+        return true;
+    }else{
+        // overlap of nodes
+        const nodesOverlap=isOverlapNodes(graph.nodes,subgraphNetwork.network,subgraphNetwork.networkStyle);
+        if (nodesOverlap){
+            return true;
+        }else{
+            // overlap of node with edges
+            const edgeNodeOverlap=isOverlapNodesEdges(graph.nodes,graph.links,subgraphNetwork.network,subgraphNetwork.networkStyle);
+            if (edgeNodeOverlap){
+                return true;
+            }
+        }
+    }
+    return false;
+}
 
-//             }
 
-//         });
-//     }
-    
-// }
-
-// function pushMetanode(metanode:string[],centroidToPushfrom:{x:number,y:number},radius:number=1,subgraphNetwork:SubgraphNetwork):void{
-//     const network = subgraphNetwork.network.value;
-//     if (metanode.length===1){
-//         const node=network.nodes[metanode[0]];
-//         let dx = node.x - centroidToPushfrom.x;
-//         let dy = node.y - centroidToPushfrom.y;
-//         let angle = Math.atan2(dy, dx);
-//         node.x = centroidToPushfrom.x + radius * Math.cos(angle);
-//         node.y = centroidToPushfrom.y + radius * Math.sin(angle);
-//     }else{
-
-//         // MARCHE PAS
-
-//         // const centroidMetanode=centroidFromNodes(metanode,subgraphNetwork);
-//         // let dx = centroidMetanode.x - centroidToPushfrom.x;
-//         // let dy = centroidMetanode.y - centroidToPushfrom.y;
-//         // let angle = Math.atan2(dy, dx);
-//         // const newCentroid={x : centroidToPushfrom.x + radius * Math.cos(angle),
-//         //                     y : centroidToPushfrom.y + radius * Math.sin(angle)};
-
-//         // metanode.forEach(nodeID=>{
-//         //     const node=network.nodes[nodeID];
-//         //     node.x += newCentroid.x - centroidMetanode.x;
-//         //     node.y += newCentroid.y - centroidMetanode.y;
-//         // });
-//     } 
-// }
-
-// function nodesInsideMetanode(groupCyclesID:string[],nodeToCheck:string[],subgraphNetwork:SubgraphNetwork):boolean{
-
-
-//     const cycles=groupCyclesID.filter(cycleID => cycleID in subgraphNetwork.cycles && subgraphNetwork.cycles[cycleID].metadata 
-//         && "radius" in subgraphNetwork.cycles[cycleID].metadata && subgraphNetwork.cycles[cycleID].metadata.radius !== undefined
-//         && "centroid" in subgraphNetwork.cycles[cycleID].metadata &&  subgraphNetwork.cycles[cycleID].metadata.centroid 
-//         && subgraphNetwork.cycles[cycleID].metadata.centroid["x"] !== undefined
-//         && typeof subgraphNetwork.cycles[cycleID].metadata.centroid["y"] !== undefined
-//     ) 
-    
-//     let i=0;
-//     let flag=true;
-//     while (flag && i<cycles.length){
-//         flag=!nodesInsideCircle(cycles[i],nodeToCheck,subgraphNetwork);
-//         i++;
-//     }
-//     return !flag;
-// }
-
-// function nodesInsideCircle(cycleID:string,nodeToCheck:string[],subgraphNetwork:SubgraphNetwork):boolean{
-//     const centroid=subgraphNetwork.cycles[cycleID].metadata.centroid as {x:number,y:number};
-//     const radius=subgraphNetwork.cycles[cycleID].metadata.radius as number;
-
-
-//     let i=-1;
-//     let flag=true;
-//     while (flag && i<nodeToCheck.length){
-//         i++;
-//         if( nodeToCheck[i] && Object.keys(subgraphNetwork.network.value.nodes).includes(nodeToCheck[i]) ){
-            
-//             const node=subgraphNetwork.network.value.nodes[nodeToCheck[i]];
-//             const distance=euclideanDistance(node,centroid);
-//             if (distance<=radius){
-//                 flag=false;
-//             }
-            
-//         }else{
-//             //console.log('node '+nodeToCheck[i]+' not in network'); // PROBLEM !!!
-//         }
-        
-        
-//     }
-//     return !flag;
-// }
+/**
+ * Retrieves the unfixed intervals from a list of nodes. An unfixed interval is a continuous range of nodes that are not fixed in a cycle.
+ * An unfixed interval is a continuous range of nodes that are not fixed in the circle.
+ *
+ * @param nodes - An array of node IDs.
+ * @param subgraphNetwork - The subgraph network containing the nodes.
+ * @returns An array of intervals representing the unfixed intervals.
+ */
+async function getUnfixedIntervals(nodes:string[],subgraphNetwork:SubgraphNetwork):Promise<number[][]> {
+    let intervals:number[][] = [];
+    let start : number |null= null;
+    const network=subgraphNetwork.network;
+    nodes.forEach((nodeID,i) => {
+        const node=network.nodes[nodeID];
+        if (!node.metadataLayout || !node.metadataLayout.fixedInCircle) {
+            if (start === null) {
+                start = i;
+            }
+        } else {
+            if (start !== null) {
+                intervals.push([start, i - 1]);
+                start = null;
+            }
+        }
+    });
 
+    // Handle case where the last node is fixed
+    if (start !== null) {
+        intervals.push([start, nodes.length - 1]);
+    }
 
+    // case first interval and last are linked : combine interval as one long interval
+    if (intervals.length!==0 && intervals[0][0]===0 && intervals[intervals.length-1][1]===nodes.length-1){
+        intervals[0][0]=intervals[intervals.length-1][0]; // change start of first interval
+        intervals.pop(); // remove last interval
+    }
 
-// function drawCycle(subgraphNetwork:SubgraphNetwork,cycleToDrawID:string,radius:number|undefined=undefined,radiusFactor:number=15):void {
+    return intervals;
+}
 
-//     console.log('drawing '+cycleToDrawID);
-    
-//     let cycle:string[]=[];
-//     let centroidX :number;
-//     let centroidY :number;
-//     if (!subgraphNetwork.cycles[cycleToDrawID].metadata)  subgraphNetwork.cycles[cycleToDrawID].metadata={};
-//     subgraphNetwork.cycles[cycleToDrawID].metadata["radius"]=undefined;
-//     subgraphNetwork.cycles[cycleToDrawID].metadata["centroid"]=undefined;
-    
 
-//     if (cycleToDrawID in subgraphNetwork.cycles){
-//         cycle=subgraphNetwork.cycles[cycleToDrawID].nodes;
-//     }else{  
-//         console.log('argument cycleToDraw invalid');
-//     }
-
-//     // Check if the node are present in the graph, and see if position is fixed in another cycle
-//     const network = subgraphNetwork.network.value;
-//     let cycleExist = true;
-//     const nodesFixed:string[]=[];
-//     cycle.forEach(node=>{
-//         if (!(node in network.nodes)){
-//             cycleExist=false;
-//         } else if (network.nodes[node].metadata && network.nodes[node].metadata.fixedInCycle){
-//             nodesFixed.push(node);
-//         }
-//     });
-
-
-//     if (cycleExist && cycle.length>0){
-
-//         if (nodesFixed.length===0){ // if independant cycle ----------------------------------------------------------------------------------
-
-//             // radius
-//             if (radius === undefined){
-//                 radius = getRadiusSize(cycle,radiusFactor);
-//             }
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.radius=radius;
-
-//             // centroid
-//             if (subgraphNetwork.cycles[cycleToDrawID].metadata && subgraphNetwork.cycles[cycleToDrawID].metadata["x"] && subgraphNetwork.cycles[cycleToDrawID].metadata["y"]){
-//                 centroidX=subgraphNetwork.cycles[cycleToDrawID].metadata["x"] as number;
-//                 centroidY=subgraphNetwork.cycles[cycleToDrawID].metadata["y"] as number;
-//             }else {
-//                 const centroid=centroidFromNodes(cycle,subgraphNetwork);
-//                 centroidX=centroid.x;
-//                 centroidY=centroid.y;
-//             }
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.centroid={x:centroidX,y:centroidY};
 
-            
-//             // Shift cycle 
-//             const topIndex = findTopCycleNode(subgraphNetwork,cycle); // first node of list is the top 
-
-//             const cycleCopy= cycle.slice();
-//             const shiftedCycle = cycleCopy.splice(topIndex).concat(cycleCopy);
-
-//             // Give position to each node
-//             cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,-Math.PI/2);
-
-//         } else if (nodesFixed.length===1){ // if cycle linked to another cycle by one node ----------------------------------------------------------------------------------
-//             const nodeFixed=network.nodes[nodesFixed[0]];
-
-//              // first node is the one fixed :
-//              const cycleCopy= cycle.slice();
-//              const firstIndex=cycle.indexOf(nodesFixed[0]);
-//              const shiftedCycle = cycleCopy.splice(firstIndex).concat(cycleCopy);
-
-//             // radius
-//             if (radius === undefined){
-//                 radius = getRadiusSize(cycle,radiusFactor);
-//             }
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.radius=radius;
-
-//             //centroid depending on fixed cycle
-//             const radiusFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.radius as number;
-//             const centroidFixedCycle=subgraphNetwork.cycles[nodeFixed.metadata.fixedCycle as string].metadata.centroid;
-//             const fixedAngle = Math.atan2(nodeFixed.y - centroidFixedCycle["y"], nodeFixed.x - centroidFixedCycle["x"]);
-//             const d = radius + radiusFixedCycle; 
-//             const centroidX = centroidFixedCycle["x"] + d * Math.cos(fixedAngle);
-//             const centroidY = centroidFixedCycle["y"] + d * Math.sin(fixedAngle);
-//             subgraphNetwork.cycles[cycleToDrawID].metadata.centroid={x:centroidX,y:centroidY};
-            
+/*******************************************************************************************************************************************************/
+//____________________________________________3.  Drawing cycle : shift position and placement in network________________________________________________
 
-//             // shift of start angle (default:pi/2) : angle of fixed node in the new cycle (with centroid calculted before)
-//             const shiftAngle = Math.atan2(nodeFixed.y - centroidY, nodeFixed.x - centroidX);
-            
-//             // drawing :
-//             cycleNodesCoordinates(cycleToDrawID,shiftedCycle,centroidX,centroidY,radius,subgraphNetwork,shiftAngle);
-             
-//         } else { // several node in common with other cycle(s) ----------------------------------------------------------------------------------
 
-//             const unfixedInterval=getUnfixedIntervals(cycle,subgraphNetwork);
-//             unfixedInterval.forEach(interval=>{
 
-//                 const startNode=cycle[(interval[0]-1+ cycle.length) % cycle.length];
-//                 const endNode=cycle[(interval[1]+1+ cycle.length) % cycle.length];
-//                 lineNodesCoordinates(network.nodes[startNode],network.nodes[endNode],cycle.slice(interval[0],interval[1]+1),subgraphNetwork);
+/**
+ * Draws all cycle groups in the given subgraphNetwork, that is, put the precalculated coordinates inside the network
+ * 
+ * @param subgraphNetwork - The subgraph network containing the cycle groups.
+ */
+export function drawAllCyclesGroup(subgraphNetwork:SubgraphNetwork):void {
 
-//             });
+    if (TypeSubgraph.CYCLEGROUP in subgraphNetwork){
+        const cycleGroups = Object.keys(subgraphNetwork[TypeSubgraph.CYCLEGROUP] as {[key:string]:Subgraph});
+        cycleGroups.forEach(cycleGroup => {
+            drawCycleGroup(cycleGroup,subgraphNetwork);
+        });
+    }
+}
 
-//         }
-//     }
+/**
+ * Shift the precalculated coordinates nodes in a cycle group to their new positions based on the given metanode position, 
+ * there are put inside the network to be drawn.
+ * The coordinates of nodes (to be shifted) need to be in the metadata of the cycle group.
+ * @param cycleGroup - The name of the cycle group.
+ * @param subgraphNetwork - The subgraph network containing the cycle group and its nodes.
+ */
+function drawCycleGroup(cycleGroup:string,subgraphNetwork:SubgraphNetwork):void{
+    if (subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
+
+        if(!(cycleGroup in subgraphNetwork[TypeSubgraph.CYCLEGROUP])) throw new Error("Cycle group not in subgraph network");
+        const cycleGroupSubgraph=subgraphNetwork[TypeSubgraph.CYCLEGROUP][cycleGroup];
+        if ( !cycleGroupSubgraph.position || !cycleGroupSubgraph.originalPosition) throw new Error("Position and/or originalPosition not in cycle group");
+        const metanodePosition=cycleGroupSubgraph.position;
+        const currentCenterPosition=cycleGroupSubgraph.originalPosition;
+        const dx=metanodePosition.x-currentCenterPosition.x;
+        const dy=metanodePosition.y-currentCenterPosition.y;
+
+        const listNodeInCycleGroup=getNodesPlacedInGroupCycleAsObject(subgraphNetwork,cycleGroup);
+        Object.entries(listNodeInCycleGroup).forEach(([nodeID,coord])=>{
+            if (!(nodeID in subgraphNetwork.network.nodes)) throw new Error("Node not in network");
+            const node=subgraphNetwork.network.nodes[nodeID];
+            node.x = coord.x + dx;
+            node.y = coord.y + dy;
+        });
 
-// }
+    }
+}
 
diff --git a/src/composables/LayoutFindCycle.ts b/src/composables/LayoutFindCycle.ts
index a0bba171ed1fe9a5c142f54a8cb788ed52fc1127..8159995846ab2d370e92e11e61f3491db77999b6 100644
--- a/src/composables/LayoutFindCycle.ts
+++ b/src/composables/LayoutFindCycle.ts
@@ -1,14 +1,53 @@
+// Type imports
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { addSubgraphToNetwork, createSubgraph, updateNodeMetadataSubgraph } from "./SubgraphForSubgraphNetwork";
-import { TypeSubgraph } from "../types/Subgraph";
+import { Network } from  "../types/TypeVizCore";
+import { Subgraph, TypeSubgraph } from "../types/Subgraph";
+import { NetworkLayout } from "../types/NetworkLayout";
+
+
+// Composable imports
+import { addSubgraphToNetwork, createSubgraph, updateNodeMetadataSubgraph, updateParentSubgraphOf } from "./SubgraphForSubgraphNetwork";
 import { keepFirstReversibleNode, renameAllIDNode } from "./LayoutReversibleReactions";
 
 
+/**
+ * This file contains functions to find (directed) cycles in a network, and add them in the subgraphNetwork.
+ * If there is reversible reactions, the function will choose the appropriate direction to get the biggest directed cycles,
+ * and then remove the other version of the reaction node (and rename it if necessary). Cycles having common nodes will be associated as parent-child.
+ * 
+ * -> addDirectedCycleToSubgraphNetwork :
+ *      adds directed cycles to a subgraphNetwork.
+ * 
+ * -> getJohnsonCycles :
+ *      finds cycles in a given subgraph network using Johnson's algorithm.
+ * 
+ * -> graphForJohnson :
+ *      constructs a graph representation suitable for Johnson's algorithm to find cycles.
+ * 
+ * -> JohnsonAlgorithm :
+ *      finds all elementary cycles in a directed graph using Johnson's algorithm.
+ * 
+ * -> arePermutations :
+ *       checks if two arrays are permutations of each other.
+ */
+
+
+
+/**
+ * Adds directed cycles to a subgraph network.
+ * 
+ * This function identifies cycles within the given subgraph network and adds them to the network.
+ * It ensures that reversible reactions are appropriately directed (in order to get a maximum of the biggest directed cycles) and updates the metadata of nodes
+ * to include cycle information. If cycles share common nodes, it establishes parent-child relationships
+ * between the cycles based on their sizes : the biggest is the parent of the smallest.
+ * 
+ * @param {SubgraphNetwork} subgraphNetwork - The subgraph network to which cycles will be added.
+ * @param {number} [minsize=4] - The minimum size of cycles to be considered.
+ * @returns {SubgraphNetwork} - The updated subgraph network with added cycles.
+ */
 export function addDirectedCycleToSubgraphNetwork(subgraphNetwork:SubgraphNetwork,minsize:number=4):SubgraphNetwork{
-    //console.log('add cycle');
-    if (!subgraphNetwork.cycles){
-        subgraphNetwork.cycles={};
+    if (!subgraphNetwork[TypeSubgraph.CYCLE]){
+        subgraphNetwork[TypeSubgraph.CYCLE]={};
     }
     let nodeToRename:{[key: string]: string}={}; // to keep track of reversible reaction that have been removed
     
@@ -17,7 +56,7 @@ export function addDirectedCycleToSubgraphNetwork(subgraphNetwork:SubgraphNetwor
     // sort result depending on size : longest cycles first
     const sortedCycles = Object.entries(cycles).sort((a, b) => b[1].length - a[1].length); // [1] to get the value of object (not key)
 
-    // adding cycles to subgraph network, and choose appropriate direction for reversible reactions
+    // adding cycles to subgraph network, and choose appropriate direction for reversible reactions :
     sortedCycles.forEach(cycle=>{// cycle[0] is the name/id, cycle[1] is the list of nodes
         // check if all nodes of cycle still exists (needed because of removal of reversible nodes)
         let existingCycle=true;
@@ -37,10 +76,9 @@ export function addDirectedCycleToSubgraphNetwork(subgraphNetwork:SubgraphNetwor
             // remove reversible version of node in cycle, to only keep the one in the direction of cycle
             const toRename=keepFirstReversibleNode(subgraphNetwork,cycle[1],false) as {[key: string]: string};
             nodeToRename=Object.assign(nodeToRename, toRename);
-            
 
             // has common nodes with a cycle in subgraphNetwork ?
-            let networkCycles = Object.values(subgraphNetwork.cycles);
+            let networkCycles = Object.values(subgraphNetwork[TypeSubgraph.CYCLE] as {[key:string]:Subgraph});
             let i = 0;
             let commonNodes = [];
             while (i < networkCycles.length) {
@@ -54,23 +92,19 @@ export function addDirectedCycleToSubgraphNetwork(subgraphNetwork:SubgraphNetwor
             const subgraph=createSubgraph(cycle[0],cycle[1],[],TypeSubgraph.CYCLE);
             subgraphNetwork=addSubgraphToNetwork(subgraphNetwork,subgraph,TypeSubgraph.CYCLE);
 
-            // if combined cycle (with cycle i) : that is, if there are more than one common nodes
-            if (commonNodes.length > 1) {  
-               const sizeCycle=cycle[1].length;
-               const sizeCommonCycle= networkCycles[i].nodes.length;
-               // add information of 'parent' (bigger cycle) and 'child' (smaller cycle) cycle 
-               if (sizeCycle<=sizeCommonCycle){ // if sorting worked : it is always the case
-                    subgraphNetwork.cycles[cycle[0]].parentSubgraph={name:networkCycles[i].name,type:TypeSubgraph.CYCLE};
-                    if(! subgraphNetwork.cycles[networkCycles[i].name].childrenSubgraphs){
-                        subgraphNetwork.cycles[networkCycles[i].name].childrenSubgraphs=[];
+            if (subgraphNetwork[TypeSubgraph.CYCLE]){
+                // if combined cycle (with cycle i) : that is, if there are more than one common nodes
+                if (commonNodes.length > 1) {  
+                const sizeCycle=cycle[1].length;
+                const sizeCommonCycle= networkCycles[i].nodes.length;
+                // add information of 'parent' (bigger cycle) and 'child' (smaller cycle) cycle 
+                if (sizeCycle<=sizeCommonCycle){ // if sorting worked : it is supposed to always be the case
+                        subgraphNetwork[TypeSubgraph.CYCLE][cycle[0]].parentSubgraph={name:networkCycles[i].name,type:TypeSubgraph.CYCLE};
+                        updateParentSubgraphOf(subgraphNetwork,subgraphNetwork[TypeSubgraph.CYCLE][cycle[0]]);
+                    }else{
+                        subgraphNetwork[TypeSubgraph.CYCLE][networkCycles[i].name].parentSubgraph={name:cycle[0],type:TypeSubgraph.CYCLE};
+                        updateParentSubgraphOf(subgraphNetwork,subgraphNetwork[TypeSubgraph.CYCLE][networkCycles[i].name]);
                     }
-                    subgraphNetwork.cycles[networkCycles[i].name].childrenSubgraphs.push({name:cycle[0],type:TypeSubgraph.CYCLE});
-                }else{
-                    subgraphNetwork.cycles[networkCycles[i].name].parentSubgraph={name:cycle[0],type:TypeSubgraph.CYCLE};
-                    if(! subgraphNetwork.cycles[cycle[0]].childrenSubgraphs){
-                        subgraphNetwork.cycles[cycle[0]].childrenSubgraphs=[];
-                    }
-                    subgraphNetwork.cycles[cycle[0]].childrenSubgraphs.push({name:networkCycles[i].name,type:TypeSubgraph.CYCLE});
                 }
             }
                       
@@ -83,6 +117,14 @@ export function addDirectedCycleToSubgraphNetwork(subgraphNetwork:SubgraphNetwor
     return subgraphNetwork;
 }
 
+/**
+ * Finds cycles in a given subgraph network using Johnson's algorithm.
+ *
+ * @param subNetwork - The subgraph network to analyze.
+ * @param onlyDirectedCycle - A boolean indicating whether to find only directed cycles. Defaults to `true`.
+ * @param minsize - The minimum size of the cycles to be found. Defaults to `4`.
+ * @returns An object where keys are cycle identifiers and values are arrays of node identifiers representing the cycles.
+ */
 function getJohnsonCycles(subNetwork:SubgraphNetwork,onlyDirectedCycle:boolean=true,minsize:number=4):{[key:string]:string[]} {
         // get graph structure for johnson algorithm
         const nodes=Object.keys(subNetwork.network.nodes).sort();
@@ -93,6 +135,14 @@ function getJohnsonCycles(subNetwork:SubgraphNetwork,onlyDirectedCycle:boolean=t
 
 
 
+/**
+ * Constructs a graph representation suitable for Johnson's algorithm to find cycles.
+ *
+ * @param network - The network containing nodes and links.
+ * @param list_nodes - An array of node identifiers.
+ * @param onlyDirectedCycle - A boolean indicating whether to consider only directed cycles (default is true).
+ * @returns A 2D array representing the adjacency list of the graph.
+ */
 function graphForJohnson(network:Network, list_nodes:string[], onlyDirectedCycle:boolean=true):number[][]{
     let graph: number[][] = Array.from({ length: list_nodes.length }, () => []);
     network.links.forEach(link=>{
@@ -107,7 +157,21 @@ function graphForJohnson(network:Network, list_nodes:string[], onlyDirectedCycle
 }
 
 
-export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "Single" | "All"="All",onlyDirectedCycle:boolean=true,minsize:number=4,network?:Network): {[key:string]:string[]} {
+/**
+ * Finds all elementary cycles in a directed graph using Johnson's algorithm.
+ * 
+ * @param graph - The adjacency matrix of the graph where graph[i][j] is 1 if there is an edge from node i to node j, otherwise 0.
+ * @param list_nodes - An array of node identifiers corresponding to the indices in the graph.
+ * @param flag - Determines whether to find a single cycle or all cycles. Can be "Single" or "All". Default is "All".
+ * @param onlyDirectedCycle - If true, only directed cycles are considered. Default is true.
+ * @param minsize - The minimum size of the cycles to be considered. Default is 4.
+ * @param network - An optional parameter representing the network which contains additional metadata for nodes.
+ * 
+ * @returns An object where the keys are cycle identifiers and the values are arrays of node identifiers representing the cycles.
+ * 
+ * @throws Will throw an error if there is a null value in the stack, indicating a problem with stackTop.
+ */
+function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "Single" | "All"="All",onlyDirectedCycle:boolean=true,minsize:number=4,network?:NetworkLayout): {[key:string]:string[]} {
     const nVertices: number = graph.length;
     let nbcycle:number=0;
     let start: number = 0;
@@ -157,9 +221,9 @@ export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "S
             // if network : node w not taken into account if reversible version in the stack (cycle with only one of the version)
             if(network){ 
                 const wNode=network.nodes[list_nodes[w]];
-                if( "metadata" in wNode && "reversibleVersion" in wNode.metadata){
+                if( wNode.metadataLayout && wNode.metadataLayout.reversibleNodeVersion){
                     const stackCopy = stack.slice(0, stackTop);
-                    const reactionRev =wNode.metadata["reversibleVersion"] as string;
+                    const reactionRev =wNode.metadataLayout.reversibleNodeVersion;
                     const reversibleNumber=list_nodes.indexOf(reactionRev);
                     if(stackCopy.includes(reversibleNumber)){
                         continue; // the reversible reaction is not processed as the original version is already in the stack
@@ -169,24 +233,25 @@ export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "S
 
             // If w is equal to start, a cycle is found (find only cycle that start from start node)
             if (w === start) {
-                let cycle: number[] = stack.slice(0, stackTop);
+                let cycle: number[] = stack.slice(0, stackTop) as number[];
 
 
                 // If the cycle length is more than 3, add it to the result
                 if (cycle.length >= minsize) {
                     const cycleID:string[]=[];
                     stack.slice(0, stackTop).forEach(nodeIndex=>{
-                        cycleID.push(list_nodes[nodeIndex]);
+                        if (nodeIndex!==null){
+                            cycleID.push(list_nodes[nodeIndex]);
+                        }else{
+                            throw new Error("null value in stack : problem with stackTop");
+                        }
                     });
                     //adding check that not already found in the case of undirected cycle
-                    if (!onlyDirectedCycle && !Object.values(result).some(existingCycle => arePermutations(existingCycle, cycleID))) {
-                        result["cycle_"+String(nbcycle)]=cycleID;
-                        nbcycle+=1;
-                    }
-                    else if (onlyDirectedCycle) {
+                    if (!Object.values(result).some(existingCycle => arePermutations(existingCycle, cycleID))) {
                         result["cycle_"+String(nbcycle)]=cycleID;
                         nbcycle+=1;
                     }
+                    
                 }
                 f = true;
             } else if (!blocked[w]) { // If w is not blocked, find a cycle starting from w (dfs principle)
@@ -212,7 +277,7 @@ export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "S
                 }
             }
         }
-        v = stackPop();
+        v = stackPop() as number;
         return f;
     }
 
@@ -238,9 +303,7 @@ export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "S
     }
 
     start = 0;
-    // for (let i = 0; i < nVertices; i++) {
-    //     console.log(String(i)+" "+String(list_nodes[i]));
-    // }
+
     while (start < nbNodeToRun) {
         for (let i = 0; i < nVertices; i++) {
             blocked[i] = false;
@@ -252,10 +315,15 @@ export function JohnsonAlgorithm(graph: number[][], list_nodes:string[],flag: "S
     return result;
 }
 
-//chatgtp function
+/**
+ * Checks if two arrays are permutations of each other.
+ *
+ * @param arr1 - The first array.
+ * @param arr2 - The second array.
+ * @returns A boolean indicating whether the two arrays are permutations of each other.
+ */
 function arePermutations(arr1: string[], arr2: string[]): boolean {
     if (arr1.length !== arr2.length) return false;
-
     const sortedArr1 = arr1.slice().sort();
     const sortedArr2 = arr2.slice().sort();
 
diff --git a/src/composables/LayoutMain.ts b/src/composables/LayoutMain.ts
index 05b65c4cf0e7ec9a9a5b8ac237ca050d9f34b844..a9dcb2e203e812d92423df4e2042900360f3fece 100644
--- a/src/composables/LayoutMain.ts
+++ b/src/composables/LayoutMain.ts
@@ -1,15 +1,14 @@
 // Type imports
 import { defaultParameters,Parameters } from "../types/Parameters";
-import { StartNodesType } from "../types/EnumArgs";
+import { PathType, StartNodesType } from "../types/EnumArgs";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
 import { TypeSubgraph } from "../types/Subgraph";
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Network , GraphStyleProperties} from  "../types/TypeVizCore";
 import { NetworkLayout } from "../types/NetworkLayout";
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
 
 // Composable imports
 import { putDuplicatedSideCompoundAside, reinsertionSideCompounds } from "./LayoutManageSideCompounds";
-import { vizLayout } from "./LayoutSugiyamaForce";
+import { vizLayout } from "./LayoutSugiyama";
 import { chooseReversibleReaction, duplicateReversibleReactions } from "./LayoutReversibleReactions";
 import { addDirectedCycleToSubgraphNetwork } from "./LayoutFindCycle";
 import { BFSWithSources } from "./AlgorithmBFS";
@@ -19,16 +18,16 @@ import { shiftAllToGetTopLeftCoord } from "./CalculateSize";
 import { getStartNodes } from "./CalculateStartNodes";
 import { networktoNetworkLayout } from "./ConvertFromNetwork";
 import { networkLayoutToNetwork } from "./ConvertToNetwork";
+import { checkNetworkFormat } from "./CheckNetwork";
+
 
-// General imports
-import { ref } from "vue";
 
 
 
 /*******************************************************************************************************************************************************
  * This file contains the main function of the algorithm (that is to change the coordinates of a network => application of the layout).
  * 
- * -> algorithmOnNetwork : 
+ * -> layoutOnNetwork : 
  *      change the nodes coordinates of network
  * -> allSteps : 
  *      apply all steps of the algorithm to change node coordinates of a network
@@ -43,41 +42,30 @@ import { ref } from "vue";
  * @param parameters - The optional parameters for the algorithm.
  * @returns A promise that resolves to the modified network after applying the algorithm.
  * @throws An error if the network or networkStyle is not defined or empty.
+ * @throws An error if the network is not in the correct format.
  */
-export async function algorithmOnNetwork(network:Network,networkStyle:GraphStyleProperties,parameters:Parameters=defaultParameters):Promise<Network>{
+export async function layoutOnNetwork(network:Network,networkStyle:GraphStyleProperties={},parameters:Parameters=defaultParameters):Promise<Network>{
 
-  // check if the network is not empty
-  if ( !network || Object.keys(network.nodes).length===0){
-    console.warn('The network is not defined or has no nodes : the algorithm will not be executed');
-    throw new Error('The network is not defined or has no nodes : the algorithm will not be executed');
-  }
+  // check if the network has the correct format
+  await checkNetworkFormat(network,false, true);
 
-  // check if the networkStyle is not empty
-  if ( !networkStyle || Object.keys(networkStyle).length===0){
-    console.warn('The networkStyle is not defined or has no properties : the algorithm will not be executed');
-    throw new Error('The networkStyle is not defined or has no properties : the algorithm will not be executed');
+  // check if the network is not empty
+  if ( !network || Object.keys(network.nodes).length===0 || network.links.length===0){
+    throw new Error('The network is not defined, has no nodes or no links : the algorithm will not be executed');
   }
 
   // convert network to networkLayout
   let networkLayout:NetworkLayout=networktoNetworkLayout(network);
-
   // initialize the subgraphNetwork object
   let subgraphNetwork:SubgraphNetwork={
     network:networkLayout,
-    networkStyle:networkStyle
+    networkStyle:networkStyle,
   }
 
- 
-  try {
-     // change coordinates of the network with the algorithm
-    await allSteps(subgraphNetwork,parameters,true,true);
-    // convert networkLayout to network
-    return networkLayoutToNetwork(subgraphNetwork.network);
-    
-  } catch(err){
-    console.log(" Error during execution of algorithm : " + err);
-    throw err;
-  }
+    // change coordinates of the network with the algorithm
+  await allSteps(subgraphNetwork,parameters,false);
+  // convert networkLayout to network
+  return networkLayoutToNetwork(subgraphNetwork.network); 
 
 }
 
@@ -86,11 +74,10 @@ export async function algorithmOnNetwork(network:Network,networkStyle:GraphStyle
  * Apply all steps of the algorithm to change node coordinates of a network. SubgraphNetwork is an object that contains the network to change and all the information needed during the steps.
  * @param subgraphNetwork object that contains the network, network style, attributs for viz, subgraph information and side compounds
  * @param parameters parameters for the algorithm
- * @param shiftCoord change the coordinates to have the one of the top left corner of nodes, if false, the coordinates are the centers
  * @param printNameStep print the name of the steps during execution
  * @returns a promise of the subgraphNetwork 
  */
-export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Parameters,shiftCoord:boolean=true,printNameStep:boolean=false):Promise<SubgraphNetwork> {
+export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Parameters,printNameStep:boolean=false):Promise<SubgraphNetwork> {
 
     let network=subgraphNetwork.network;
     let networkStyle=subgraphNetwork.networkStyle;
@@ -102,10 +89,9 @@ export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Param
       console.log('---------------');
     }
   
-  
     // duplicate side compounds and put them aside
     if (printNameStep) console.log('SideCompound duplication and put aside');
-    await putDuplicatedSideCompoundAside(subgraphNetwork,parameters.doDuplicateSideCompounds,parameters.doPutAsideSideCompounds,true,"/sideCompounds.txt").then(
+    await putDuplicatedSideCompoundAside(subgraphNetwork,parameters.doDuplicateSideCompounds,parameters.doPutAsideSideCompounds).then(
       (subgraphNetworkModified)=>{
           subgraphNetwork=subgraphNetworkModified;
       }
@@ -136,7 +122,7 @@ export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Param
     ).then(
       () => {
         // if no cycle, we don't need to do the cycle step
-        if (parameters.doCycle && subgraphNetwork[TypeSubgraph.CYCLE] && Object.keys(subgraphNetwork[TypeSubgraph.CYCLE]).length===0){
+        if (parameters.doCycle && (!subgraphNetwork[TypeSubgraph.CYCLE] || Object.keys(subgraphNetwork[TypeSubgraph.CYCLE]).length===0)){
           parameters.doCycle=false;
           console.warn('doCycle is true but no cycle found : doCycle set to false');
         }
@@ -155,16 +141,19 @@ export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Param
         // get main chains
         if (parameters.doMainChain){
           if (printNameStep) console.log('Find main chain');
+          if(parameters.pathType===PathType.ALL && !parameters.merge){
+            console.warn('PathType is ALL, but because merge is false, only longest keeped when no merge : use LONGEST instead or set merge to true');
+          }
           const sources=await getStartNodes(network,parameters.startNodeTypeMainChain);
-          addMainChainFromSources(subgraphNetwork, sources,parameters.getSubgraph, parameters.merge,parameters.pathType);
+          await addMainChainFromSources(subgraphNetwork, sources,parameters.getSubgraph, parameters.merge,parameters.pathType);
         }
       }
     ).then(
-      () => {
+      async () => {
         // add minibranch
         if(parameters.doMainChain && parameters.doMiniBranch){
           if (printNameStep) console.log('Add minibranch');
-          subgraphNetwork= addMiniBranchToMainChain(subgraphNetwork);
+          subgraphNetwork= await addMiniBranchToMainChain(subgraphNetwork);
         }else if ( !parameters.doMainChain && parameters.doMiniBranch){
           console.warn('doMiniBranch is true but doMainChain is false : minibranch will not be added');
         }
@@ -212,18 +201,13 @@ export async function allSteps(subgraphNetwork: SubgraphNetwork,parameters:Param
     ).then(
       () => {
         // shift coordinates to have top left corner coordinate (because of svg drawing)
-        if (shiftCoord){
+        if (parameters.shiftCoord){
           if (printNameStep) console.log('Shift coordinates nodes to have center at the old coordinates');
           shiftAllToGetTopLeftCoord(network,networkStyle);
         }
       }
-    ).then(
-      () => {
-        // add color to link (optional : for debug)
-        //subgraphNetwork = addBoldLinkMainChain(subgraphNetwork);
-        //subgraphNetwork=addRedLinkcycleGroup(subgraphNetwork);
-      }
     );
+    
     return subgraphNetwork;
   
 }
diff --git a/src/composables/LayoutMainChain.ts b/src/composables/LayoutMainChain.ts
index bd130f864244d5f8e7e57602aaf3d13fabaf1308..9af059454f7de0cf2c48524fb1c8340d6bff001e 100644
--- a/src/composables/LayoutMainChain.ts
+++ b/src/composables/LayoutMainChain.ts
@@ -1,54 +1,92 @@
+// Type imports
 import { PathType, StartNodesType } from "../types/EnumArgs";
-import { DFSWithSources, DFSsourceDAG } from "./AlgorithmDFS";
-import { networkToGDSGraph } from "./ConvertFromNetwork";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
+import {TypeSubgraph, type Subgraph} from "../types/Subgraph";
+import { Network } from  "../types/TypeVizCore";
+
+// Composable imports
+import { DFSsourceDAG } from "./AlgorithmDFS";
+import { networkToGDSGraph } from "./ConvertFromNetwork";
 import { getStartNodes } from "./CalculateStartNodes";
-import { Network } from "@metabohub/viz-core/src/types/Network";
 import { BFS } from "./AlgorithmBFS";
-import {TypeSubgraph, type Subgraph} from "../types/Subgraph";
-import { addSubgraphToNetwork, createSubgraph, updateNodeMetadataSubgraph } from './SubgraphForSubgraphNetwork';
+import { addSubgraphToNetwork, createSubgraph } from './SubgraphForSubgraphNetwork';
+
+
+/**
+ * This file contains functions find and add main chains and mini-branch (form main chain) in a subgraphNetwork.
+ * 
+ * *********************************
+ * 
+ * 0. Main steps for main chains
+ * 
+ * -> addMainChainFromSources :
+ *      Adds main chains from sources to the subgraph network.
+ * 
+ * -> addMiniBranchToMainChain :
+ *      Adds mini branches to the main chain in the cluster network.
+ * 
+ * 
+ * *********************************
+ * 
+ * 1. Method for main chains
+ * 
+ * -> getPathSourcesToTargetNode :
+ *      Returns the longest paths from source nodes for the source DAG.
+ * 
+ * -> DistanceFromSourceDAG :
+ *      Dijkstra like algorithm to get longest distance from source node for each node.
+ * 
+ * -> findMaxKeys :
+ *      Find the keys associated with the maximum value in the object.
+ * 
+ * -> mergeNewPath :
+ *     Merge a new path in the object with all paths.
+ * 
+ */
+
+/*******************************************************************************************************************************************************/
+//___________________________________________________0.  Main steps for main chains_______________________________________________________________________
 
 
 /**
- * Add clusters (in ClusterNetwork object) taht are at least of a minimum size. A method is given in the parameters to get the clusters
- * @param subgraphNetwork an object with the network and object for clusters
- * @param sources array of node ID or a type of method to get sources automatically
+ * Adds main chains from sources to the subgraph network.
+ *
+ * @param subgraphNetwork - The subgraph network to which main chains will be added.
+ * @param sources - An array of source node IDs or a StartNodesType object.
  * RANK_ONLY : sources are nodes of rank 0
  * SOURCE_ONLY : sources are topological sources of the network (nul indegree)
  * RANK_SOURCE : sources are node of rank 0, then source nodes
  * ALL : sources are all nodes
  * SOURCE_ALL : sources are topological sources, then all the others nodes
  * RANK_SOURCE_ALL : sources are node of rank 0, then topological sources, then all the other nodes
- * For this function, the advised choice is either RANK_ONLY, SOURCE_ONLY or RANK_SOURCE.
- * @param getMainChains function that return the clusters to add
- * @param merge if true, merge the path with an existing one if a common node is found, else common nodes in several clusters
- *  => not taken by all methods of getCluster
- * @param pathType the type of path to target node wanted :
- * LONGEST :  one of the longest path
+ * @param getMainChains - A function that returns main chains of paths from sources. Defaults to `getPathSourcesToTargetNode`.
+ * @param merge - A boolean indicating whether to merge paths. Defaults to `true`.
+ * @param pathType - The type of path to consider. Defaults to `PathType.ALL_LONGEST`.
+ * * LONGEST :  one of the longest path
  * ALL_LONGEST : all the longest lenght
  * ALL : all path
- * => not taken by all methods of getCluster
- * @param minHeight minimum size of a cluster to be added
- * @returns the clusterNetwork with more cluster
+ * @param minHeight - The minimum height of the main chain to be added. Defaults to `4`.
+ * @returns A promise of the updated subgraph network with the added main chains.
  */
-export function addMainChainFromSources(subgraphNetwork:SubgraphNetwork, sources:Array<string> | StartNodesType, 
-    getMainChains:(network: Network, sources: Array<string>,merge?:boolean,pathType?:PathType) => {[key:string]:{nodes:Array<string>, height:number}}=getPathSourcesToTargetNode,
+export async function addMainChainFromSources(subgraphNetwork:SubgraphNetwork, sources:Array<string> | StartNodesType, 
+    getMainChains:(network: Network, sources: Array<string>,merge?:boolean,pathType?:PathType) => Promise<{[key:string]:{nodes:Array<string>, height:number}}>=getPathSourcesToTargetNode,
     merge:boolean=true,
     pathType:PathType=PathType.ALL_LONGEST,
     minHeight:number=4
-):SubgraphNetwork{
+):Promise<SubgraphNetwork>{
 
-    //console.log('create main chain from longest path');
     const network=subgraphNetwork.network;
-    subgraphNetwork[TypeSubgraph.MAIN_CHAIN]={};
+    if(!subgraphNetwork[TypeSubgraph.MAIN_CHAIN]) subgraphNetwork[TypeSubgraph.MAIN_CHAIN]={};
+    const mainChains=subgraphNetwork[TypeSubgraph.MAIN_CHAIN];
+    
     
     // get sources
     if (!Array.isArray(sources)){
-        sources=getStartNodes(network,sources);
+        sources=await getStartNodes(network,sources);
     }
 
     // get main chains of paths from sources
-    const newMainChains=getMainChains(network,sources as string[],merge,pathType);
+    const newMainChains=await getMainChains(network,sources as string[],merge,pathType);
 
     // add main chains if length > minsize, and update metadata for nodes
     Object.entries(newMainChains).forEach(([mainChainID,mainChain]:[string,{nodes:Array<string>, height:number}])=>{
@@ -56,11 +94,7 @@ export function addMainChainFromSources(subgraphNetwork:SubgraphNetwork, sources
             // create subgraph and add it
             const newMainChainId="mainChain__"+mainChainID;
             const newMainChain= createSubgraph(newMainChainId, mainChain.nodes,[],TypeSubgraph.MAIN_CHAIN);
-            subgraphNetwork.mainChains[newMainChainId]=newMainChain;
-            // add metadata for node in cluster
-            mainChain.nodes.forEach(nodeID=>{
-                updateNodeMetadataSubgraph(network, nodeID, newMainChainId);
-            });
+            subgraphNetwork=addSubgraphToNetwork(subgraphNetwork,newMainChain,TypeSubgraph.MAIN_CHAIN);
         }
     });
 
@@ -72,18 +106,19 @@ export function addMainChainFromSources(subgraphNetwork:SubgraphNetwork, sources
  * Adds mini branches to the main chain in the cluster network.
  * A mini branch is a child of a node in main chain cluster that has no children.
  * @param subgraphNetwork - The cluster network to modify.
- * @returns The modified cluster network.
+ * @returns A promise of the modified cluster network.
  */
-export function addMiniBranchToMainChain(subgraphNetwork:SubgraphNetwork):SubgraphNetwork{
-    //console.log('add mini branch to main chain');
+export async function addMiniBranchToMainChain(subgraphNetwork:SubgraphNetwork):Promise<SubgraphNetwork>{
+    if(!subgraphNetwork[TypeSubgraph.MAIN_CHAIN]) return subgraphNetwork;
+    const mainChains=subgraphNetwork[TypeSubgraph.MAIN_CHAIN];
     const network=subgraphNetwork.network;
-    const graph=networkToGDSGraph(network);  
+    const graph=await networkToGDSGraph(network);  
     // for each main chain :
-    Object.entries(subgraphNetwork.mainChains).forEach(([mainChainID,mainChain]:[string,Subgraph]) => {
+    Object.entries(mainChains).forEach(([mainChainID,mainChain]:[string,Subgraph]) => {
         const nodesToAdd:string[]=[];
         // for each node of the main chain :
         mainChain.nodes.forEach(node=>{
-            const children=graph.adjacent(node);
+            const children:string[]=graph.adjacent(node);
             children.forEach(child=>{
                 // if child is sink : 
                 if (graph.outdegree(child)===0){
@@ -106,128 +141,9 @@ export function addMiniBranchToMainChain(subgraphNetwork:SubgraphNetwork):Subgra
 
 
 
-// ----------------------------------------------------------------------------------------------------------------------------------------------
-// ---------------------------------------------- Method 1 of getCluster for main chains ---------------------------------------------------------
-// ----------------------------------------------------------------------------------------------------------------------------------------------
-
-
-/**
- * Get a long path from sources using a DFS. The path isn't the longest if there some undirected cycle.
- * @param network 
- * @param sources to use for DFS
- * @returns some node clusters with id
- */
-export function getLongPathDFS(network:Network,sources:string[]):{[key:string]:{nodes:Array<string>, height:number}}{ 
-    //console.log('DFS long path');
-    // create graph for library from network 
-    const graph=networkToGDSGraph(network);  
-    // DFS
-    const dfs=DFSWithSources(network, sources);
-    // get new clusters : 'longest' paths from sources with DFS
-    return longestPathFromDFS(graph,dfs,sources as string[]);
-}
-
-
-/**
- * The 'longest' (not the longest if undirected or directed cycle) path associated with each source with the DFS is found. BEWARE : the order of the id can change the result.
- * @param graph object for graph-data-structure library
- * @param dfs the return string (of nodes id) of a dfs (logically obtained with same sources as the sources for this functions)
- * @param sources array of node ID or a type of method to get sources automatically
- * RANK_ONLY : sources are nodes of rank 0
- * SOURCE_ONLY : sources are topological sources of the network (nul indegree)
- * RANK_SOURCE : sources are node of rank 0, then source nodes
- * ALL : sources are all nodes
- * SOURCE_ALL : sources are topological sources, then all the others nodes
- * RANK_SOURCE_ALL : sources are node of rank 0, then topological sources, then all the other nodes
- * For this function, the advised choice is either RANK_ONLY, SOURCE_ONLY or RANK_SOURCE.
- * 
- * @returns an object for the different path, the key is the source of the path
- */
-function longestPathFromDFS(graph:{[key:string]:Function},DFSreversed:Array<string>,sources:Array<string>):{[key:string]:{nodes:Array<string>, height:number}}{
-    let dfs = Array.from(DFSreversed).reverse(); // the code has been done whith a backward reading of dfs
-
-    let longestPaths:{[key:string]:{nodes:Array<string>, height:number}}={};
-    let path:Array<string>;
-    let source:string=undefined;
-    let i=dfs.length-1; // index of node in dfs array
-    let add=false;
-
-    while( i !== -1 ){ // dfs nodes are read backwards
-        const visitedNode=dfs[i];
-        const previousNode: string = (dfs.length - 1 >= i + 1) ? dfs[i + 1] : undefined;
-        
-        // if visited node is source
-        if( sources.includes(visitedNode) ){
-            
-            if (source!==undefined){
-                // end the path (of the previous source, if one)
-                longestPaths=endPath(source,longestPaths,path);
-                // suppress nodes after the current node (those already analysed in while loop, because backward reading)
-                dfs.splice(i + 1);
-            }
-
-            // define new source and add to path
-            source = visitedNode;
-            longestPaths[source]={nodes:[source],height:1};
-            add=true;
-            path=[source];
-        
-        // if there is a previous node
-        } else if (previousNode){
-            // if visited node is child of the previous visited node in dfs : add to path
-            if (nodeIsChildOf(graph,visitedNode,previousNode)){       
-                add=true;
-                path.push(visitedNode);
-            }else{
-                // end the path if a node has been added in the last pass of the loop
-                if (add && source!==undefined){
-                    longestPaths=endPath(source,longestPaths,path);
-                }
-                // remove previous visited node if this node is not the parent of current node
-                dfs.splice(i+1,1);
-                path.splice(path.length-1);
-                i++; // next loop pass will be on the same visited node (because the parent of the visited node wasn't found)
-                add=false; // no node has been added
-            }
-        }
-
-        i--; //backward reading
-    }
-    return longestPaths;
-}
-
-/**
- * Is the node a child of the parent node ?
- * @param graph object that contains function to get children of a node
- * @param node is this node a child of the parent?
- * @param parentNode the parent node
- * @returns boolean
- */
-function nodeIsChildOf(graph:{[key:string]:Function},node:string, parentNode:string):boolean{
-    return graph.adjacent(parentNode).includes(node);
-}
-
-/**
- * Check if the given path is longer than the one in the longest, if it is : update the path to keep the longest of the two
- * @param source source of the path (first node)
- * @param longestPaths 
- * @param path the path to check
- * @returns the new longest paths
- */
-function endPath(source:string, longestPaths:{[key:string]:{nodes:Array<string>, height:number}},path:Array<string>):{[key:string]:{nodes:Array<string>, height:number}}{
-    if (source in longestPaths){
-        if(longestPaths[source].height < path.length){
-            longestPaths[source]={nodes:path.slice(),height:path.length};
-        }
-    }else{
-        console.error("source key not in object")
-    }
-    return longestPaths;
-}
+/*******************************************************************************************************************************************************/
+//___________________________________________________1.  Method for main chains_________________________________________________________________________
 
-// ----------------------------------------------------------------------------------------------------------------------------------------------
-// ---------------------------------------------- Method 2 of getCluster for main chains ---------------------------------------------------------
-// ----------------------------------------------------------------------------------------------------------------------------------------------
 
 /**
  * Return the longest paths from source nodes for the source DAG. "Source DAG" doesn't mean the graph is a DAG, but the subraph containing all descendant of the source is a DAG.
@@ -242,37 +158,35 @@ function endPath(source:string, longestPaths:{[key:string]:{nodes:Array<string>,
  * ALL : all path
  * @returns some node clusters with id
  */
-export function getPathSourcesToTargetNode(network:Network, sources:string[],merge:boolean=true,pathType:PathType=PathType.ALL_LONGEST):{[key:string]:{nodes:Array<string>, height:number}}{
-
-    //console.log('DAG_Dijkstra');
+export async function getPathSourcesToTargetNode(network:Network, sources:string[],merge:boolean=true,pathType:PathType=PathType.ALL_LONGEST):Promise<{[key:string]:{nodes:Array<string>, height:number}}>{
 
     let pathsFromSources:{[key:string]:{nodes:Array<string>, height:number}}={};
 
     // for each source : do an independant dfs
-    sources.forEach(source=>{
+    for (const source of sources){
         // DFS to get a DAG from this source, and get topological sort
-        const {dfs,graph}=DFSsourceDAG(network,[source]);
+        const {dfs,graph}=await DFSsourceDAG(network,[source]);
         // get max distance from source node for all nodes, and by which parent nodes the node had been accessed
-        const {distances, parents}=DistanceFromSourceDAG(graph,dfs,pathType);
+        const {distances, parents}=await DistanceFromSourceDAG(graph,dfs,pathType);
         // get the farthest node from source (node with max distance)
         const targetNodes=findMaxKeys(distances);
         // for each target node : (if several path wanted)
         if (pathType==PathType.ALL_LONGEST || pathType==PathType.ALL){
-            targetNodes.key.forEach(target => {
+            for (const target of targetNodes.key){
                 // get the parents that goes from source to target node 
                 const nodesBetweenSourceTarget=BFS(parents,target);
                 // merge with an existing path if node in common
                 // height is the max distance +1 
-                pathsFromSources=mergeNewPath(source,{nodes:nodesBetweenSourceTarget, height:targetNodes.max+1},pathsFromSources,merge);
-            });
+                pathsFromSources=await mergeNewPath(source,{nodes:nodesBetweenSourceTarget, height:targetNodes.max+1},pathsFromSources,merge);
+            };
         } else  if(pathType==PathType.LONGEST){ // if only one path wanted : take the first
             // get the parents that goes from source to target node 
             const nodesBetweenSourceTarget=BFS(parents,targetNodes.key[0]);
             // merge with an existing path if node in common
-            pathsFromSources=mergeNewPath(source,{nodes:nodesBetweenSourceTarget, height:targetNodes.max},pathsFromSources,merge);
+            pathsFromSources= await mergeNewPath(source,{nodes:nodesBetweenSourceTarget, height:targetNodes.max+1},pathsFromSources,merge);
         }
             
-    });      
+    }    
     return pathsFromSources;
 }
 
@@ -286,7 +200,7 @@ export function getPathSourcesToTargetNode(network:Network, sources:string[],mer
  * ALL : add all parents
  * @returns maximal distance to the source and parents nodes for each nodes
  */
-function DistanceFromSourceDAG(graph:{[key:string]:Function}, topologicalOrderFromSource:string[],pathType:PathType=PathType.ALL_LONGEST):{distances:{[key:string]:number}, parents:{[key:string]:string[]}} {
+async function DistanceFromSourceDAG(graph:{[key:string]:Function}, topologicalOrderFromSource:string[],pathType:PathType=PathType.ALL_LONGEST):Promise<{distances:{[key:string]:number}, parents:{[key:string]:string[]}}> {
 
     // the source is the first node in the topological order
     const source=topologicalOrderFromSource[0];
@@ -302,7 +216,7 @@ function DistanceFromSourceDAG(graph:{[key:string]:Function}, topologicalOrderFr
     // Process node in topological order
     topologicalOrderFromSource.forEach(parent=> {
         // For each children
-        graph.adjacent(parent).sort().forEach( child => {
+        graph.adjacent(parent).sort().forEach( (child:string) => {
             const childDistance= distanceFromSource[child];
             const newDistance=distanceFromSource[parent] + graph.getEdgeWeight(parent,child);
             if ( newDistance > childDistance) {
@@ -326,7 +240,6 @@ function DistanceFromSourceDAG(graph:{[key:string]:Function}, topologicalOrderFr
             }
         })
     });
-
     return {distances:distanceFromSource, parents:parentsFromSource};
 }
 
@@ -335,8 +248,8 @@ function DistanceFromSourceDAG(graph:{[key:string]:Function}, topologicalOrderFr
  * @param obj The object containing key-value pairs.
  * @returns The keys associated with the maximum value (or undefined if the object is empty) and the max value.
  */
-function findMaxKeys(obj: { [key: string]: number }): {key:string[]|undefined,max:number} {
-    let maxKeys: string[] | undefined;
+function findMaxKeys(obj: { [key: string]: number }): {key:string[],max:number} {
+    let maxKeys: string[]=[];
     let maxValue = -Infinity;
     Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
     .forEach(([key, value]) => {
@@ -361,31 +274,54 @@ function findMaxKeys(obj: { [key: string]: number }): {key:string[]|undefined,ma
  * @param [merge=true] if true, merge the path with an existing one if a common node is found
  * @returns all paths including the new one
  */
-function mergeNewPath(source:string,newPath:{nodes:Array<string>, height:number},pathsFromSources:{[key:string]:{nodes:Array<string>, height:number}}, merge:boolean=true):{[key:string]:{nodes:Array<string>, height:number}}{
+async function mergeNewPath(source:string,newPath:{nodes:Array<string>, height:number},pathsFromSources:{[key:string]:{nodes:Array<string>, height:number}}, merge:boolean=true):Promise<{[key:string]:{nodes:Array<string>, height:number}}>{
     const keys=Object.keys(pathsFromSources).sort();
-    let hasmerged=false;
-    if (merge) {
-        keys.forEach(key=>{
-            const pathNodes = pathsFromSources[key].nodes;
-            // Check for common nodes, but target nodes
-            const commonNodes = pathNodes.find(node => newPath.nodes.includes(node));
-            if (commonNodes) {
+    let processed:boolean=false;
+
+    // if no path in the object : add the new path
+    if (keys.length===0){
+        pathsFromSources[source]=newPath;
+        return pathsFromSources;
+    }
+
+    // for each path in the object
+    keys.forEach(key=>{
+        const pathNodes = pathsFromSources[key].nodes;
+        // Check for common nodes
+        const commonNodes = pathNodes.find(node => newPath.nodes.includes(node));
+        if (commonNodes && commonNodes.length>0){
+            processed=true;
+            if(merge){
                 // Merge paths
                 const mergedPath = Array.from(new Set(pathNodes.concat(newPath.nodes)));
-                // Create new key
-                const newKey = `${key}__${source}`;
+                // Create new key if necessary
+                let newKey:string = key;
+                const sourceAlreadyInKey=key.split('__').includes(source);
+                if(!sourceAlreadyInKey){ 
+                    newKey= `${key}__${source}`;
+                }
                 // Update pathsFromSources object
                 const newheight=newPath.height>pathsFromSources[key].height?newPath.height:pathsFromSources[key].height;
                 pathsFromSources[newKey] = {nodes:mergedPath,height:newheight};
-                // Remove old key
-                delete pathsFromSources[key];
-                hasmerged=true;
+                
+                // Remove old key if the name has changed
+                if(!sourceAlreadyInKey){
+                    delete pathsFromSources[key];
+                }
+            }else{
+                // Highest path is kept
+                if(newPath.height>pathsFromSources[key].height){
+                    delete pathsFromSources[key];
+                    pathsFromSources[source]=newPath;
+                }
             }
-        });
-    }
-    // if no merge : added on it's own
-    if (!hasmerged){
+        }
+    });
+    if (!processed){
+        // If no common nodes : path added on it's own
+        if(pathsFromSources[source]) throw new Error('source already in pathsFromSources, but supposed to have no common nodes');
         pathsFromSources[source]=newPath;
     }
     return pathsFromSources;
-}
\ No newline at end of file
+}
+
diff --git a/src/composables/LayoutManageSideCompounds.ts b/src/composables/LayoutManageSideCompounds.ts
index 34416e9897899500b434cf1dca9b89d95bc66f2d..680cdbb1b19cb654f64ba373ea5a1ec406bc33f1 100644
--- a/src/composables/LayoutManageSideCompounds.ts
+++ b/src/composables/LayoutManageSideCompounds.ts
@@ -1,23 +1,14 @@
 // Type imports
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Node } from "@metabohub/viz-core/src/types/Node";
+import { Node } from  "../types/TypeVizCore";
 import { MetaboliteType, Reaction, ReactionInterval } from "../types/Reaction";
 import { VizArgs } from "../types/EnumArgs";
 import { Coordinate } from "../types/CoordinatesSize";
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
 
 // Composable imports
-import { removeAllSelectedNodes , duplicateAllNodesByAttribut} from "@metabohub/viz-core";
-import { getMeanNodesSizePixel, inchesToPixels, minEdgeLength as minEdgeLength, pixelsToInches } from "./CalculateSize";
-import { sideCompoundAttribute,isDuplicate, isReaction, isSideCompound, setAsSideCompound } from "./GetSetAttributsNodes";
-
-// General imports
-//import { e, S } from "vitest/dist/reporters-1evA5lom";
-import { c } from "vite/dist/node/types.d-aGj9QkWt";
-import { resolve } from "path";
-import { error } from "console";
-
+import { removeAllSelectedNodes , duplicateAllNodesByAttribut} from "./VizCoreFunctions";
+import { getMeanNodesSizePixel, inchesToPixels, minEdgeLength, pixelsToInches } from "./CalculateSize";
+import { sideCompoundAttribute,isDuplicate, isSideCompound, setAsSideCompound } from "./GetSetAttributsNodes";
 
 
 
@@ -25,28 +16,11 @@ import { error } from "console";
 /*******************************************************************************************************************************************************
  * 
  * This file contains the functions to manage side compounds.
- * 
- * *********************************
- * 
- * 0. Declare side compounds
- * 
- * -> addSideCompoundAttributeFromList :
- *       add attribute of side compound to all nodes in network from a list
- * 
- * -> getIDSideCompoundsInNetworkFromFile :
- *      return the list of id of side compounds in the network
- * 
- * -> getIDSideCompoundsFromFile :
- *      return the list of id of side compounds from a file
- * 
- * -> getContentFromURL :
- *     fetch url to return data
- * 
- * 
+ *  
  **********************************
  * 
  * 
- * 1. Duplicate and remove side compounds
+ * 0. Duplicate and remove side compounds
  * 
  * 
  * -> putDuplicatedSideCompoundAside :
@@ -65,7 +39,7 @@ import { error } from "console";
  * *********************************
  * 
  * 
- * 2. Reinsert side compounds
+ * 1. Reinsert side compounds
  * 
  * -> reinsertionSideCompounds :
  *      reinsert side compounds in the network
@@ -79,6 +53,8 @@ import { error } from "console";
  * -> motifStampSideCompound :
  *       applies a motif stamp to place side compound for a reaction
  * 
+ ****** 1.0  Stamp motif : initialization
+ * 
  * -> initializeReactionSideCompounds :
  *      initializes a reaction for side compounds motif
  * 
@@ -88,6 +64,8 @@ import { error } from "console";
  * -> angleRadianSegment :
  *      calculates the angle in radians between two points
  * 
+ * ***** 1.1  Stamp motif : find intervals
+ * 
  * -> addSideCompoundsIntervals :
  *      finds the cofactor intervals for a given reaction
  * 
@@ -103,9 +81,13 @@ import { error } from "console";
  * -> sizeInterval :
  *      calculates the size of an interval (between two metabolites) in a reaction
  * 
+ * ***** 1.2  Stamp motif : find spacing
+ * 
  * -> findSpacingSideCompounds :
  *      finds spacing between side compounds
  * 
+ * ***** 1.3  Stamp motif : give coordinates
+ * 
  * -> giveCoordAllSideCompounds :
  *      calculates the coordinates for all side compounds in a subgraph network
  * 
@@ -121,6 +103,7 @@ import { error } from "console";
  * -> giveCoordSideCompound :
  *     calculates the coordinates for a side compound 
  * 
+ * ***** 1.4  Stamp motif : insert side compounds
  * -> insertAllSideCompoundsInNetwork :
  *      inserts all side compounds in the network
  * 
@@ -128,93 +111,13 @@ import { error } from "console";
  *      inserts a side compound in the network
  * 
  * 
- * 
  *      
  * *******************************************************************************************************************************************************/
 
 
 
-
-
 /*******************************************************************************************************************************************************/
-//___________________________________________________0.  Declare side compounds__________________________________________________________________________
-
-
-
-
-/**
- * Add attribute of side compound to all nodes in network from a list 
- * @param network 
- * @param pathListSideCompounds path to the list of id of side compounds
- */
-export async function addSideCompoundAttributeFromList(subgraphNetwork:SubgraphNetwork, pathListSideCompounds:string):Promise<void>{
-    const listIDSideCompounds = await getIDSideCompoundsInNetworkFromFile(subgraphNetwork,pathListSideCompounds);
-    listIDSideCompounds.forEach((sideCompoundID) => {
-        setAsSideCompound(subgraphNetwork.network,sideCompoundID);
-    });
-}
-
-/**
- * Return the list of id of side compounds in the network
- * @param subgraphNetwork 
- * @param pathListSideCompounds path to the list of id of side compounds
- * @returns list of id of side compounds in the network
- */
-async function getIDSideCompoundsInNetworkFromFile(subgraphNetwork:SubgraphNetwork,pathListSideCompounds:string):Promise<string[]>{
-    let listIDSideCompounds:string[];
-    const network = subgraphNetwork.network;
-    try {
-        listIDSideCompounds = await getIDSideCompoundsFromFile(pathListSideCompounds);
-        const sideCompoundInNetwork = Object.keys(network.nodes).filter(id => listIDSideCompounds.includes(id));
-        return sideCompoundInNetwork;
-    } catch (error) {
-        throw error;
-    }   
-}
-
-/**
- * Return the list of id of side compounds from a file
- * @param pathListSideCompounds path to the list of id of side compounds
- * @returns list of id of side compounds
- */
-async function getIDSideCompoundsFromFile(pathListSideCompounds:string):Promise<string[]>{
-    try {
-        const sideCompoundsFile=pathListSideCompounds;
-        const sideCompoundsString = await getContentFromURL(sideCompoundsFile);
-        const lines = sideCompoundsString.split('\n');
-        const listId: Array<string> = [];
-        lines.forEach((line: string) => {
-        listId.push(line.split('\t')[0]);
-        })
-        return listId;
-    }catch (error) {
-        throw error;
-    }
-}
-
-/**
- * Fetch url to return data
- * @param url URL to fetch 
- * @returns Return response
- */
-export async function getContentFromURL(url: string): Promise<string> {
-	try {
-	  const response = await fetch(url);
-	  if (!response.ok) {
-		throw new Error('La requête a échoué avec le statut ' + response.status);
-	  }
-	  const content = await response.text();
-	  return content;
-	} catch (error) {
-	  console.error('Une erreur s\'est produite lors de la récupération du contenu du fichier :', error);
-	  throw error;
-	}
-  }
-  
-
-
-/*******************************************************************************************************************************************************/
-//___________________________________________________1.  Duplicate and remove side compounds__________________________________________________________________________
+//___________________________________________________0.  Duplicate and remove side compounds__________________________________________________________________________
 
 
 
@@ -227,16 +130,13 @@ export async function getContentFromURL(url: string): Promise<string> {
  * @param pathListSideCompounds path to the list of id of side compounds
  * @returns subgraphNetwork with updated network and sideCompounds
  */
-export async function putDuplicatedSideCompoundAside(subgraphNetwork:SubgraphNetwork, doDuplicateSideCompounds:boolean,doPutAsideSideCompounds:boolean, addSideCompoundAttribute:boolean=true, pathListSideCompounds:string):Promise<SubgraphNetwork>{
+export async function putDuplicatedSideCompoundAside(subgraphNetwork:SubgraphNetwork, doDuplicateSideCompounds:boolean,doPutAsideSideCompounds:boolean):Promise<SubgraphNetwork>{
     try {
-        // finding side compounds in network
-        if (addSideCompoundAttribute){
-            await addSideCompoundAttributeFromList(subgraphNetwork,pathListSideCompounds);
-        }
         // duplication of side compounds
         if (doDuplicateSideCompounds){
             await duplicateSideCompound(subgraphNetwork);
         }
+
         // remove side compounds from network, they are keeped aside in subgraphNetwork.sideCompounds
         if (doPutAsideSideCompounds){
             return removeSideCompoundsFromNetwork(subgraphNetwork);
@@ -255,7 +155,7 @@ export async function putDuplicatedSideCompoundAside(subgraphNetwork:SubgraphNet
  * @param subgraphNetwork - The subgraph network containing the side compounds to be duplicated.
  * @returns void
  */
-export async function duplicateSideCompound(subgraphNetwork:SubgraphNetwork):Promise<void>{
+async function duplicateSideCompound(subgraphNetwork:SubgraphNetwork):Promise<void>{
     const network = subgraphNetwork.network;
     const networkStyle = subgraphNetwork.networkStyle;
     // duplication of side compounds
@@ -321,7 +221,7 @@ function removeSideCompoundsFromNetwork(subgraphNetwork:SubgraphNetwork): Subgra
 }
 
 /*******************************************************************************************************************************************************/
-//___________________________________________________2.  Reinsert side compounds__________________________________________________________________________
+//___________________________________________________1.  Reinsert side compounds__________________________________________________________________________
 
 
 /**
@@ -332,7 +232,9 @@ function removeSideCompoundsFromNetwork(subgraphNetwork:SubgraphNetwork): Subgra
  * @returns subgraphNetwork with updated network and sideCompounds
  */
 export async function reinsertionSideCompounds(subgraphNetwork:SubgraphNetwork,factorMinEdgeLength:number=1/2,doReactionReversible:boolean):Promise<SubgraphNetwork>{
-    if(subgraphNetwork.sideCompounds){
+    if(!subgraphNetwork.sideCompounds){
+        return subgraphNetwork;
+    }else {
         const sideCompounds = subgraphNetwork.sideCompounds 
         // get information for length of edge for side compounds :
         // get the min length of edge in the network (if not, use default value)
@@ -352,12 +254,13 @@ export async function reinsertionSideCompounds(subgraphNetwork:SubgraphNetwork,f
         }
 
         // for each reaction, apply motif stamp
-        Object.keys(sideCompounds).forEach( async (reactionID)=>{
-            subgraphNetwork= await motifStampSideCompound(subgraphNetwork,reactionID,factorMinEdgeLength);
-        });       
-    }
-    return subgraphNetwork;
+        for (const reactionID of Object.keys(sideCompounds)) {
+            subgraphNetwork = await motifStampSideCompound(subgraphNetwork, reactionID, factorMinEdgeLength);
+        }   
 
+        return subgraphNetwork;   
+    }
+    
 }
 
 /**
@@ -391,7 +294,7 @@ async function updateSideCompoundsReversibleReaction(subgraphNetwork:SubgraphNet
     if (subgraphNetwork.sideCompounds){
         let sideCompounds=subgraphNetwork.sideCompounds;
         Object.keys(sideCompounds).forEach((reactionID)=>{
-            if (!(reactionID in network.nodes)) throw new Error("Reaction not in subgraphNetwork")
+            if (!(reactionID in network.nodes)) throw new Error("Reaction in side compounds but not in network")
             // if reaction has been reversed : exchange products and reactants
             if(network.nodes[reactionID].metadataLayout && network.nodes[reactionID].metadataLayout.isReversedVersion){
                 const products=sideCompounds[reactionID].products;
@@ -414,7 +317,7 @@ async function updateSideCompoundsReversibleReaction(subgraphNetwork:SubgraphNet
  * @returns The subgraphNetwork with the motif stamp applied for the reaction.
  */
 async function motifStampSideCompound(subgraphNetwork:SubgraphNetwork,reactionID:string,factorMinEdgeLength:number=1/2):Promise<SubgraphNetwork>{
-    //try {
+    try {
         // initialize reaction stamp
         let reaction= await initializeReactionSideCompounds(subgraphNetwork,reactionID);
         // find intervals between reactants and products
@@ -430,15 +333,15 @@ async function motifStampSideCompound(subgraphNetwork:SubgraphNetwork,reactionID
         subgraphNetwork= await giveCoordAllSideCompounds(subgraphNetwork,reaction,factorMinEdgeLength);
         // insert side compounds in network
         insertAllSideCompoundsInNetwork(subgraphNetwork,reaction);
-    // } catch (error) {
-    //     throw new Error("Error in motifStampSideCompound, reaction : "+ reactionID+ "\n"+error);
-    // }
-    return subgraphNetwork;
+        return subgraphNetwork;
+    } catch (error) {
+        throw new Error("Error in motifStampSideCompound, reaction : "+ reactionID+ "\n"+error);
+    }
 }
 
 
 /***********************************************************************/
-//______________2.1  Stamp motif : initialization
+//______________1.0  Stamp motif : initialization
 
 
 /**
@@ -514,7 +417,6 @@ async function getMetaboliteFromReaction(subgraphNetwork: SubgraphNetwork, idRea
  */
 function angleRadianSegment(x1:number,y1:number,x2:number,y2:number,clockwise:boolean=true):number{
     if (!isFinite(x1) || !isFinite(y1) || !isFinite(x2) || !isFinite(y2)) {
-        //console.error("Invalid coordinates for angle : one or more coordinates are not finite numbers.");
         throw new Error("Invalid coordinates for angle : one or more coordinates are not finite numbers."); 
     }
 
@@ -535,7 +437,7 @@ function angleRadianSegment(x1:number,y1:number,x2:number,y2:number,clockwise:bo
 }
 
 /***********************************************************************/
-//______________2.2  Stamp motif : find intervals
+//______________1.1  Stamp motif : find intervals
 
 
 
@@ -548,6 +450,16 @@ function angleRadianSegment(x1:number,y1:number,x2:number,y2:number,clockwise:bo
 async function addSideCompoundsIntervals(reaction: Reaction):Promise<Reaction> {
 
     try {
+        // if no reactant or product
+        if (Object.keys(reaction.metabolitesAngles).length===0) {
+            reaction.intervalsAvailables =[{
+                typeInterval: 0, 
+                reactant: undefined,
+                product: undefined,
+            }];
+            return reaction;
+        }
+
         // Sort metabolites by angle
         const sortedMetabolites = Object.entries(reaction.metabolitesAngles)
         .map(([id, {angle, type}]) => ({id, angle, type}))
@@ -582,7 +494,6 @@ async function addSideCompoundsIntervals(reaction: Reaction):Promise<Reaction> {
                 product: undefined,
             }];
         }
-
         return reaction;
     } catch(error){
         throw error;
@@ -703,20 +614,21 @@ function sizeInterval(reaction:Reaction,intervalIndex:number):number{
 }
 
 /***********************************************************************/
-//______________2.3  Stamp motif : find spacing
+//______________1.2  Stamp motif : find spacing
 
 
 async function findSpacingSideCompounds(reaction:Reaction,sizeInterval:number):Promise<{reactant:number|undefined,product:number|undefined}>{
     const reactantNumber=reaction.sideCompoundsReactants.length;
     const productNumber=reaction.sideCompoundsProducts.length;
+    if (reactantNumber<0 && productNumber<0) throw new Error("Number of side compounds negative");
     return {
-        reactant: reactantNumber === 0 ? undefined : sizeInterval / (2 * (reactantNumber+1)),
-        product: productNumber === 0 ? undefined : sizeInterval / (2 * (productNumber+1))
+        reactant: reactantNumber === 0 ? undefined :  Number((sizeInterval / (2 * (reactantNumber+1))).toFixed(3)),
+        product: productNumber === 0 ? undefined : Number((sizeInterval / (2 * (productNumber+1))).toFixed(3))
     };
 }
 
 /***********************************************************************/
-//______________2.4  Stamp motif : give coordinates
+//______________1.3  Stamp motif : give coordinates
 
 
 /**
@@ -780,15 +692,14 @@ async function placeSideCompounds(sideCompounds: Array<Node>, reaction: Reaction
     }
     const interval = reaction.intervalsAvailables[0];
     const startSideCompound = placeReactants ? interval.reactant : interval.product;
-    if (!startSideCompound) {
-        console.error("No start side compound found");
-        return;
+    let startAngle:number=0;
+    if (startSideCompound) {
+        startAngle = startSideCompound ? reaction.metabolitesAngles[startSideCompound].angle : 0;
     }
-    let startAngle = startSideCompound ? reaction.metabolitesAngles[startSideCompound].angle : 0;
+   
     const angleSpacing = placeReactants ? reaction.angleSpacingReactant : reaction.angleSpacingProduct;
-    if (! angleSpacing! || isFinite(angleSpacing)) {
-        console.error("No angle spacing found");
-        return;
+    if (angleSpacing===undefined || isNaN(angleSpacing) || angleSpacing===null){ 
+        throw new Error("No angle spacing found");
     }
 
     sideCompounds.forEach((sideCompoundNode, i) => {
@@ -826,14 +737,14 @@ function determineDirection(typeInterval: number, placeReactants: boolean): numb
  * @returns The side compound node with updated coordinates.
  */
 function giveCoordSideCompound(sideCompound:Node,angle:number,center:{x:number,y:number},distance:number):Node{
-    sideCompound.x = center.x + distance * Math.cos(angle);
-    sideCompound.y = center.y + distance * Math.sin(angle);
+    sideCompound.x = Number((center.x + distance * Math.cos(angle)).toFixed(3));
+    sideCompound.y = Number((center.y + distance * Math.sin(angle)).toFixed(3));
     return sideCompound;
 }
 
 
 /***********************************************************************/
-//______________2.4  Stamp motif : insertion in network
+//______________1.4  Stamp motif : insertion in network
 
 
 /**
diff --git a/src/composables/LayoutReversibleReactions.ts b/src/composables/LayoutReversibleReactions.ts
index ba48db017ef5d36dbabc338c9577d1c9b7a8d5d6..cc326725ede5fff81d54773965e117514b5bc114 100644
--- a/src/composables/LayoutReversibleReactions.ts
+++ b/src/composables/LayoutReversibleReactions.ts
@@ -1,20 +1,15 @@
 // Type imports
-import { Link } from "@metabohub/viz-core/src/types/Link";
-import { Node } from "@metabohub/viz-core/src/types/Node";
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Link, Node, Network } from  "../types/TypeVizCore";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
 import { TypeSubgraph } from "../types/Subgraph";
 import { StartNodesType } from "../types/EnumArgs";
 import { NetworkLayout, NodeLayout } from "../types/NetworkLayout";
 
 // Composable imports
-import { removeAllSelectedNodes } from "@metabohub/viz-core";
+import { removeAllSelectedNodes } from "./VizCoreFunctions";
 import { BFSWithSources } from "./AlgorithmBFS";
-import { addLinkClassReversible, addMetadataReversibleWithClass, isReaction, isReversible } from "./GetSetAttributsNodes";
+import { addLinkClassReversible, isReaction, isReversible } from "./GetSetAttributsNodes";
 
-// General imports
-//import { e } from "vitest/dist/reporters-1evA5lom";
-import { l } from "vite/dist/node/types.d-aGj9QkWt";
 
 
 
@@ -76,19 +71,16 @@ import { l } from "vite/dist/node/types.d-aGj9QkWt";
 export async function duplicateReversibleReactions(networkLayout:NetworkLayout):Promise<void> {
 
   try {
-    // add metadata "reversible" to nodes
-    console.warn("add metadata reversible if in the class. Not necessary if file with correct format (done bcs of the file I have) => to remove ?");
-    await addMetadataReversibleWithClass(networkLayout);
 
     const newLinks: Array<Link> = []; //links associated with new reactions nodes
-
-    console.warn("metadata en double pr old et new attr")
     
     await Promise.all(networkLayout.links.map(async (link) => {
       // If the link is reversible: get the reaction node and duplicate it
       const linkReversible = linkIsReversible(networkLayout, link);
-  
       if (linkReversible.isReversible) {
+        // add class reversible to the link
+        link= addLinkClassReversible(link);
+
         // Duplication of the reaction node
         let nodeToDuplicate: NodeLayout;
         const reactionIsSource: boolean|undefined = linkReversible.sourceIsReversible;
@@ -152,7 +144,7 @@ function linkIsReversible(network:Network,link: Link): {isReversible:boolean,sou
  * @param nodeReaction - The node reaction to duplicate.
  * @returns The duplicated node reaction with reversible metadata.
  */
-async function duplicateNodeReactionReversible(networkLayout: NetworkLayout, nodeReaction: Node): Promise<Node> {
+async function duplicateNodeReactionReversible(networkLayout: NetworkLayout, nodeReaction: NodeLayout): Promise<Node> {
     return new Promise(async (resolve, reject) => {
         try {
             // Duplicate target node
@@ -163,11 +155,11 @@ async function duplicateNodeReactionReversible(networkLayout: NetworkLayout, nod
             if (!networkLayout.nodes[nodeReaction.id]) {
                 throw new Error("Node not found to set as reversible");
             }
-            let metadataLayout=networkLayout.nodes[nodeReaction.id].metadataLayout;
-            if (!metadataLayout) {
-               metadataLayout={};
+
+            if (!nodeReaction.metadataLayout) {
+              nodeReaction.metadataLayout={};
             }
-            metadataLayout.reversibleNodeVersion = newId;
+            nodeReaction.metadataLayout.reversibleNodeVersion = newId;
 
             resolve(newReactionNode);
         } catch (error) {
@@ -195,25 +187,9 @@ async function reversibleNodeReaction(node: NodeLayout, suffix: string = "_rev")
     newNodeReversed=true;
   }
   
-  //const newLabel = label.endsWith(suffix) ? label.slice(0, -suffix.length) : label + suffix;
-
-  //const newClasses: string[] = [];
-  // add classes of original reaction, 
-  // and add class reversibleVersion if not present, removed if present
-  // classes.forEach(item =>{
-  //   newClasses.push(item)
-  // });
-  // const revIndex = newClasses.indexOf("reversibleVersion");
-  // if (revIndex !== -1) {
-  //   newClasses.splice(revIndex, 1);
-  // }else{
-  //   newClasses.push("reversibleVersion");
-  // }
-  
   const newNode: NodeLayout = {
     ...node,
     id: newId,
-    //metadata: {...node.metadata, reversibleVersion: id},  // to remove : doest work
     metadataLayout: {reversibleNodeVersion:id,isReversedVersion:newNodeReversed},
   };
   return newNode;
@@ -265,6 +241,7 @@ function reversibleLink(network:Network,link:Link,sourceID:string,targetID:strin
  * RANK_SOURCE_ALL : sources are node of rank 0, then topological sources, then all the other nodes
  * For this method, a source type with "all" is advised, to not miss any duplicated reaction.
  * @param nodeOrderFunction the method that return an array of nodes order (a same node can be present several time!), with sources as input
+ * @returns A promise of the subgraphNetwork with only one version of the duplicated reactions
  */
 export async function chooseReversibleReaction(
   subgraphNetwork:SubgraphNetwork,
@@ -276,7 +253,7 @@ export async function chooseReversibleReaction(
   // get node order
   nodeOrder = await nodeOrderFunction(network,sources);
   // keep the first node seen only, for duplicated nodes
-  subgraphNetwork=keepFirstReversibleNode(subgraphNetwork, nodeOrder) as SubgraphNetwork;
+  subgraphNetwork=keepFirstReversibleNode(subgraphNetwork, nodeOrder,true) as SubgraphNetwork;
 
   return subgraphNetwork;
 }
@@ -294,6 +271,7 @@ export function keepFirstReversibleNode(subgraphNetwork:SubgraphNetwork,nodeOrde
 
   for(let i=0;i<nodeOrder.length;i++){
     const nodeID=nodeOrder[i];
+    if (!(nodeID in network.nodes))  throw new Error("Error : the node "+nodeID+" is not found in the network.");
     // if there is a reversible version of the current node:
     if(network.nodes[nodeID].metadataLayout && network.nodes[nodeID].metadataLayout.reversibleNodeVersion){
       const reversibleNodeID=network.nodes[nodeID].metadataLayout.reversibleNodeVersion;
@@ -304,6 +282,9 @@ export function keepFirstReversibleNode(subgraphNetwork:SubgraphNetwork,nodeOrde
 
       // Put on list renaming of id if necessary :
       if(network.nodes[nodeID].metadataLayout.isReversedVersion){
+        if (network.nodes[reversibleNodeID].metadataLayout &&  network.nodes[reversibleNodeID].metadataLayout.isReversedVersion){
+          throw new Error("Both version of node have attribut reversibleNodeVersion as true");
+        }
         // the duplicated version is the one keeped, its id have to be renamed by the original id
         nodeToRename[nodeID]=reversibleNodeID;
       }else if (!network.nodes[reversibleNodeID].metadataLayout || !network.nodes[reversibleNodeID].metadataLayout.isReversedVersion){
@@ -320,7 +301,7 @@ export function keepFirstReversibleNode(subgraphNetwork:SubgraphNetwork,nodeOrde
   // remove one version of the reaction
   removeAllSelectedNodes(reactionToRemove,network);
   // rename the other if it was the reversible version that is keeped
-  console.warn("need to check if subgraph still exist ?")
+  
   if(doRename){
     const subgraphNetworkRename=renameAllIDNode(subgraphNetwork,nodeToRename);
     return  subgraphNetworkRename// return object renamed
@@ -329,7 +310,7 @@ export function keepFirstReversibleNode(subgraphNetwork:SubgraphNetwork,nodeOrde
 }
 
 /**
- * Renames the nodes and edges in a subgraph network based on the provided mapping.
+ * Renames the nodes and edges in a subgraph network based on the provided mapping {oldName:newName}.
  * 
  * @param subgraphNetwork - The subgraph network to modify.
  * @param nodesToRename - An object containing the mapping of old node IDs to new node IDs.
@@ -352,16 +333,6 @@ export function renameAllIDNode(subgraphNetwork:SubgraphNetwork,nodesToRename:{[
     }
   });
 
-  // Modify edges
-  network.links.forEach(link => {
-    if (nodesToRename[link.source.id]) {
-      link.source = network.nodes[nodesToRename[link.source.id]];
-    }
-    if (nodesToRename[link.target.id]) {
-      link.target = network.nodes[nodesToRename[link.target.id]];
-    }
-  });
-
   // Modify subgraphs
   // in cycles
   renameAllInSubgraph(subgraphNetwork,TypeSubgraph.CYCLE,nodesToRename);
@@ -382,77 +353,15 @@ export function renameAllIDNode(subgraphNetwork:SubgraphNetwork,nodesToRename:{[
  * @param typeSubgraph - The type of subgraph.
  * @param nodesToRename - The mapping of nodes to their new names.
  */
-function renameAllInSubgraph(subgraphNetwork:SubgraphNetwork, typeSubgraph:TypeSubgraph, nodesToRename:{[key: string]: string}){
+function renameAllInSubgraph(subgraphNetwork:SubgraphNetwork, typeSubgraph:TypeSubgraph, nodesToRename:{[key: string]: string}):void{
   const subgraphs = subgraphNetwork[typeSubgraph] ? subgraphNetwork[typeSubgraph] : {};
   Object.entries(subgraphs).forEach(([ID, subgraph]) => {
     subgraph.nodes = subgraph.nodes.map(node => {
       if(nodesToRename[node]){
-        // change metadata of node to know in which subgraph it is
-        //console.warn("pk fonction ici ?");
-        //updateNodeMetadataSubgraph(subgraphNetwork.network.value, nodesToRename[node], ID, typeSubgraph);
         // change the name of the node in the subgraph
         return nodesToRename[node];
       }
       return node;
     });
   });
-}
-
-
-
-
-// export function renameIDNode(subgraphNetwork:SubgraphNetwork,oldName:string,newName:string):SubgraphNetwork{
-//   const network = subgraphNetwork.network.value;
-//   if(oldName in network.nodes && !(newName in network.nodes)){
-
-//     // modify node :
-//     // insert new node with new name
-//     network.nodes[newName]=network.nodes[oldName];
-//     const newNode=network.nodes[newName];
-//     newNode.id=newName;
-//     // delete old node
-//     delete network.nodes[oldName];
-
-//     // modify edges :
-//     // when the node is source
-//     const linksOldNodeAsSource = Object.values(network.links).filter((link) => {
-//       return link.source.id === oldName;
-//     });
-//     linksOldNodeAsSource.forEach((link) => {
-//       link.source = newNode;
-//     });
-//     // when the node is target
-//     const linksOldNodeAsTarget = Object.values(network.links).filter((link) => {
-//       return link.target.id === oldName;
-//     });
-//     linksOldNodeAsTarget.forEach((link) => {
-//       link.target = newNode;
-//     });
-
-//     // modify subgraphs :
-//     // in cycles
-//     renameInSubgraph(subgraphNetwork,TypeSubgraph.CYCLE,oldName,newName);
-//     // in main chains
-//     renameInSubgraph(subgraphNetwork,TypeSubgraph.MAIN_CHAIN,oldName,newName);
-//     // in secondary chains
-//     renameInSubgraph(subgraphNetwork,TypeSubgraph.SECONDARY_CHAIN,oldName,newName);
-
-//     return subgraphNetwork;
-
-//   }else{
-//     console.log("Error : impossible to rename node "+oldName+" to "+newName+", node already exist or not found in network.");
-//   }
-// }
-
-// function renameInSubgraph(subgraphNetwork:SubgraphNetwork,typeSubgraph:TypeSubgraph,oldName:string,newName:string){
-//   const subgraphs = subgraphNetwork[typeSubgraph]? subgraphNetwork[typeSubgraph]:{};
-//     Object.entries(subgraphs).forEach(([ID,subgraph])=>{
-//       if(subgraph.nodes.includes(oldName)){
-//         // change the name of the node in the subgraph
-//         subgraph.nodes = subgraph.nodes.map(node => node === oldName ? newName : node);
-//         // change metadata of node to know in which subgraph it is
-//         updateNodeMetadataSubgraph(subgraphNetwork.network.value, newName, ID,typeSubgraph); //newName because the node has been renamed
-//       }
-//     });
-// }
-
+}
\ No newline at end of file
diff --git a/src/composables/LayoutSugiyama.ts b/src/composables/LayoutSugiyama.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f5cee69119f4e284f18064dad3218c97c65f1e8d
--- /dev/null
+++ b/src/composables/LayoutSugiyama.ts
@@ -0,0 +1,47 @@
+// Type imports
+import { JsonViz } from "../types/FormatJsonViz";
+import { SubgraphNetwork } from "../types/SubgraphNetwork";
+
+// Composable imports
+import { getSepAttributesInches } from "./CalculateSize";
+import {  networkToDOT } from './ConvertFromNetwork';
+import {  changeNetworkFromViz } from './ConvertToNetwork';
+
+// General imports
+import { instance } from "@viz-js/viz";
+
+
+/**
+ * This file contains functions to apply layout algorithms to a network.
+ * 
+ * -> vizLayout :
+ *      Take a network object and change the (x,y) position of the node with viz lib
+ * 
+ * -> forceLayout :
+ *      Applies the force-directed layout algorithm to the given network.
+ */
+
+
+
+/** 
+ * Take a network object and change the (x,y) position of the node with viz lib
+ * @param {Network}  Network object
+ * @param mainChains clusters for viz (type version for quick search)
+ * @param graphAttributes for viz dot layout (see https://graphviz.org/docs/layouts/dot/)
+ * @param assignRank indicates if rank and order need to be infered after layout is applied
+ * @param [callbackFunction=() => {}] function to do after the layout is done
+ */
+export async function vizLayout(subgraphNetwork:SubgraphNetwork,assignRank:boolean=false, cycle:boolean=true,addNodes:boolean=true,
+    groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false,printDot:boolean=false,dpi:number=72,factorLenghtEdge:number=3,callbackFunction = () => {}): Promise<SubgraphNetwork> {
+        await instance().then( async viz => {
+        // attributes for viz
+        const sep =await getSepAttributesInches(subgraphNetwork.network,subgraphNetwork.networkStyle,factorLenghtEdge);
+        subgraphNetwork.attributs={rankdir: "BT" , newrank:true, compound:true,splines:false,ranksep:sep.rankSep,nodesep:sep.nodeSep,dpi:dpi};
+        const dot=await networkToDOT(subgraphNetwork,cycle,addNodes,groupOrCluster,orderChange);
+        if(printDot) console.log(dot);
+        const json=viz.renderJSON(dot) as JsonViz;
+        subgraphNetwork= await changeNetworkFromViz(json,subgraphNetwork,assignRank);
+        callbackFunction();
+    });
+    return subgraphNetwork;
+}
diff --git a/src/composables/LayoutSugiyamaForce.ts b/src/composables/LayoutSugiyamaForce.ts
deleted file mode 100644
index 3d8725d23c53c3994749047bb152d53abf8b1e2d..0000000000000000000000000000000000000000
--- a/src/composables/LayoutSugiyamaForce.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-// Type imports
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-import { JsonViz } from "../types/FormatJsonViz";
-import { Subgraph } from "../types/Subgraph";
-import { SubgraphNetwork } from "../types/SubgraphNetwork";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-
-// Composable imports
-import { getSepAttributesInches } from "./CalculateSize";
-import {  networkToDOT } from './ConvertFromNetwork';
-import {  changeNetworkFromViz } from './ConvertToNetwork';
-
-// General imports
-//import * as d3 from 'd3';
-import { reactive } from "vue";
-// import cytoscape from 'cytoscape';
-// import fcose from 'cytoscape-fcose';
-// import cosebilkent from 'cytoscape-cose-bilkent';
-import dagre from 'dagrejs';
-import { Graph, instance } from "@viz-js/viz";
-
-
-/**
- * This file contains functions to apply layout algorithms to a network.
- * 
- * -> dagreLayout :
- *      Take a network object and change the (x,y) position of the node with dagre lib
- * 
- * -> vizLayout :
- *      Take a network object and change the (x,y) position of the node with viz lib
- * 
- * -> forceLayout :
- *      Applies the force-directed layout algorithm to the given network.
- */
-
-
-
-
-/** 
- * Take a network object and change the (x,y) position of the node with dagre lib
- * @param {Network}  Network object 
- * @param  graphAttributes for dagre layout (see https://github.com/dagrejs/dagre/wiki)
- * @param [callbackFunction=() => {}] function to do after the layout is done
- */
-// export function dagreLayout(network: Network,graphAttributes={},callbackFunction = () => {}):void {
-
-//     setTimeout(async function() {
-//         let graphDagre = networkToDagre(network,graphAttributes);
-//         dagre.layout(graphDagre);
-//         changeNetworkFromDagre(graphDagre, network).then(() => {
-//             callbackFunction();
-//         });
-//     }, 1);
-        
-// }
-
-
-/** 
- * Take a network object and change the (x,y) position of the node with viz lib
- * @param {Network}  Network object
- * @param mainChains clusters for viz (type version for quick search)
- * @param graphAttributes for viz dot layout (see https://graphviz.org/docs/layouts/dot/)
- * @param assignRank indicates if rank and order need to be infered after layout is applied
- * @param [callbackFunction=() => {}] function to do after the layout is done
- */
-export async function vizLayout(subgraphNetwork:SubgraphNetwork,assignRank:boolean=false, cycle:boolean=true,addNodes:boolean=true,
-    groupOrCluster:"group"|"cluster"="cluster",orderChange:boolean=false,printDot:boolean=false,dpi:number=72,factorLenghtEdge:number=3,callbackFunction = () => {}): Promise<SubgraphNetwork> {
-
-        await instance().then( async viz => {
-        // attributes for viz
-        const sep =await getSepAttributesInches(subgraphNetwork.network,subgraphNetwork.networkStyle,factorLenghtEdge);
-        subgraphNetwork.attributs={rankdir: "BT" , newrank:true, compound:true,splines:false,ranksep:sep.rankSep,nodesep:sep.nodeSep,dpi:dpi};
-        const dot=networkToDOT(subgraphNetwork,cycle,addNodes,groupOrCluster,orderChange);
-        if(printDot) console.log(dot);
-        const json=viz.renderJSON(dot) as JsonViz;
-        subgraphNetwork= await changeNetworkFromViz(json,subgraphNetwork,assignRank);
-        callbackFunction();
-    });
-    return subgraphNetwork;
-}
-
-
-
-/**
- * Applies the force-directed layout algorithm to the given network.
- * Use cytoscapes' cose-bilkent layout.
- * 
- * @param network - The network to apply the layout to.
- * @param networkStyle - The style properties of the network.
- * @param shiftCoord - Optional. Specifies whether to shift the coordinates of the nodes to the top-left corner. Defaults to false.
- * @returns A Promise that resolves to the updated network after applying the layout.
- */
-// export async function forceLayout(network: Network, networkStyle:GraphStyleProperties, shiftCoord: boolean = false): Promise<Network> {
-
-//     //cytoscape.use(fcose);
-//     cytoscape.use(cosebilkent);
-
-//     const size=await getMeanNodesSizePixel(Object.values(network.nodes), networkStyle,false);
-//     const edgeFactor=3;
-//     const edgeLength = Math.max(size.height, size.width) * edgeFactor;
-
-//     const layout ={
-//         name:"cose-bilkent", //fcose
-//         animate: false,
-//         randomize: false,
-//         idealEdgeLength: edgeLength,
-//         nodeRepulsion: 70000, // high number if randomize = false
-//         gravity : 0.001,
-//         numIter: 3000
-
-//     }
-
-//     let cyto = networkToCytoscape(network,true);
-
-//     await new Promise<void>((resolve) => {
-//         cyto.ready(function () {
-//             setTimeout(function () {
-//                 cyto.elements().layout(layout).run();
-//                 resolve();
-//             }, 5000);
-//         });
-//     });
-
-//     if (shiftCoord) {
-//         shiftAllToGetTopLeftCoord(network, networkStyle);
-//     }
-
-//     const json = cyto.json();
-//     changeNetworkFromCytoscape(json, network);
-
-//     return network;
-// }
-  
-  
-
-
-//   /**
-//    * Take a network and apply a d3 force layout algorithm on WITHOUT simulation
-//    * @param network Network object
-//    * @returns {Network} Network object with d3 force layout apply on
-//    */
-//   export async function forceLayout3(network: Network, autoRescale: Boolean = false): Promise<Network> {
-//     const seuil = 0.04;
-//     const maxiter = 1000;
-//     const minMovement = 0.01; 
-//     const listNodesID=Object.keys(network.nodes);
-//     let svgHeight = screen.height;
-//     let svgWidth = screen.width;
-
-//     const simulation = d3.forceSimulation(Object.values(network.nodes))
-//         .force('link', d3.forceLink()
-//             .id((d: any) => d.id)
-//             .links(network.links)
-//         )
-//         .force('charge', d3.forceManyBody())
-//         .velocityDecay(0.1)
-//         .force('center', d3.forceCenter(svgWidth / 2, svgHeight / 2))
-//         .stop();
-
-//     await sendTick();
-
-//     async function sendTick() {
-//         let iter=0;
-//         let lastPositions = new Map(Object.values(network.nodes).map(node => [node.id, { x: node.x, y: node.y }]));
-//         while (iter < maxiter) {
-//             iter++;
-//             simulation.tick();
-
-//             let maxMovement = 0;
-//             for (let nodeID of listNodesID) {
-//                 const node=network.nodes[nodeID];
-//                 const lastPos = lastPositions.get(nodeID);
-//                 const dx = node.x - lastPos.x;
-//                 const dy = node.y - lastPos.y;
-//                 const distance = Math.sqrt(dx * dx + dy * dy);
-
-//                 if (distance > maxMovement) {
-//                     maxMovement = distance;
-//                 }
-
-//                 lastPositions.set(node.id, { x: node.x, y: node.y });
-//             }
-
-//             if (maxMovement < minMovement) {
-//                 console.log('Force layout converged after ' + iter + ' iterations');
-//                 break;
-//             }else{
-//                 console.log(iter);
-//             }
-//         }
-//     }
-
-//     return network;
-// }
-
-  
-// export async function forceLayout2(network: Network, autoRescale: Boolean = false): Promise<Network> {
-//     let svgHeight = screen.height;
-//     let svgWidth = screen.width;
-  
-//     const simulation = d3.forceSimulation(Object.values(network.nodes))
-//       .force('link', d3.forceLink()
-//         .id((d: any) => {
-//           return d.id;
-//         })
-//         .links(network.links)
-//       )
-//       .force('charge', d3.forceManyBody())
-//       .force('center', d3.forceCenter(svgWidth / 2, svgHeight / 2))
-//       .alphaMin(0.4)
-//       .stop();
-  
-//     await sendTick();
-  
-//     async function sendTick() {
-//       for (let i = simulation.alpha(); i > 0.4; i = simulation.alpha()) {
-//         simulation.tick();
-//       }
-//     }
-  
-//     return network;
-  
-//   }
diff --git a/src/composables/MetricsApplication.ts b/src/composables/MetricsApplication.ts
deleted file mode 100644
index 7a6b41be71e258815596a333117dcd2e4b367701..0000000000000000000000000000000000000000
--- a/src/composables/MetricsApplication.ts
+++ /dev/null
@@ -1,329 +0,0 @@
-// import { ref } from "vue";
-// import { getContentFromURL, importNetworkFromURL } from "./importNetwork";
-// import { Network } from "@metabohub/viz-core/src/types/Network";
-// import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-// import { SubgraphNetwork } from "../types/SubgraphNetwork";
-// import { addSideCompoundAttributeFromList, duplicateSideCompound, putDuplicatedSideCompoundAside } from "./LayoutManageSideCompounds";
-// import { createStaticForceLayout } from "@metabohub/viz-core";
-// import { Parameters,defaultParameters } from "../types/Parameters";
-// import { forceLayout, vizLayout } from "./LayoutSugiyamaForce";
-// import { Algo, PathType } from "../types/EnumArgs";
-// import { countIntersectionEdgeNetwork, countOverlapNodeNetwork, countOverlapNodeEdgeNetwork, countDifferentCoordinatesNodeNetwork, countNodes, countEdges, coefficientOfVariationEdgeLength, analyseDirectorVector } from "./MetricsCalculation";
-// import { TypeSubgraph } from "../types/Subgraph";
-// import { networkToGDSGraph } from "./ConvertFromNetwork";
-// import { allSteps } from "./LayoutMain";
-
-
-// export async function analyseAllJSON(pathListJSON: string,algo:Algo=Algo.DEFAULT,metricGraph:boolean=true): Promise<void> {
-//     const jsonFileString = await getContentFromURL(pathListJSON);
-//     const allJson = jsonFileString.split('\n');
-//     let resultGraphAllJSON: Array<Array<number>> = [];
-//     let resultLayoutAllJSON: Array<Array<number>> = [];
-//     let nameMetrics:{graph:string[],layout:string[]}= {graph:[],layout:[]};
-//     let nameFile: string[] = [];
-
-//     // which layout to apply
-//     let applyLayout: (subgraph: SubgraphNetwork) => Promise<SubgraphNetwork> =defaultApplyLayout;
-//     switch (algo) {
-//         case Algo.FORCE:
-//             console.log('apply Force');
-//             applyLayout = applyForceLayout;
-//             console.warn('Use of timeout so exec time is not accurate (no comparison possible)');
-//             break;
-//         case Algo.VIZ:
-//             console.log('apply Viz');
-//             applyLayout = applyVizLayout;
-//             break;
-//         case Algo.ALGO:
-//             console.log('applyAlgo : default');
-//             applyLayout = applyAlgo;
-//             break;
-//         case Algo.ALGO_V0:
-//             console.log('applyAlgo_V0: no main chain');
-//             applyLayout = applyAlgo_V0;
-//             break;
-//         case Algo.ALGO_V1:
-//             console.log('applyAlgo_V1 : longuest');
-//             applyLayout = applyAlgo_V1;
-//             break;
-//         case Algo.ALGO_V3:
-//             console.log('applyAlgo_V3 : all');
-//             applyLayout = applyAlgo_V3;
-//             break;
-//         default:
-//             console.log('no change');
-//             applyLayout = defaultApplyLayout;
-//             break;
-//     }
-//     let firstJSON=true;
-//     for (const json of allJson) {
-//         console.log(json);
-//         const resultJSON= await analyseJSON(json,metricGraph,applyLayout,false);
-//         if (firstJSON){
-//             nameMetrics.graph=resultJSON.graph.nameMetrics;
-//             nameMetrics.layout=resultJSON.layout.nameMetrics;
-//             firstJSON=false;
-//         }
-//         if (resultJSON.graph !== undefined){
-//             resultGraphAllJSON.push(resultJSON.graph.result);
-//         }
-//         if (resultJSON.layout !== undefined){
-//             nameFile.push(json);
-//             resultLayoutAllJSON.push(resultJSON.layout.result);
-//         }
-       
-//     }  
-
-//     if (metricGraph){
-//         print1DArray(nameMetrics.graph);
-//         print2DArray(resultGraphAllJSON);
-//     }
-//     print1DArray(nameMetrics.layout);
-//     print2DArray(resultLayoutAllJSON);
-
-//     console.warn("If apply metrics on another layout : refresh the page, else results are the same than last time (idk why)");
-//     console.warn('Some metrics are calculated without side compounds');
-// }
-
-
-// async function analyseJSON(json: string, metricGraph:boolean=true, applyLayout: (subgraph: SubgraphNetwork) => Promise<SubgraphNetwork> =defaultApplyLayout,printColumnName:boolean=true):
-//  Promise<{graph:{nameMetrics:string[],result:number[]},layout:{nameMetrics:string[],result:number[]}} | undefined> {
-
-//     // initialize objects
-//     const networkForJSON = ref<Network>({ id: '', nodes: {}, links: [] });
-//     const networkStyleforJSON = ref<GraphStyleProperties>({
-//         nodeStyles: {},
-//         linkStyles: {}
-//     });
-//     let startTime:number;
-//     let endTime:number;
-//     let subgraphNetwork:SubgraphNetwork;
-//     let resultGraph: {nameMetrics:string[],result:number[]}= {nameMetrics:[],result:[]};
-//     let resultLayout:{nameMetrics:string[],result:number[]}= {nameMetrics:[],result:[]};
-
-//     // import network from JSON, and process it
-//     try {
-//         await new Promise<void>((resolve, reject) => {
-//             try {
-//                 importNetworkFromURL(json, networkForJSON, networkStyleforJSON, () => {
-//                     //// Callback function (after network imported) :
-
-//                     // set style (same for all)
-//                     changeNodeStyles(networkStyleforJSON.value);
-//                     // create subgraphNetwork object
-//                     subgraphNetwork={network:networkForJSON,networkStyle:networkStyleforJSON,attributs:{},mainChains:{}};
-//                     // duplicate side compounds 
-//                     addSideCompoundAttributeFromList(subgraphNetwork,"/sideCompounds.txt").then(
-//                         ()=>{
-//                         duplicateSideCompound(subgraphNetwork);
-//                         }
-//                     ).then(
-//                         ()=>{
-//                         // calculate metrics of graph 
-//                         if (metricGraph) {
-//                             const metricsGraph=applyMetricsGraph(subgraphNetwork.network.value,printColumnName); 
-//                             resultGraph.result=metricsGraph.metrics
-//                             resultGraph.nameMetrics=metricsGraph.nameMetrics;
-//                         }
-//                         }
-//                     ).then(
-//                          ()=>{                       
-//                             startTime = performance.now();
-//                         }
-//                     ).then(
-//                         async ()=>{                       
-//                         // apply layout
-//                         subgraphNetwork=await applyLayout(subgraphNetwork);
-//                         }
-//                     ).then(
-//                          ()=>{                       
-//                             endTime = performance.now();
-//                         }
-//                     ).then(
-//                         ()=>{
-//                         // calculate metrics on resulting layout
-//                         const metricsLayout=applyMetricsLayout(subgraphNetwork,true,printColumnName);     
-//                         resultLayout.result= metricsLayout.metrics; 
-//                         resultLayout.nameMetrics=metricsLayout.nameMetrics;          
-//                         resolve();
-//                         }
-//                     );
-                    
-//                 });
-//             } catch (error) {
-//                 reject(error);
-//             }
-//         });
-//     } catch (error) {
-//         console.error("error file : " + json + "\n" + error);
-//         return undefined;
-//     }
-//     // add execution time of layout only (not duplication side compounds)
-//     const executionTime = parseFloat((endTime - startTime).toFixed(3));
-//     if (executionTime) {
-//         resultLayout.result.push(executionTime);
-//         resultLayout.nameMetrics.push('execution time (ms)');
-//     }
-    
-//     return {graph:resultGraph,layout:resultLayout};
-// }
-
-
-// function changeNodeStyles(networkStyle:GraphStyleProperties):void{
-// 	networkStyle.nodeStyles = {
-// 		metabolite: {
-// 			width: 25,
-// 			height: 25,
-// 			fill:  '#FFFFFF',
-// 			shape: 'circle'
-// 		},
-//     sideCompound: {
-// 			width: 12,
-// 			height: 12,
-// 			fill:  '#f0e3e0',
-// 			shape: 'circle'
-// 		},
-// 		reaction: {
-// 			width: 15,
-// 			height: 15,
-// 			fill: "grey",
-// 			shape: 'rect'
-// 		},
-// 		// reversible : {
-// 		// 	fill : "green",
-// 		// 	shape:"inverseTriangle"
-// 		// },
-// 		// reversibleVersion:{
-// 		// 	fill:"red",
-// 		// 	shape: "triangle"
-// 		// }
-
-// 	}
-// }
-
-// function print1DArray(data: Array<string|number|boolean>): void {
-//     const stringData = data.join(',');
-//     console.log(stringData);
-// }
-
-// function print2DArray(data: Array<Array<string|number|boolean>>): void {
-//     const stringData = data.map(row => row.join(',')).join('\n');
-//     console.log(stringData);
-// }
-
-
-
-// const defaultApplyLayout = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     return subgraphNetwork;
-// };
-
-// const applyForceLayout = (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     const network=subgraphNetwork.network.value;
-//     const styleNetwork= subgraphNetwork.networkStyle.value;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>(async (resolve, reject) => {
-//         try {
-//             await forceLayout(network, styleNetwork, false);
-//             resolve(subgraphNetwork);
-//         } catch (error) {
-//             reject(error);
-//         }
-//     });
-//     return subgraphNetworkPromise;
-// };
-
-// const applyVizLayout = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     let parameters: Parameters = defaultParameters;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>((resolve, reject) => {
-//         resolve(vizLayout(subgraphNetwork, false, false, parameters.addNodes, parameters.groupOrCluster, false, false, parameters.dpi, parameters.numberNodeOnEdge))
-//     })
-//     return subgraphNetworkPromise;
-// };
-
-// const applyAlgo = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     let parameters: Parameters=defaultParameters;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>((resolve, reject) => {
-//         resolve(allSteps(subgraphNetwork,parameters,false));
-//     })
-//     return subgraphNetworkPromise;
-// };
-
-// const applyAlgo_V0 = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     let parameters: Parameters=defaultParameters;
-//     parameters.doMainChain=false;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>((resolve, reject) => {
-//         resolve(allSteps(subgraphNetwork,parameters,false));
-//     })
-//     return subgraphNetworkPromise;
-// };
-
-// const applyAlgo_V1 = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     let parameters: Parameters=defaultParameters;
-//     parameters.pathType=PathType.LONGEST;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>((resolve, reject) => {
-//         resolve(allSteps(subgraphNetwork,parameters,false));
-//     })
-//     return subgraphNetworkPromise;
-// };
-
-// const applyAlgo_V3 = async (subgraphNetwork: SubgraphNetwork): Promise<SubgraphNetwork> => {
-//     let parameters: Parameters=defaultParameters;
-//     parameters.pathType=PathType.ALL;
-//     const subgraphNetworkPromise = new Promise<SubgraphNetwork>((resolve, reject) => {
-//         resolve(allSteps(subgraphNetwork,parameters,false));
-//     })
-//     return subgraphNetworkPromise;
-// };
-
-
-
-
-// export function applyMetricsGraph(network: Network,printColumnName:boolean=true): {nameMetrics:string[],metrics:number[]} {
-//     const networkGDS=networkToGDSGraph(network);
-//     const result: number[]=[];
-
-//     const nameColumnGraph: string[] = ['nodes', 'node not side compound','edges','edge  not side compound', 'hasDirectedCycle' ];
-//     if (printColumnName) print1DArray(nameColumnGraph);
-
-//     // number of nodes
-//     result.push(countNodes(network,true));
-//     // number of nodes not side compounds
-//     result.push(countNodes(network,false));
-//     // number of edges
-//     result.push(countEdges(network,true));
-//     // number of edges not side compounds
-//     result.push(countEdges(network,false));
-//     // has directed cycle
-//     result.push(Number(networkGDS.hasCycle()));
-
-//     return {nameMetrics:nameColumnGraph,metrics:result};
-// }
-
-// export function applyMetricsLayout(subgraphNetwork: SubgraphNetwork, coordAreCenter:boolean=true, printColumnName:boolean=true): {nameMetrics:string[],metrics:number[]} {
-//     const network=subgraphNetwork.network.value;
-//     const networkStyle=subgraphNetwork.networkStyle.value;
-//     const networkGDS=networkToGDSGraph(network);
-//     const result: number[]=[];
-
-//     const nameColumnLayout: string[] = ['node overlap', 'edge node overlap', 'different x (not SD)' ,'different y (not SD)','edge intersections','coef var edge length (no SD)','% colineat axis (not SD)', 'coef var vect dir (not SD)'];
-//     if (printColumnName) print1DArray(nameColumnLayout);
-
-
-//     //number of node overlap
-//     result.push(countOverlapNodeNetwork(network,networkStyle,coordAreCenter));
-//     // number of edge node overlap
-//     result.push(countOverlapNodeEdgeNetwork(network,networkStyle,coordAreCenter));
-//     // number of different x and y coordinates (without side compounds)
-//     const countDiffCoord=countDifferentCoordinatesNodeNetwork(network,networkStyle,coordAreCenter,false);
-//     result.push(countDiffCoord.x);
-//     result.push(countDiffCoord.y);
-//     // number of edges intersections
-//     result.push(countIntersectionEdgeNetwork(network,networkStyle,coordAreCenter));
-//     // variance edge length (without side compounds?)
-//     result.push(coefficientOfVariationEdgeLength(network,networkStyle,coordAreCenter,false));
-//     // direction edge : % of edge colinear to axis and coef of variaton of angle
-//     const resultDirection=analyseDirectorVector(network,networkStyle,coordAreCenter,true,false);
-//     result.push(resultDirection.colinearAxis)
-//     result.push(resultDirection.coefVariation)
-
-//     return {nameMetrics:nameColumnLayout,metrics:result};
-// }
diff --git a/src/composables/MetricsCalculation.ts b/src/composables/MetricsCalculation.ts
deleted file mode 100644
index 522a6d35cd721be8544dae2f33c46497d2a712d7..0000000000000000000000000000000000000000
--- a/src/composables/MetricsCalculation.ts
+++ /dev/null
@@ -1,448 +0,0 @@
-// import { Link } from "@metabohub/viz-core/src/types/Link";
-// import { Network } from "@metabohub/viz-core/src/types/Network";
-// import { Node } from "@metabohub/viz-core/src/types/Node";
-// import { getTopLeftCoordFromCenter, getSizeNodePixel, getCenterCoordFromTopLeft } from "./CalculateSize";
-// import { checkIntersection } from "line-intersect";
-// import { Coordinate,Size } from "../types/CoordinatesSize";
-// import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// //---------------------------------------------------- Utilitary Functions -----------------------------------------------------------//
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-// export function getCenterNode(node: Node,networkStyle:GraphStyleProperties,coordAreCenter:boolean=false): Coordinate {
-//     let nodeCenter: {x:number,y:number};
-
-//     if (coordAreCenter) {
-//         nodeCenter={x:node.x,y:node.y};
-//     }else{
-//         nodeCenter=getCenterCoordFromTopLeft(node,networkStyle);
-//     }
-//     return nodeCenter;
-// }
-
-
-// function commonNodeBetween2Links(link1: Link,link2: Link): boolean {
-//     if (link1.source==link2.source || link1.source==link2.target || link1.target==link2.source || link1.target==link2.target) {
-//         return true;
-//     }else {
-//         return false;
-//     }
-// } 
-
-// export function countNodes(network: Network, countSideCompound: boolean = true): number {
-//     if (countSideCompound) {
-//         return Object.keys(network.nodes).length;
-//     } else {
-//         let nodes = 0;
-//         Object.keys(network.nodes).forEach((nodeID) => {
-//             const node = network.nodes[nodeID];
-//             if (!(node.metadata && node.metadata["isSideCompound"])) {
-//                 nodes += 1;
-//             }
-//         });
-//         return nodes;
-//     }
-// }
-
-// export function countEdges(network: Network, countSideCompound: boolean = true): number {
-//     if (countSideCompound) {
-//         return network.links.length;
-//     } else {
-//         let links = 0;
-//         network.links.forEach(link => {
-//             if (!(link.source.metadata && link.source.metadata["isSideCompound"]) && !(link.target.metadata && link.target.metadata["isSideCompound"])) {
-//                 links += 1;
-//             }
-//         });
-//         return links;
-//     }
-// }
-
-// function getNormalizedDirectorVector(link: Link, style: GraphStyleProperties, coordAreCenter: boolean = false): Coordinate {
-
-//     const sourceCenter = getCenterNode(link.source, style, coordAreCenter);
-//     const targetCenter = getCenterNode(link.target, style, coordAreCenter);
-
-//     const dx = targetCenter.x - sourceCenter.x;
-//     const dy = targetCenter.y - sourceCenter.y;
-
-//     const length = edgeLength(link, style, coordAreCenter);
-
-//     if (length === 0) {
-//         return { x: 0, y: 0 }; // Handle case with zero length
-//     }
-
-//     return {
-//         x: parseFloat((dx / length).toFixed(2)),
-//         y:  parseFloat((dy / length).toFixed(2))
-//     };
-// }
-
-// function linkOfSideCompound(link:Link):boolean{
-//     return (link.source.metadata && (link.source.metadata["isSideCompound"]) as boolean) || (link.target.metadata && (link.target.metadata["isSideCompound"]) as boolean);
-// }
-
-
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// //-------------------------------------------------------- Node Metrics --------------------------------------------------------------//
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Node overlap
-
-
-// export function countOverlapNodeNetwork(network: Network,networkStyle:GraphStyleProperties,coordAreCenter:boolean=false): number {
-//     let nb=0;
-//     const nodesID=Object.keys(network.nodes);
-
-//     for (let i=0 ; i<nodesID.length ; i++) {
-//         for (let j=i+1 ; j<nodesID.length ; j++) {
-//             // node1
-//             const node1=network.nodes[nodesID[i]];
-//             const coordNode1=getCenterNode(node1,networkStyle,coordAreCenter);
-//             const sizeNode1=getSizeNodePixel(node1,networkStyle);
-//             // node2
-//             const node2=network.nodes[nodesID[j]];
-//             const coordNode2=getCenterNode(node2,networkStyle,coordAreCenter);
-//             const sizeNode2=getSizeNodePixel(node2,networkStyle);
-
-//             if (nodesOverlap(coordNode1,sizeNode1,coordNode2,sizeNode2)){
-//                 nb+=1;
-//             }
-//         }
-//     }
-//     return nb;
-// }
-
-
-
-// function nodesOverlap(coord1: Coordinate, size1: Size, coord2: Coordinate, size2: Size): boolean {
-
-//     // coordinate are center
-
-//     if ( !size1.width || !size1.height || !size2.width || !size2.height || !coord1.x || !coord1.y || !coord2.x || !coord2.y) {
-//         // Handle null or undefined inputs appropriately
-//         return false;
-//     }
-
-//     // rectangle 1
-//     const left1 = coord1.x - size1.width / 2;
-//     const right1 = coord1.x + size1.width / 2;
-//     const top1 = coord1.y - size1.height / 2;
-//     const bottom1 = coord1.y + size1.height / 2;
-
-//     // rectangle 2
-//     const left2 = coord2.x - size2.width / 2;
-//     const right2 = coord2.x + size2.width / 2;
-//     const top2 = coord2.y - size2.height / 2;
-//     const bottom2 = coord2.y + size2.height / 2;
-
-//     // overlap?
-//     const overlapX = left1 < right2 && right1 > left2;
-//     const overlapY = top1 < bottom2 && bottom1 > top2;
-
-//     return overlapX && overlapY;
-// }
-
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Node on edge
-
-
-
-// export function countOverlapNodeEdgeNetwork(network: Network,networkStyle:GraphStyleProperties,coordAreCenter:boolean=false): number {
-//     let nb=0;
-//     const nodesID=Object.keys(network.nodes);
-//     nodesID.forEach( (nodeID) =>{
-
-//         const node=network.nodes[nodeID];
-//         const coordNode1=getCenterNode(node,networkStyle,coordAreCenter);
-//         const sizeNode=getSizeNodePixel(node,networkStyle);
-//         nb += countOverlapEdgeForNode(network,networkStyle,nodeID,coordNode1,sizeNode,coordAreCenter);
-
-//     });
-
-//     return nb;
-// }
-
-// function countOverlapEdgeForNode(network:Network,networkStyle:GraphStyleProperties,nodeID:string,coordNode:Coordinate,sizeNode:Size,coordAreCenter:boolean=false): number {
-//     let nb=0;
-
-//     network.links.forEach(link => {        
-//         // if node not linked to the edge : check if it is on the edge
-//         if(!(link.source.id==nodeID || link.target.id==nodeID)){
-
-//             let coordSource=getCenterNode(link.source,networkStyle,coordAreCenter);
-//             let coordTarget=getCenterNode(link.target,networkStyle,coordAreCenter);
-
-//             if (nodeEdgeOverlap(coordNode,sizeNode,coordSource,coordTarget)){
-//                 nb+=1;
-//             }
-//         }
-//     });
-//     return nb;
-// }
-
-
-// function nodeEdgeOverlap(centerCoordNode: Coordinate, sizeNode: Size, coordSource: Coordinate, coordTarget: Coordinate): boolean { // CORRIGER !!!!!
-    
-//     // Treat the node as a rectangle (coordinates are center of node)
-//     const rect = {
-//         left: centerCoordNode.x - sizeNode.width / 2,
-//         right: centerCoordNode.x + sizeNode.width / 2,
-//         top: centerCoordNode.y - sizeNode.height / 2,
-//         bottom: centerCoordNode.y + sizeNode.height / 2
-//     };
-
-//     // Check if any of the edge's endpoints is inside the rectangle => same as node overlap (to suppress ?)
-//     const isPointInsideRect = (point: Coordinate) => 
-//         point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
-
-//     if (isPointInsideRect(coordSource) || isPointInsideRect(coordTarget)) {
-//         return true; // One of the endpoints is inside the rectangle
-//     }
-
-//     // Check for overlap between the edge and the sides of the rectangle
-//     // Convert the sides of the rectangle into line segments
-//     const rectangleEdges = [
-//         { start: { x: rect.left, y: rect.top }, end: { x: rect.right, y: rect.top } }, // Top
-//         { start: { x: rect.right, y: rect.top }, end: { x: rect.right, y: rect.bottom } }, // Right
-//         { start: { x: rect.left, y: rect.bottom }, end: { x: rect.right, y: rect.bottom } }, // Bottom
-//         { start: { x: rect.left, y: rect.top }, end: { x: rect.left, y: rect.bottom } } // Left
-//     ];
-
-//     // Use checkIntersection function to check if two line segments intersect
-//     for (const edge of rectangleEdges) {
-//         const result = checkIntersection(edge.start.x,edge.start.y, edge.end.x,edge.end.y, coordSource.x, coordSource.y,coordTarget.x,coordTarget.y);
-//         if (result.type === "intersecting") {
-//             return true; // There is an overlap
-//         }
-//     }
-
-//     return false; // No overlap detected
-// }
-
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Node different coordinates
-
-// export function countDifferentCoordinatesNodeNetwork(network: Network, networkStyle: GraphStyleProperties, coordAreCenter: boolean = false, countSideCompound:boolean=true,roundAt: number = 2): { x: number, y: number } {
-//     let uniqueX = new Set<number>();
-//     let uniqueY = new Set<number>();
-
-//     Object.keys(network.nodes).forEach((nodeID) => {
-//         const node = network.nodes[nodeID];
-//         // Do not count side compounds if countSideCompound is false
-//         if (countSideCompound ||  !(node.metadata && node.metadata["isSideCompound"])) {
-//             const coordNode = getCenterNode(node, networkStyle, coordAreCenter);
-            
-//             // Round the coordinates based on roundAt
-//             const roundedX = parseFloat(coordNode.x.toFixed(roundAt));
-//             const roundedY = parseFloat(coordNode.y.toFixed(roundAt));
-            
-//             uniqueX.add(roundedX);
-//             uniqueY.add(roundedY);
-//         }
-//     });
-
-//     return { x: uniqueX.size, y: uniqueY.size };
-// }
-
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// //-------------------------------------------------------- Edge Metrics --------------------------------------------------------------//
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Edge intersection
-// // (overlap not taken into account)
-
-
-// /**
-//  * Counts how many crossings are in a network
-//  * @param network the network
-//  * @returns the number of crossings
-//  */
-// export function countIntersectionEdgeNetwork(network: Network,style:GraphStyleProperties,coordAreCenter:boolean=false): number {
-//     let nb: number = 0;
-//     for (let i=0 ; i<network.links.length ; i++) {
-//         for (let j=i+1 ; j<network.links.length ; j++) {
-//             const link1=network.links[i];
-//             const link2=network.links[j];
-//             if (edgesIntersect(link1, link2,style,coordAreCenter)){
-//                 nb++;
-//             }
-//         }
-//     }
-//     return nb;
-// }
-
-// function edgesIntersect(link1: Link, link2: Link,style:GraphStyleProperties,coordAreCenter:boolean=false): boolean {
-
-//     // Case of common node
-//     if (commonNodeBetween2Links(link1,link2)) {
-//         return false;
-//     }
-
-//     // Get center of node : where the link is attached
-//     const node1Center=getCenterNode(link1.source,style,coordAreCenter);
-//     const node2Center=getCenterNode(link1.target,style,coordAreCenter);
-//     const node3Center=getCenterNode(link2.source,style,coordAreCenter);
-//     const node4Center=getCenterNode(link2.target,style,coordAreCenter);
-
-
-//     // Check intersection
-//     const result = checkIntersection(node1Center.x, node1Center.y, node2Center.x, node2Center.y, node3Center.x, node3Center.y, node4Center.x, node4Center.y);
-//     if (result.type == "intersecting") {
-//         return true;
-//     } else {
-//         return false;
-//     }
-    
-// }
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Edge length
-
-
-// export function coefficientOfVariationEdgeLength(network: Network,style:GraphStyleProperties,coordAreCenter:boolean=false,includeSideCompounds:boolean=true): number {
-
-//     let links = network.links;
-
-//     if (!includeSideCompounds) {
-//         links = links.filter(link => 
-//             !(link.source.metadata && link.source.metadata["isSideCompound"]) && 
-//             !(link.target.metadata && link.target.metadata["isSideCompound"])
-//         );
-//     }
-
-//     if (links.length === 0) {
-//         return 0; // Handle case with no edge 
-//     }
-
-//     const lengths = links.map(link => edgeLength(link, style, coordAreCenter));
-    
-//     const mean = lengths.reduce((a, b) => a + b, 0) / lengths.length;
-//     if (mean === 0) {
-//         return 0; // Handle case with no edge lengths
-//     }
-//     const variance = lengths.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / lengths.length;
-//     const stdDeviation = Math.sqrt(variance);
-//     const coefVariation = stdDeviation / Math.abs(mean);
-//     return parseFloat(coefVariation.toFixed(3));
-// }
-
-// function edgeLength(link: Link,style:GraphStyleProperties,coordAreCenter:boolean=false): number {
-//     const sourceCenter=getCenterNode(link.source,style,coordAreCenter);
-//     const targetCenter=getCenterNode(link.target,style,coordAreCenter);
-
-//     const dx = sourceCenter.x - targetCenter.x;
-//     const dy = sourceCenter.y - targetCenter.y;
-//     return Math.sqrt(dx * dx + dy * dy);
-// }
-
-
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Edge colinear with axis 
-// // => function used with edge direction
-
-// function calculateNormalizedDirectorVectors(links: Link[], style: GraphStyleProperties, coordAreCenter: boolean = false,includeSideCompounds:boolean=true): Coordinate[] {
-//     const vectors: Coordinate[] = [];
-
-//     links.forEach(link => {
-//         if (includeSideCompounds || !linkOfSideCompound(link)){
-//             const normalizedVector = getNormalizedDirectorVector(link, style, coordAreCenter);
-//             vectors.push(normalizedVector);
-//         }
-       
-//     });
-
-//     return vectors;
-// }
-
-// function isColinearAxisNetwork(vector:Coordinate): boolean {
-//         return vector.x === 0 || vector.y === 0;
-// }
-
-// function countEdgeColinearAxisNetwork(vectors:Coordinate[], pourcentage:boolean=false): number {
-
-//     if (vectors.length === 0) {
-//         return 0;
-//     }
-
-//     let count = 0;
-//     vectors.forEach(vector => {
-//         if (isColinearAxisNetwork(vector)) {
-//             count += 1;
-//         }
-//     });
-//     if (pourcentage) {
-//         return parseFloat((count / vectors.length).toFixed(2));
-//     }
-//     return count;
-// }
-
-
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// //-------------------------------------------------------- Domain Metrics --------------------------------------------------------------//
-// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-// // WHEN CLEAN : fonction that said if sidecompound or not
-
-// /////////////////////////////////////////////////////
-// // ------------------------- Edge direction
-
-// export function analyseDirectorVector(network: Network, style: GraphStyleProperties, coordAreCenter: boolean = false, pourcentageColinearAxis:boolean=false, includeSideCompounds:boolean=true):{colinearAxis:number,coefVariation:number} { 
-    
-//     const result: { colinearAxis: number, coefVariation: number } = { colinearAxis: undefined, coefVariation: undefined };
-
-//     let links = network.links;
-
-//     if (!includeSideCompounds) {
-//         links = links.filter(link => 
-//             !(link.source.metadata && link.source.metadata["isSideCompound"]) && 
-//             !(link.target.metadata && link.target.metadata["isSideCompound"])
-//         );
-//     }
-    
-//     // get all normalized director vectors
-//     const vectors = calculateNormalizedDirectorVectors(links, style, coordAreCenter,includeSideCompounds);
-//     if (vectors.length==0) return { colinearAxis: 0, coefVariation: 0 }
-
-//     // count colinear with axis
-//     result.colinearAxis = countEdgeColinearAxisNetwork(vectors,pourcentageColinearAxis);
-
-//     // coeficient of variation of angle
-
-//      // calculate angles of vectors
-//      const angles = vectors.map(vector => Math.atan2(vector.y, vector.x));
-
-//      // calculate mean of angles
-//      const mean = angles.reduce((a, b) => a + b, 0) / angles.length;
-//      if (mean === 0) {
-//          result.coefVariation = 0; // Handle case with no angles
-//          return result;
-//      }
- 
-//      // calculate variance of angles
-//      const variance = angles.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / angles.length;
- 
-//      // calculate standard deviation
-//      const stdDeviation = Math.sqrt(variance);
- 
-//      // calculate coefficient of variation
-//      const coefVariation = stdDeviation / mean;
-//      result.coefVariation = parseFloat(coefVariation.toFixed(2));
- 
-//      return result;
-
-// }
-
-
diff --git a/src/composables/SBMLtoJSON.ts b/src/composables/SBMLtoJSON.ts
deleted file mode 100644
index 4a16b9f44e1b8c9f6b7aef8516e35d670a040e66..0000000000000000000000000000000000000000
--- a/src/composables/SBMLtoJSON.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-// import { parseString } from 'xml2js';
-// import type { JSONGraphFormat, XMLSpecies, XMLReactions } from '../types/JSONGraphFormat';
-
-// /**
-//  * Return a number between min (inclusive) and max (inclusive)
-//  * @param min 
-//  * @param max 
-//  * @returns a number
-//  */
-// function getRandomInt(min: number, max: number): number {
-//     min = Math.ceil(min);
-//     max = Math.floor(max);
-//     return Math.floor(Math.random() * (max - min + 1)) + min;
-// }
-
-// /**
-//  * Convert an xml graph into a JSON graph format
-//  * @param sbmlString the xml file as a string
-//  * @returns the graph as a Promise 
-//  */
-// export async function sbml2json(sbmlString: string): Promise<JSONGraphFormat> {
-//     return new Promise((resolve, reject) => {
-//         parseString(sbmlString, { explicitArray: false }, (err, result) => {
-//             if (err) {
-//                 console.log("Error during the parsing of the file");
-//                 console.log(err);
-//                 reject(err);
-//             } else {
-//                 const model = result.sbml.model;
-//                 // graph to return
-//                 const graph: JSONGraphFormat = {
-//                     graph: {
-//                         id: model.$.id,
-//                         type: 'metabolic',
-//                         metadata: {
-//                             style: {
-//                                 nodeStyles: {
-//                                     metabolite: {
-//                                         width: 25,
-//                                         height: 25,
-//                                         strokeWidth: 1,
-//                                         shape: 'circle'
-//                                     },
-//                                     reaction: {
-//                                         width: 15,
-//                                         height: 15,
-//                                         strokeWidth: 0.5,
-//                                         shape: 'rect',
-//                                         fill: 'grey'
-//                                     },
-//                                     reversible: {
-//                                         fill: 'green',
-//                                         shape: 'inverseTriangle'
-//                                     },
-//                                     reversibleVersion: {
-//                                         fill: 'red',
-//                                         shape: 'triangle'
-//                                     }
-//                                 }
-//                             }
-//                         },
-//                         nodes: {},
-//                         edges: []
-//                     }
-//                 };
-
-//                 // Transform species to nodes
-//                 const speciesList = model.listOfSpecies.species;
-//                 // add a metabolite node for each species
-//                 speciesList.forEach((species: XMLSpecies) => {
-//                     graph.graph.nodes[species.$.id] = {
-//                         id: species.$.id,
-//                         metadata: {
-//                             classes: ['metabolite'],
-//                             position: {
-//                                 x: getRandomInt(0, 100),
-//                                 y: getRandomInt(0, 100)
-//                             }
-//                         },
-//                         label: species.$.name
-//                     };
-//                 });
-
-//                 // Transform reactions to nodes and edges
-//                 const reactions = model.listOfReactions.reaction;
-//                 reactions.forEach((reaction: XMLReactions) => {
-//                     const reactionId = reaction.$.id;
-                    
-//                     let classReversible :string;
-//                     const isReversible=reaction.$.reversible;
-//                     if (isReversible==="true"){
-//                         classReversible = "reversible";
-//                     }else{
-//                         classReversible = "irreversible";
-//                     }
-
-//                     // get the reactants and products for every reaction
-//                     const reactants: string[] = [];
-//                     if (reaction.listOfReactants.speciesReference != undefined && (reaction.listOfReactants.speciesReference as Partial<XMLSpecies>[]).length != undefined) {
-//                         // type : array
-//                         (reaction.listOfReactants.speciesReference as Partial<XMLSpecies>[]).forEach((ref: Partial<XMLSpecies>) => {
-//                             reactants.push(ref.$.species);
-//                         });
-//                     } else if (reaction.listOfReactants.speciesReference != undefined) {
-//                         // type : object
-//                         reactants.push((reaction.listOfReactants.speciesReference as Partial<XMLSpecies>).$.species);
-//                     }
-//                     const products: string[] = [];
-//                     if (reaction.listOfProducts.speciesReference != undefined && (reaction.listOfProducts.speciesReference as Partial<XMLSpecies>[]).length != undefined) {
-//                         // type : array
-//                         (reaction.listOfProducts.speciesReference as Partial<XMLSpecies>[]).forEach((ref: Partial<XMLSpecies>) => {
-//                             products.push(ref.$.species);
-//                         });
-//                     } else if (reaction.listOfProducts.speciesReference != undefined) {
-//                         // type : object
-//                         products.push((reaction.listOfProducts.speciesReference as Partial<XMLSpecies>).$.species);
-//                     }
-
-//                     // add the reaction as a node
-//                     graph.graph.nodes[reactionId] = {
-//                         id: reactionId,
-//                         metadata: {
-//                             classes: ['reaction',classReversible],
-//                             position: {
-//                                 x: getRandomInt(0, 100),
-//                                 y: getRandomInt(0, 100)
-//                             }
-//                         },
-//                         label: reaction.$.name
-//                     };
-
-//                     // add the edges for the reaction and its reactants and products
-//                     reactants.forEach((reactant: string) => {
-//                         graph.graph.edges.push({
-//                             id: `${reactant}--${reactionId}`,
-//                             source: reactant,
-//                             target: reactionId,
-//                             metadata: {
-//                                 classes: [classReversible]
-//                             }
-//                         });
-//                     });
-//                     products.forEach((product: string) => {
-//                         graph.graph.edges.push({
-//                             id: `${reactionId}--${product}`,
-//                             source: reactionId,
-//                             target: product,
-//                             metadata: {
-//                                 classes: [classReversible]
-//                             }
-//                         });
-//                     });
-//                 });
-
-//                 // return the graph object
-//                 resolve(graph);
-//             }
-//         });
-//     });
-// }
diff --git a/src/composables/SubgraphForSubgraphNetwork.ts b/src/composables/SubgraphForSubgraphNetwork.ts
index 2222479e00dd56290029d880cce09be5fad3e38a..265a3e76e67150c89774b3247528f8acd4620c22 100644
--- a/src/composables/SubgraphForSubgraphNetwork.ts
+++ b/src/composables/SubgraphForSubgraphNetwork.ts
@@ -30,13 +30,13 @@ import { SubgraphNetwork } from "../types/SubgraphNetwork";
  * @param classes The array of classes associated with the subgraph (defaults to an empty array).
  * @returns The newly created subgraph.
  */
-export function createSubgraph(name: string, nodes: Array<string>, classes: Array<string>, type: TypeSubgraph, forSubgraph?: {name:string,type:TypeSubgraph}, associatedSubgraphs?:Array<{name:string,type:TypeSubgraph}>): Subgraph {
+export function createSubgraph(name: string, nodes: Array<string>, classes: Array<string>, type: TypeSubgraph, parentSubgraph?: {name:string,type:TypeSubgraph}, associatedSubgraphs?:Array<{name:string,type:TypeSubgraph}>): Subgraph {
     return {
         name,
         classes,
         nodes,
         type,
-        parentSubgraph: forSubgraph,
+        parentSubgraph: parentSubgraph,
         childrenSubgraphs: associatedSubgraphs
     };
 }
diff --git a/src/composables/SubgraphForViz.ts b/src/composables/SubgraphForViz.ts
index 1653e6527683ff5fef3349c9386abe07576224fe..d63934be39276255dcbc3258c00a16b981cead47 100644
--- a/src/composables/SubgraphForViz.ts
+++ b/src/composables/SubgraphForViz.ts
@@ -1,6 +1,8 @@
 // Type imports
-import {  TypeSubgraph } from "../types/Subgraph";
+import { TypeSubgraph } from "../types/Subgraph";
 import { SubgraphNetwork } from "../types/SubgraphNetwork";
+import { SubgraphViz } from "../types/TypeViz";
+
 
 // General imports
 import { Graph } from "@viz-js/viz";
@@ -70,7 +72,6 @@ export function addMainChainForViz(vizGraph: Graph, nameMainChain: string, subgr
         vizGraph.subgraphs = [];
     }
     vizGraph.subgraphs.push(clusterViz);
-
     return vizGraph;
 }
 
@@ -94,7 +95,6 @@ function changeCycleMetanodes(subgraphNetwork:SubgraphNetwork,listNodeBefore:str
         let cycle:string;
         if (network.nodes[node].metadataLayout && network.nodes[node].metadataLayout[TypeSubgraph.CYCLEGROUP]){
             cycle = network.nodes[node].metadataLayout[TypeSubgraph.CYCLEGROUP]; 
-            //cycle=inBiggerCycle(cycle,subgraphNetwork)
             if(!(listNodeAfter.includes(cycle))){
                 // push node cycle
                 listNodeAfter.push(cycle);
diff --git a/src/composables/VizCoreFunctions.ts b/src/composables/VizCoreFunctions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4e1928674b92a64d701e0a6b605c6b0fea2eda0
--- /dev/null
+++ b/src/composables/VizCoreFunctions.ts
@@ -0,0 +1,122 @@
+// Type imports
+import { GraphStyleProperties, Network, Node } from  "../types/TypeVizCore";
+
+
+
+/*******************************************************************************************************************************************************/
+//___________________________________________________0.  Node duplication _________________________________________________________________________
+
+
+/**
+ * Duplicate all nodes according to a specific metadata attribut
+ * @param network Network object that contains all nodes
+ * @param attribut Metadata attribut of node. Must be a boolean
+ */
+export function duplicateAllNodesByAttribut(network: Network, networkStyle: GraphStyleProperties, attribut: string): void {
+    Object.keys(network.nodes).forEach((nodeID: string) => {
+      const node = network.nodes[nodeID] as Node;
+      if (node.metadata && node.metadata[attribut]) {
+        const checkAtt = node.metadata[attribut] as boolean;
+        if (checkAtt) {
+          duplicateNode(nodeID, network, networkStyle);
+        }
+      }
+    });
+  }
+
+
+  /**
+ * Duplicate specific node in network object
+ * @param nodeId Node id
+ * @param network Network object
+ * @param networkStyle Style object
+ */
+export function duplicateNode(nodeId: string, network: Network, networkStyle: GraphStyleProperties): void {
+    if (network.nodes[nodeId]) {
+      const linksIndex = [] as Array<number>;
+      const originalNode = network.nodes[nodeId];
+  
+      if (networkStyle.nodeStyles) {
+        networkStyle.nodeStyles['duplicate'] = {
+          fill: '#FFFFFF',
+          height: 10,
+          width: 10,
+          shape: 'circle',
+        }
+      } else {
+        networkStyle['nodeStyles'] = {
+          duplicate: {
+            fill: '#FFFFFF',
+            height: 10,
+            width: 10,
+            shape: 'circle',
+          }
+        }
+      }
+  
+      network.links.forEach((link) => {
+        if (link.source.id === nodeId || link.target.id === nodeId) {
+          const index = network.links.indexOf(link);
+          linksIndex.push(index);
+        }
+      });
+  
+      for (let i = 0; i < linksIndex.length; i++) {
+        
+        const newNode: Node = {
+          id: nodeId + i,
+          classes: ['duplicate'],
+          label: originalNode.label,
+          x: 0,
+          y: 0
+        };
+  
+        const index = linksIndex[i];
+  
+        if (network.links[index].source.id === nodeId) {
+          newNode.x = originalNode.x - ((originalNode.x - network.links[index].target.x) / 2);
+          newNode.y = originalNode.y - ((originalNode.y - network.links[index].target.y) / 2);
+          network.links[index].source = newNode;
+        }
+  
+        if (network.links[index].target.id === nodeId) {
+          newNode.x = originalNode.x - ((originalNode.x - network.links[index].source.x) / 2);
+          newNode.y = originalNode.y - ((originalNode.y - network.links[index].source.y) / 2);
+          network.links[index].target = newNode
+        }
+  
+        network.nodes[nodeId+i] = newNode;
+      }
+  
+      delete network.nodes[nodeId];
+    }
+  }
+  
+
+
+/*******************************************************************************************************************************************************/
+//___________________________________________________1.  Node suppresion _________________________________________________________________________
+
+
+/**
+ * Remove a list of nodes from network object
+ * @param listId Array of nodes id
+ * @param network Network object
+ */
+export function removeAllSelectedNodes(listId: Array<string>, network: Network): void {
+    listId.forEach((nodeId: string) => {
+      if (Object.keys(network.nodes).includes(nodeId)) {
+        delete network.nodes[nodeId];
+  
+        const links = network.links.filter((link) => {
+          if (link.source.id !== nodeId && link.target.id !== nodeId) {
+            return link;
+          }
+        });
+    
+        network.links = links;
+      }
+    });
+}
+  
+  
\ No newline at end of file
diff --git a/src/composables/__tests__/AlgorithmBFS.test.ts b/src/composables/__tests__/AlgorithmBFS.test.ts
index c3fb0864225d064b6c16a428060cfda0250776ea..80fc7b7415da5a38ec2801ddcc5d63f289393491 100644
--- a/src/composables/__tests__/AlgorithmBFS.test.ts
+++ b/src/composables/__tests__/AlgorithmBFS.test.ts
@@ -1,6 +1,6 @@
 // Types imports
 import { StartNodesType } from "../../types/EnumArgs";
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Network } from  "../../types/TypeVizCore";
 
 // Composable imports
 import * as CalculateStartNodes from "../CalculateStartNodes";
diff --git a/src/composables/__tests__/AlgorithmDFS.test.ts b/src/composables/__tests__/AlgorithmDFS.test.ts
index fef66d18a28d62e053f6f8be28b23c92570b8cfa..203d358715ef6aa7517affa104ea8af3cf4edd2c 100644
--- a/src/composables/__tests__/AlgorithmDFS.test.ts
+++ b/src/composables/__tests__/AlgorithmDFS.test.ts
@@ -1,7 +1,5 @@
 // Types imports
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Link } from "@metabohub/viz-core/src/types/Link";
-import { Node } from "@metabohub/viz-core/src/types/Node";
+import { Network, Node, Link } from "../../types/TypeVizCore";
 import { StartNodesType } from "../../types/EnumArgs";
 
 
@@ -102,6 +100,7 @@ describe('AlgorithmDFS', () => {
         it('should return a DFS order, when no directed cycle', async () => {
 
             // MOCK
+            const removeEdgeMock = jest.fn();
             const networkToGDSGraphMock = jest.spyOn(ConvertFromNetwork, 'networkToGDSGraph');
             networkToGDSGraphMock.mockImplementation(async () => {
                     return Promise.resolve({
@@ -126,7 +125,7 @@ describe('AlgorithmDFS', () => {
                                     return [];
                             }
                         }),
-                        removeEdge: jest.fn()
+                        removeEdge: removeEdgeMock
                     });
                    
             });
@@ -149,13 +148,14 @@ describe('AlgorithmDFS', () => {
 
             // EXPECT
             expect(result.dfs).toEqual([ 'A', 'C', 'E', 'B', 'D' ]);
-            expect(true).toBe(false); // need to check if no edge removed
+            expect(removeEdgeMock).not.toHaveBeenCalled();
 
 
         });
 
         it('should return a DFS order and a graph without cycles, when directed cycle', async () => {
              // MOCK
+             const removeEdgeMock = jest.fn();
              const networkToGDSGraphMock = jest.spyOn(ConvertFromNetwork, 'networkToGDSGraph');
              networkToGDSGraphMock.mockImplementation(async () => {
                      return Promise.resolve({
@@ -180,7 +180,7 @@ describe('AlgorithmDFS', () => {
                                      return [];
                              }
                          }),
-                         removeEdge: jest.fn()
+                         removeEdge: removeEdgeMock
                      });
                     
              });
@@ -204,13 +204,11 @@ describe('AlgorithmDFS', () => {
 
             // EXPECT
             expect(result.dfs).toEqual([ 'A', 'C', 'E', 'B', 'D' ]);
-            expect(true).toBe(false); // need to check if edge removed
-
+            expect(removeEdgeMock).toHaveBeenCalled();
 
         });
 
        
-       
     });
 
 });
diff --git a/src/composables/__tests__/CalculateOverlaps.test.ts b/src/composables/__tests__/CalculateOverlaps.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5412c4a180cc8af2c59cb0360ef36ea871e016e9
--- /dev/null
+++ b/src/composables/__tests__/CalculateOverlaps.test.ts
@@ -0,0 +1,292 @@
+// Type import
+import { GraphStyleProperties, Network } from "../../types/TypeVizCore";
+
+
+// Composable imports
+import * as CalculateSize from '../CalculateSize';
+import * as CalculateOverlaps from '../CalculateOverlaps';
+
+// General imports
+import { checkIntersection } from 'line-intersect';
+
+jest.mock("line-intersect", () => ({
+    checkIntersection: jest.fn(),
+  }));
+
+describe('CalculateOverlaps', () => {
+
+    let getSizeNodePixelMock: jest.SpyInstance;
+
+    beforeEach(() => {
+
+        // MOCK 
+        getSizeNodePixelMock = jest.spyOn(CalculateSize, 'getSizeNodePixel');
+        getSizeNodePixelMock.mockReturnValue({width: 4, height: 4});
+
+    });
+
+    afterEach(() => {
+        jest.clearAllMocks();
+    });
+
+
+    describe('isIntersectionGraph', () => {
+        it('should return false when there are no intersecting edges', () => {
+            // MOCK
+            checkIntersection.mockImplementationOnce(() => {return {type:"none"}});
+
+            // DATA
+            const nodes = {
+                A: {x: 0, y: 0},
+                B: {x: 10, y: 0},
+                C: {x: 0, y: 10},
+                D: {x: 10, y: 10}
+            };
+            const links = [
+                {source: 'A', target: 'B'},
+                {source: 'C', target: 'D'}
+            ];
+
+            // TEST
+            const result = CalculateOverlaps.isIntersectionGraph(nodes, links);
+
+            // EXPECT
+            expect(result).toBe(false);
+            expect(checkIntersection).toHaveBeenCalledTimes(1);
+        });
+
+        it('should return true when edges intersect', () => {
+            // MOCK
+            checkIntersection.mockImplementationOnce(() => {return {type:"intersecting"}});
+
+            // DATA
+            const nodes = {
+                A: {x: 0, y: 0},
+                B: {x: 10, y: 10},
+                C: {x: 0, y: 10},
+                D: {x: 10, y: 0}
+            };
+            const links = [
+                {source: 'A', target: 'B'},
+                {source: 'C', target: 'D'}
+            ];
+            
+            // TEST 
+            const result = CalculateOverlaps.isIntersectionGraph(nodes, links);
+
+            // EXPECT
+            expect(result).toBe(true);
+            expect(checkIntersection).toHaveBeenCalledTimes(1);
+
+        });
+
+        it('should return false when edges as a common end', () => {
+            // DATA
+            const nodes = {
+                A: {x: 0, y: 0},
+                B: {x: 10, y: 10},
+                C: {x: 0, y: 0},
+                D: {x: 10, y: 0}
+            };
+            const links = [
+                {source: 'A', target: 'B'},
+                {source: 'C', target: 'D'}
+            ];
+
+            // TEST 
+            const result = CalculateOverlaps.isIntersectionGraph(nodes, links);
+
+            // EXPECT
+            expect(result).toBe(false);
+            expect(checkIntersection).not.toHaveBeenCalled();
+
+        });
+
+    });
+
+    describe('isOverlapNodes', () => {
+
+
+        it('should return false when nodes do not overlap', () => {
+            
+            // DATA
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 100, y: 100}
+            };
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A', x: 0, y: 0}, // position in network is not the one taken into account
+                    B: {id: 'B' , x: 0, y: 0}
+                },
+                links:[]
+            };
+            const networkStyle:GraphStyleProperties = {};
+            
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodes(nodesPosition, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(false);
+        });
+    
+        it('should return false because a null value', () => {
+            // DATA
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 0, y: 0}
+            };
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A', x: 0, y: 0}, // position in network is not the one taken into account
+                    B: {id: 'B' , x: 0, y: 0}
+                },
+                links:[]
+            };
+            const networkStyle:GraphStyleProperties = {};
+
+            // MOCK
+            getSizeNodePixelMock.mockReturnValue({width: null, height: 2});
+            
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodes(nodesPosition, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(false);
+        });
+
+        it('should return true when nodes do overlap', () => {
+            // DATA
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 1, y: 1}
+            };
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A', x: 0, y: 0}, // position in network is not the one taken into account
+                    B: {id: 'B' , x: 10, y: 10}
+                },
+                links:[]
+            };
+            const networkStyle:GraphStyleProperties = {};
+            
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodes(nodesPosition, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(true);
+        });
+
+    });
+
+    describe('isOverlapNodesEdges', () => {
+
+        it('should return false when no node overlaps with any edge', () => {
+            // MOCK
+            checkIntersection.mockImplementation(() => {return {type:"none"}});
+
+            // DATA
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 100, y: 0},
+                C: {x: 50, y: 50}
+            };
+            const links = [
+                {source: 'A', target: 'B'}
+            ];
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A',x:0, y:0}, // position in network is not the one taken into account
+                    B: {id: 'B',x:0, y:0},
+                    C: {id: 'C', x:0, y:0}
+                },
+                links:[]
+            };
+            const networkStyle = {};
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodesEdges(nodesPosition, links, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(false);
+            expect(checkIntersection).toHaveBeenCalledTimes(4);
+
+            checkIntersection.mockClear();
+        });
+    
+        it('should return true when a node overlaps with an edge', () => {
+            // MOCK
+            checkIntersection.mockImplementationOnce(() => {return {type:"none"}})
+            .mockImplementationOnce(() => {return {type:"none"}})
+            .mockImplementationOnce(() => {return {type:"intersecting"}})
+            .mockImplementation(() => {return {type:"none"}});
+
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 100, y: 0},
+                C: {x: 50, y: 1} // center Node C is close of the edge
+            };
+            const links = [
+                {source: 'A', target: 'B'}
+            ];
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A',x:0, y:0}, // position in network is not the one taken into account
+                    B: {id: 'B',x:0, y:0},
+                    C: {id: 'C', x:0, y:0}
+                },
+                links:[]
+            };
+            const networkStyle = {};
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodesEdges(nodesPosition, links, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(true);
+            expect(checkIntersection.mock.calls.length).toBeGreaterThanOrEqual(1);
+            expect(checkIntersection.mock.calls.length).toBeLessThanOrEqual(4);
+        });
+
+        it('should return true when a node overlaps with an edge because end of edge inside node', () => {
+            const nodesPosition = {
+                A: {x: 0, y: 0},
+                B: {x: 100, y: 0},
+                C: {x: 0, y: 1} // center Node C in rectangle of node A
+            };
+            const links = [
+                {source: 'A', target: 'B'}
+            ];
+            const network:Network = {
+                id:"network",
+                nodes: {
+                    A: {id: 'A',x:0, y:0}, // position in network is not the one taken into account
+                    B: {id: 'B',x:0, y:0},
+                    C: {id: 'C', x:0, y:0}
+                },
+                links:[]
+            };
+            const networkStyle = {};
+
+            // TEST
+            const result=CalculateOverlaps.isOverlapNodesEdges(nodesPosition, links, network, networkStyle);
+    
+            // EXPECT
+            expect(result).toBe(true);
+            expect(checkIntersection).not.toHaveBeenCalled();
+
+            checkIntersection.mockClear();
+        });
+    });
+    
+
+
+});
diff --git a/src/composables/__tests__/CalculateRelationCycle.test.ts b/src/composables/__tests__/CalculateRelationCycle.test.ts
index 91418dd1e628820382baff7ead4d02847dd11a41..6ad6e6ead41a2aec7608c0a18b2165ec4b72b628 100644
--- a/src/composables/__tests__/CalculateRelationCycle.test.ts
+++ b/src/composables/__tests__/CalculateRelationCycle.test.ts
@@ -3,7 +3,7 @@ import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 import { Subgraph, TypeSubgraph } from '../../types/Subgraph';
 import { LinkLayout, NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
 import { Coordinate } from '../../types/CoordinatesSize';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
+import { GraphStyleProperties } from "../../types/TypeVizCore";
 import { Ordering } from '../../types/EnumArgs';
 
 // Composable imports
@@ -60,12 +60,12 @@ describe('CalculateRelationCycle', () => {
 // 0. Get nodes *****************************************************************
 
     describe('getNodesIDPlacedInGroupCycle', () => {
-        it('should throw error because cycle group not defined in subgraphNetwork when trying to get node id placed inside ', () => {
+        it('should throw error because cycle group not defined in subgraphNetwork when trying to get node id placed inside ', async () => {
             // EXPECT
-            expect(()=>{CalculateRelationCycle.getNodesIDPlacedInGroupCycle(subgraphNetwork,"groupCycle")}).toThrow();
-
+            await expect(CalculateRelationCycle.getNodesIDPlacedInGroupCycle(subgraphNetwork, "groupCycle")).rejects.toThrow();
         });
-        it('should get nodes id placed inside group cycle ', () => {
+
+        it('should get nodes id placed inside group cycle ', async() => {
             // DATA
             const cycleGroup:Subgraph={
                 name: "groupCycle",
@@ -77,22 +77,23 @@ describe('CalculateRelationCycle', () => {
                 type: TypeSubgraph.CYCLEGROUP
             };
 
-            subgraphNetwork[TypeSubgraph.CYCLEGROUP]={};
-            subgraphNetwork[TypeSubgraph.CYCLEGROUP]["groupCycle"] = cycleGroup;
-
+            subgraphNetwork[TypeSubgraph.CYCLEGROUP]={groupCycle:cycleGroup};
             const nodesIDExpected=["node2","node3"];
 
             // TEST
-            const nodesID=CalculateRelationCycle.getNodesIDPlacedInGroupCycle(subgraphNetwork,"groupCycle");
+            const nodesID=await CalculateRelationCycle.getNodesIDPlacedInGroupCycle(subgraphNetwork,"groupCycle");
 
             // EXPECT
             expect(nodesID).toEqual(nodesIDExpected);
 
         });
+    });
+
+    describe('getNodesPlacedInGroupCycleAsArray', () => {
 
         it('should throw error because cycle group not defined in subgraphNetwork when trying to get node placed inside ', () => {
             // EXPECT
-            expect(()=>{CalculateRelationCycle.getNodesPlacedInGroupCycle(subgraphNetwork,"groupCycle")}).toThrow();
+            expect(()=>{CalculateRelationCycle.getNodesPlacedInGroupCycleAsArray(subgraphNetwork,"groupCycle")}).toThrow();
 
         });
 
@@ -122,8 +123,8 @@ describe('CalculateRelationCycle', () => {
             ];
 
             // TEST
-            const nodes=CalculateRelationCycle.getNodesPlacedInGroupCycle(subgraphNetwork,"groupCycle");
-            const nodesFixed=CalculateRelationCycle.getNodesPlacedInGroupCycle(subgraphNetwork,"groupCycle",true);
+            const nodes=CalculateRelationCycle.getNodesPlacedInGroupCycleAsArray(subgraphNetwork,"groupCycle");
+            const nodesFixed=CalculateRelationCycle.getNodesPlacedInGroupCycleAsArray(subgraphNetwork,"groupCycle",true);
 
             // EXPECT
             expect(nodes).toEqual(nodesExpected);
@@ -145,7 +146,7 @@ describe('CalculateRelationCycle', () => {
             const nodesExpected:{ id: string,x?:number, y?:number, fx?:number, fy?:number }[]=[];
 
             // TEST
-            const nodes=CalculateRelationCycle.getNodesPlacedInGroupCycle(subgraphNetwork,"groupCycle");
+            const nodes=CalculateRelationCycle.getNodesPlacedInGroupCycleAsArray(subgraphNetwork,"groupCycle");
 
             // EXPECT
             expect(nodes).toEqual(nodesExpected);
@@ -213,7 +214,7 @@ describe('CalculateRelationCycle', () => {
 
     describe('childNodeNotInCycle', () => {
 
-        it('should return child of a groupcycle, that are not in a cycle , no sorting (and a parent node in cycle)', () => {
+        it('should return child of a groupcycle, that are not in a cycle , no sorting (and a parent node in cycle)', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -229,7 +230,7 @@ describe('CalculateRelationCycle', () => {
             ];
 
             // TEST
-            const children=CalculateRelationCycle.childNodeNotInCycle(subgraphNetwork,listNodes,false);
+            const children=await CalculateRelationCycle.childNodeNotInCycle(subgraphNetwork,listNodes,false);
 
             // EXPECT
             expect(children).toEqual(childrenExpected);
@@ -237,7 +238,7 @@ describe('CalculateRelationCycle', () => {
 
         });
 
-        it('should return child of a groupcycle, that are not in a cycle ,sorting (no parent node in cycle)', () => {
+        it('should return child of a groupcycle, that are not in a cycle ,sorting (no parent node in cycle)', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -253,7 +254,7 @@ describe('CalculateRelationCycle', () => {
             ];
 
             // TEST
-            const children=CalculateRelationCycle.childNodeNotInCycle(subgraphNetwork,listNodes,true);
+            const children=await CalculateRelationCycle.childNodeNotInCycle(subgraphNetwork,listNodes,true);
 
             // EXPECT
             expect(children).toEqual(childrenExpected);
@@ -264,7 +265,7 @@ describe('CalculateRelationCycle', () => {
 
     describe('parentNodeNotInCycle', () => {
 
-        it('should return parent of a groupcycle, that are not in a cycle , no sorting (and a parent node in cycle)', () => {
+        it('should return parent of a groupcycle, that are not in a cycle , no sorting (and a parent node in cycle)', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -280,14 +281,14 @@ describe('CalculateRelationCycle', () => {
             ];
 
             // TEST
-            const parent=CalculateRelationCycle.parentNodeNotInCycle(subgraphNetwork,listNodes,false);
+            const parent=await CalculateRelationCycle.parentNodeNotInCycle(subgraphNetwork,listNodes,false);
 
             // EXPECT
             expect(parent).toEqual(parentExpected);
 
         });
 
-        it('should return parent of a groupcycle, that are not in a cycle , sorting (no parent node in cycle)', () => {
+        it('should return parent of a groupcycle, that are not in a cycle , sorting (no parent node in cycle)', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -303,7 +304,7 @@ describe('CalculateRelationCycle', () => {
             ];
 
             // TEST
-            const parent=CalculateRelationCycle.parentNodeNotInCycle(subgraphNetwork,listNodes,true);
+            const parent=await CalculateRelationCycle.parentNodeNotInCycle(subgraphNetwork,listNodes,true);
 
             // EXPECT
             expect(parent).toEqual(parentExpected);
@@ -314,13 +315,13 @@ describe('CalculateRelationCycle', () => {
 
     describe('neighborsGroupCycle', () => {
 
-        it('sould throw an error instead of returning parent/child of a group cycle , because no groupcycle', () => {
+        it('sould throw an error instead of returning parent/child of a group cycle , because no groupcycle', async() => {
             // EXPECT
-            expect(()=>{CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent")}).toThrow();
-            expect(()=>{CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child")}).toThrow();
+            await expect(CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent")).rejects.toThrow();
+            await expect(CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child")).rejects.toThrow();
         });
 
-        it('sould return parent/child of a group cycle , but no precalculatedPosition', () => {
+        it('sould return parent/child of a group cycle , but no precalculatedPosition', async() => {
 
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
@@ -341,8 +342,8 @@ describe('CalculateRelationCycle', () => {
             const resultExpected:string[]=[];
 
             // TEST
-            const resultParent=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent");
-            const resultChildren=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child");
+            const resultParent=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent");
+            const resultChildren=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child");
 
             // EXPECT
             expect(resultParent).toEqual(resultExpected);
@@ -350,7 +351,7 @@ describe('CalculateRelationCycle', () => {
 
         });
 
-        it('sould return parent/child of a group cycle with neighborsGroupCycle, no sorting', () => {
+        it('sould return parent/child of a group cycle with neighborsGroupCycle, no sorting', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -375,8 +376,8 @@ describe('CalculateRelationCycle', () => {
             const childrenExpected:string[]=["node1","node4","node0"];
 
             // TEST
-            const resultParent=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent",false);
-            const resultChildren=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child",false);
+            const resultParent=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent",false);
+            const resultChildren=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child",false);
 
             // EXPECT
             expect(resultParent).toEqual(parentExpected);
@@ -384,7 +385,7 @@ describe('CalculateRelationCycle', () => {
 
         });
 
-        it('sould return parent/child of a group cycle with neighborsGroupCycle, sorting', () => {
+        it('sould return parent/child of a group cycle with neighborsGroupCycle, sorting', async() => {
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
             inCycleMock.mockImplementation( (network,id)=>{
@@ -409,8 +410,8 @@ describe('CalculateRelationCycle', () => {
             const childrenExpected:string[]=["node1","node0","node4"];
 
             // TEST
-            const resultParent=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent",true);
-            const resultChildren=CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child",true);
+            const resultParent=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","parent",true);
+            const resultChildren=await CalculateRelationCycle.neighborsGroupCycle(subgraphNetwork,"groupCycle","child",true);
 
             // EXPECT
             expect(resultParent).toEqual(parentExpected);
@@ -466,9 +467,9 @@ describe('CalculateRelationCycle', () => {
 
     describe('sortLinksWithAllGroupCycle', () => {
 
-        it('should not change links order', () => {
+        it('should not change links order', async() => {
             // TEST
-            const result=CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,false);
+            const result=await CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,false);
 
             // EXPECT
             expect(result.subgraphNetwork).toEqual(subgraphNetwork);
@@ -476,7 +477,7 @@ describe('CalculateRelationCycle', () => {
 
         });
 
-        it('should change links order, with ordering in (parent)', () => {
+        it('should change links order, with ordering in (parent)', async() => {
 
             // MOCK
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
@@ -492,7 +493,7 @@ describe('CalculateRelationCycle', () => {
 
             const subgraph:Subgraph={
                 name: "groupCycle",
-                nodes: ["node2","node3"],
+                nodes: [],
                 precalculatedNodesPosition:  {
                     node2: { x: 2, y: 3 },
                     node3: { x: 3, y: 2 },
@@ -523,8 +524,7 @@ describe('CalculateRelationCycle', () => {
             ]
 
             // TEST
-            const result=CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,true);
-            //console.log(result.linksOrdered);
+            const result=await CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,true);
 
             // EXPECT
             expect(result.subgraphNetwork).toEqual(expectedSubgraphNetwork);
@@ -532,7 +532,7 @@ describe('CalculateRelationCycle', () => {
 
         });
 
-        it('should change links order, with ordering out (child)', () => {
+        it('should change links order, with ordering out (child)', async() => {
 
             // MOCK : declare node in cycle
             const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
@@ -577,15 +577,14 @@ describe('CalculateRelationCycle', () => {
             ]
 
             // TEST
-            const result=CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,true);
-            //console.log(result.linksOrdered);
+            const result=await CalculateRelationCycle.sortLinksWithAllGroupCycle(subgraphNetwork,true);
 
             // EXPECT
             expect(result.subgraphNetwork).toEqual(expectedSubgraphNetwork);
             expect(result.linksOrdered).toEqual(expectedLinksOrdered);
 
         });
-    });
+     });
     describe('getLinksForListNodes', () => {
 
         test('getLinksForListNodes', () => {
@@ -608,10 +607,10 @@ describe('CalculateRelationCycle', () => {
     });
 
 
-// 2. Get graph *****************************************************************
+// // 2. Get graph *****************************************************************
 
     describe('get Graph For Cycle Group', () => {
-        test('getListNodeLinksForCycleGroup', () => {
+        test('getListNodeLinksForCycleGroupAsArray', () => {
             // DATA 
             nodes.node2.metadataLayout={ [TypeSubgraph.CYCLEGROUP]: "groupCycle" };
             nodes.node3.metadataLayout={ [TypeSubgraph.CYCLEGROUP]: "groupCycle" };
@@ -648,8 +647,8 @@ describe('CalculateRelationCycle', () => {
             };
 
             // TEST
-            const resultNotFixed=CalculateRelationCycle.getListNodeLinksForCycleGroup(subgraphNetwork,"groupCycle",false);
-            const resultFixed=CalculateRelationCycle.getListNodeLinksForCycleGroup(subgraphNetwork,"groupCycle",true);
+            const resultNotFixed=CalculateRelationCycle.getListNodeLinksForCycleGroupAsArray(subgraphNetwork,"groupCycle",false);
+            const resultFixed=CalculateRelationCycle.getListNodeLinksForCycleGroupAsArray(subgraphNetwork,"groupCycle",true);
 
             // EXPECT
             expect(resultNotFixed).toEqual(resultExpectedNotFixed);
@@ -693,7 +692,7 @@ describe('CalculateRelationCycle', () => {
         });
     });
 
-// 3. Get relation cycle *****************************************************************
+// // 3. Get relation cycle *****************************************************************
 
         // parentCycle
     describe('parentCycle', () => {
diff --git a/src/composables/__tests__/CalculateSize.test.ts b/src/composables/__tests__/CalculateSize.test.ts
index 96be7537c7a97cedbb7da35e1c8bed5555d3b4de..41bf2425b41a0debb0de9134b2b7a10ca77dcd4e 100644
--- a/src/composables/__tests__/CalculateSize.test.ts
+++ b/src/composables/__tests__/CalculateSize.test.ts
@@ -1,7 +1,5 @@
 // Type imports
-import { Node} from '@metabohub/viz-core/src/types/Node';
-import { Network } from '@metabohub/viz-core/src/types/Network';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
+import { Network, GraphStyleProperties } from "../../types/TypeVizCore";
 import { Coordinate } from '../../types/CoordinatesSize';
 import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 import { NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
diff --git a/src/composables/__tests__/CheckNetwork.test.ts b/src/composables/__tests__/CheckNetwork.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e628267bf104cadcbd461da8bccbd29c220bca24
--- /dev/null
+++ b/src/composables/__tests__/CheckNetwork.test.ts
@@ -0,0 +1,439 @@
+// Type imports
+import {Network, Node, Link, GraphStyleProperties} from '../../types/TypeVizCore';
+
+// Composable imports
+import * as CheckNetwork from '../CheckNetwork';
+
+describe('CheckNetwork', () => {
+
+    describe('CheckNetwork.checkValidIdForViz', () => {
+    
+        // Test valid alphabetic IDs
+        it('should accept valid alphabetic ID', () => {
+            expect(() => CheckNetwork.checkValidIdForViz("valid_id123")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("A1B2C3")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("validID")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("valid_id_with_underscore")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("éclair")).not.toThrow(); // Testing with extended ASCII
+        });
+    
+        // Test valid numerals
+        it('should accept valid numerals', () => {
+            expect(() => CheckNetwork.checkValidIdForViz("123")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("-123.45")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz(".456")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("0.1")).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz("-0.1")).not.toThrow();
+        });
+    
+        // Test valid double-quoted strings
+        it('should accept valid double-quoted strings', () => {
+            expect(() => CheckNetwork.checkValidIdForViz('"Hello, World!"')).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz('"This is a test with an escaped quote: \\"')).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz('"Just a string"')).not.toThrow();
+        });
+    
+        // Test valid HTML strings
+        it('should accept valid HTML strings', () => {
+            expect(() => CheckNetwork.checkValidIdForViz('<div>')).not.toThrow();
+            expect(() => CheckNetwork.checkValidIdForViz('<span class="test">')).not.toThrow();
+        });
+    
+        // Test invalid cases
+        it('should throw an error for invalid strings', () => {
+            expect(() => CheckNetwork.checkValidIdForViz("123invalid")).toThrow('Invalid string: 123invalid');
+            expect(() => CheckNetwork.checkValidIdForViz("!@#$%")).toThrow('Invalid string: !@#$%');
+            expect(() => CheckNetwork.checkValidIdForViz("")).toThrow('Invalid string: ');
+            expect(() => CheckNetwork.checkValidIdForViz("invalid-id-!")).toThrow('Invalid string: invalid-id-!');
+            expect(() => CheckNetwork.checkValidIdForViz("<div>Invalid</span>")).toThrow('Invalid string: <div>Invalid</span>');
+        });
+    });
+    
+
+    describe('checkNodeFormat', () => {
+
+        const validNode: Node = { id: '1', x: 0, y: 0 };
+        const validNodeWithOptionalProps: Node = {
+            id: '1',
+            x: 10,
+            y: 20,
+            label: 'Node 1',
+            classes: ['class1'],
+            hidden: false,
+            selected: true,
+            metadata: { key: 'value' }
+        };
+
+        it('should pass with a valid node without exact interface check', () => {
+            expect(() => CheckNetwork.checkNodeFormat('1', validNode, false)).not.toThrow();
+            expect(() => CheckNetwork.checkNodeFormat('1', validNodeWithOptionalProps, false)).not.toThrow();
+        });
+
+        it('should pass with a valid node with exact interface check', () => {
+            expect(() => CheckNetwork.checkNodeFormat('1', validNode, true)).not.toThrow();
+            expect(() => CheckNetwork.checkNodeFormat('1', validNodeWithOptionalProps, true)).not.toThrow();
+        });
+
+        it('should pass with a node with too many key with checkExactKeysInterface=false', () => {
+            const invalidNode = { id: '1', x: 0, y: 0, extraKey: 'extraValue' };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).not.toThrow();
+        });
+
+        it('should throw an error if node is not an object', () => {
+            expect(() => CheckNetwork.checkNodeFormat('1', 'not-an-object' as any, false)).toThrow('Node 1 is not an object.');
+        });
+
+        it('should throw an error if node lacks mandatory key id', () => {
+            const noIDNode:any = { x: 0, y: 0 };
+            expect(() => CheckNetwork.checkNodeFormat('1', noIDNode, false)).toThrow('Node 1 lacks key: id');
+        });
+
+        it('should throw an error if node lacks mandatory key x', () => {
+            const invalidNode:any = { id: '1', y: 0 };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 lacks key: x');
+        });
+
+        it('should throw an error if node lacks mandatory key y', () => {
+            const invalidNode :any= { id: '1', x: 0 };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 lacks key: y');
+        });
+
+        it('should throw an error if node has invalid type for key id', () => {
+            const invalidNode:any = { id: 13, x: 0, y: '0' }; // id should be a string
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 has an invalid type for key id: expected string, got number');
+        });
+
+        it('should throw an error if node has invalid type for key x', () => {
+            const invalidNode:any = { id: '1', x: '0', y: 0 }; // x should be a number
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 has an invalid type for key x: expected number, got string');
+        });
+
+        it('should throw an error if node has invalid type for key y', () => {
+            const invalidNode:any = { id: '1', x: 0, y: '0' }; // y should be a number
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 has an invalid type for key y: expected number, got string');
+        });
+
+        it('should throw an error if node id and key mismatch', () => {
+            const invalidNode:Node = { id: '2', x: 0, y: 0 };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node id and key mismatch: expected 1 for id, got 2');
+        });
+
+        it('should throw an error if checkExactInterface is true and node has extra keys', () => {
+            const invalidNode = { id: '1', x: 0, y: 0, extraKey: 'extraValue' };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, true)).toThrow('Node 1 has an invalid key: extraKey');
+        });
+
+        it('should throw an error if node has metadataLayout when checkExactInterface is false', () => {
+            const invalidNode = { id: '1', x: 0, y: 0, metadataLayout: {} };
+            expect(() => CheckNetwork.checkNodeFormat('1', invalidNode, false)).toThrow('Node 1 contains metadataLayout, which is not allowed.');
+        });
+
+        it('should pass with check of id for Viz', () => {
+            expect(() => CheckNetwork.checkNodeFormat('1', validNode, false,true)).not.toThrow();
+        });
+
+        it('should throw an error if id is not valid for Viz', () => {
+            const invalidNode = { id: '1node', x: 0, y: 0, metadataLayout: {} };
+            expect(() => CheckNetwork.checkNodeFormat('1node', invalidNode, false,true)).toThrow('Invalid string: 1node');
+        });
+
+    });
+
+    describe('checkLinkFormat', () => {
+        const validNode1: Node = { id: '1', x: 0, y: 0 };
+        const validNode2: Node = { id: '2', x: 10, y: 10 };
+        const validNetwork: Network = {
+            id: 'network1',
+            nodes: {
+                '1': validNode1,
+                '2': validNode2
+            },
+            links: []
+        };
+
+        const validLink: Link = { id: 'link1', source: validNode1, target: validNode2 };
+
+        it('should pass with a valid link', () => {
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, validLink, false)).not.toThrow();
+        });
+
+        it('should throw an error if link source is not in network', () => {
+            const invalidLinkSource: Link = { id: 'link2', source: { id: '3', x: 0, y: 0 }, target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLinkSource, false)).toThrow('Link link2 has no source node in the network.');
+        });
+
+        it('should throw an error if link source is invalid (no pointer)', () => {
+            const invalidLinkPointer: Link = { id: 'link3', source: { id: '1', x: 0, y: 0 }, target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLinkPointer, false)).toThrow('Link link3 has an invalid source node (no pointer).');
+        });
+
+        it('should throw an error if link target is not in network', () => {
+            const invalidLinkTarget: Link = { id: 'link4', source: validNode1, target: { id: '3', x: 0, y: 0 } };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLinkTarget, false)).toThrow('Link link4 has no target node in the network.');
+        });
+
+        it('should throw an error if link target is invalid (no pointer)', () => {
+            const invalidLinkPointerTarget: Link = { id: 'link5', source: validNode1, target: { id: '2', x: 10, y: 10 } };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLinkPointerTarget, false)).toThrow('Link link5 has an invalid target node (no pointer).');
+        });
+
+        it('should throw an error if link lacks the key id', () => {
+            const invalidLink: Partial<Link> = { source: validNode1, target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as Link, false))
+                .toThrow('Link lacks key: id');
+        });
+    
+        it('should throw an error if link lacks the key source', () => {
+            const invalidLink: Partial<Link> = { id: 'link2', target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as Link, false))
+                .toThrow('Link lacks key: source');
+        });
+    
+        it('should throw an error if link lacks the key target', () => {
+            const invalidLink: Partial<Link> = { id: 'link3', source: validNode1 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as Link, false))
+                .toThrow('Link lacks key: target');
+        });
+    
+        it('should throw an error if link id is not an string', () => {
+            const invalidLink = { id: 45, source: validNode1, target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as any, false))
+                .toThrow('Link has an invalid type for key id: expected string, got number');
+        });
+
+        it('should throw an error if link source is not an object', () => {
+            const invalidLink = { id: 'link4', source: 'invalid-source', target: validNode2 };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as any, false))
+                .toThrow('Link has an invalid type for key source: expected object, got string');
+        });
+    
+        it('should throw an error if link target is not an object', () => {
+            const invalidLink = { id: 'link5', source: validNode1, target: 'invalid-target' };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink as any, false))
+                .toThrow('Link has an invalid type for key target: expected object, got string');
+        });
+
+        it('should pass with exact keys interface when all keys match', () => {
+            const validLinkWithOptionalProps: Link = { 
+                id: 'link10', 
+                source: validNode1, 
+                target: validNode2, 
+                label: 'link-label', 
+                classes: ['class1'], 
+                type: 'link-type', 
+                relation: 'relation1', 
+                directed: true,
+                metadata: { key: 'value' }
+            };
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, validLinkWithOptionalProps, true)).not.toThrow();
+        });
+    
+        it('should throw an error if link has extra keys when exact keys interface is checked', () => {
+            const invalidLinkWithExtraKey: Link = { 
+                id: 'link11', 
+                source: validNode1, 
+                target: validNode2, 
+                extraKey: 'extraValue' // Invalid key
+            } as any;
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLinkWithExtraKey, true))
+                .toThrow('Link has an invalid key: extraKey');
+        });
+
+        it('should throw an error if link not an object', ()=>{
+            const invalidLink:any="linkString";
+            expect(() => CheckNetwork.checkLinkFormat(validNetwork, invalidLink, true))
+                .toThrow('Link is not an object.');
+        })
+
+       
+
+    });
+
+    describe('checkNetworkFormat', () => {
+        const validNode1: Node = { id: '1', x: 0, y: 0 };
+        const validNode2: Node = { id: '2', x: 10, y: 10 };
+        const validLink: Link = { id: 'link1', source: validNode1, target: validNode2 };
+        const validNetwork: Network = {
+            id: 'network1',
+            nodes: {
+                '1': validNode1,
+                '2': validNode2
+            },
+            links: [validLink]
+        };
+
+        // Test: Network must be an object
+        it('should throw an error if the network is not an object', async () => {
+            await expect(CheckNetwork.checkNetworkFormat(56 as any, false,false)).rejects
+                .toThrow('Network is not an object.');
+        });
+
+        // Test: Mandatory keys are present
+        it('should throw an error if the network lacks a mandatory key (id)', async () => {
+            const networkWithoutId = { nodes: {}, links: [] } as any;
+            await expect(CheckNetwork.checkNetworkFormat(networkWithoutId, false,false)).rejects
+                .toThrow('Network lacks key: id');
+        });
+
+        it('should throw an error if the network lacks a mandatory key (nodes)', async () => {
+            const networkWithoutNodes = { id: 'network1', links: [] } as any;
+            await expect(CheckNetwork.checkNetworkFormat(networkWithoutNodes, false,false)).rejects
+                .toThrow('Network lacks key: nodes');
+        });
+
+        it('should throw an error if the network lacks a mandatory key (links)', async () => {
+            const networkWithoutLinks = { id: 'network1', nodes: {} } as any;
+            await expect(async () => await CheckNetwork.checkNetworkFormat(networkWithoutLinks, false,false)).rejects
+                .toThrow('Network lacks key: links');
+        });
+
+        // Test: Exact interface check
+        it('should pass if the network contains only the exact keys', async () => {
+            await expect(CheckNetwork.checkNetworkFormat(validNetwork, true,false)).resolves.not.toThrow();
+        });
+
+        it('should throw an error if the network contains extra keys when exact interface is checked', async () => {
+            const networkWithExtraKey = { ...validNetwork, extraKey: 'extraValue' } as any;
+            await expect(CheckNetwork.checkNetworkFormat(networkWithExtraKey, true,false)).rejects
+                .toThrow('Network has an invalid key: extraKey');
+        });
+
+        it('should pass if the network contains extra keys when exact interface is not checked', async() => {
+            const networkWithExtraKey = { ...validNetwork, extraKey: 'extraValue' } as any;
+            await expect(CheckNetwork.checkNetworkFormat(networkWithExtraKey, false,false)).resolves.not.toThrow();
+        });
+
+        // Test: Node validation failure
+        it('should throw an error if a node in the network is invalid', async () => {
+            const invalidNode = { id: '3', y: 0 }; // Missing the 'x' key
+            const networkWithInvalidNode = {
+                ...validNetwork,
+                nodes: { '3': invalidNode as Node }
+            };
+
+            // Assuming checkNodeFormat throws the appropriate error
+            await expect(CheckNetwork.checkNetworkFormat(networkWithInvalidNode, false,false)).rejects
+                .toThrow('Node 3 lacks key: x');
+        });
+
+        // Test: Link validation failure
+        it('should throw an error if a link in the network is invalid', async () => {
+            const invalidLink = { id: 'link1', source: validNode1 }; // Missing the 'target' key
+            const networkWithInvalidLink = {
+                ...validNetwork,
+                links: [invalidLink as Link]
+            };
+
+            // Assuming checkLinkFormat throws the appropriate error
+            await expect(CheckNetwork.checkNetworkFormat(networkWithInvalidLink, false,false)).rejects
+                .toThrow('Link lacks key: target');
+        });
+
+        // Test: check if of node for Viz
+        it('should pass with check of id for Viz', async () => {
+            await expect(CheckNetwork.checkNetworkFormat(validNetwork, false,true)).resolves.not.toThrow();
+        });
+
+        it('should throw an error if id is not valid for Viz', async () => {
+            const networkWithInvalidNodeID = {
+                ...validNetwork,
+                nodes: { '1node': {id:"1node",x:0,y:0} }
+            };
+            await expect(CheckNetwork.checkNetworkFormat(networkWithInvalidNodeID, false,true)).rejects.toThrow('Invalid string: 1node');
+        });
+
+    });
+
+    
+    describe('checkNetworkStyleFormat', () => {
+
+        const validNode1: Node = { id: '1', x: 0, y: 0, classes: ['class1'] };
+        const validNode2: Node = { id: '2', x: 10, y: 10, classes: ['class2'] };
+        const validLink: Link = { id: 'link1', source: validNode1, target: validNode2, classes: ['linkClass1'] };
+        const validNetwork: Network = {
+            id: 'network1',
+            nodes: {
+                '1': validNode1,
+                '2': validNode2
+            },
+            links: [validLink]
+        };
+
+        const validGraphStyle: GraphStyleProperties = {
+            nodeStyles: {
+                class1: { stroke: 'blue' },
+                class2: { stroke: 'red' }
+            },
+            linkStyles: {
+                linkClass1: { stroke: 'green' }
+            }
+        };
+
+        // Test: All node classes have corresponding styles
+        it('should pass if all node classes have corresponding styles in networkStyle.nodeStyles', () => {
+            expect( () => CheckNetwork.checkNetworkStyleFormat(validNetwork, validGraphStyle)).not.toThrow();
+        });
+
+        // Test: All link classes have corresponding styles
+        it('should pass if all link classes have corresponding styles in networkStyle.linkStyles', () => {
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(validNetwork, validGraphStyle)).not.toThrow();
+        });
+
+        // Test: Missing nodeStyles when node classes exist
+        it('should throw an error if node classes are present but no nodeStyles are provided', () => {
+            const missingNodeStyle: GraphStyleProperties = {
+                linkStyles: validGraphStyle.linkStyles // Missing nodeStyles
+            };
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(validNetwork, missingNodeStyle))
+                .toThrow('Node classes are present in the network but no nodeStyles are provided.');
+        });
+
+        // Test: Missing linkStyles when link classes exist
+        it('should throw an error if link classes are present but no linkStyles are provided', () => {
+            const missingLinkStyle: GraphStyleProperties = {
+                nodeStyles: validGraphStyle.nodeStyles // Missing linkStyles
+            };
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(validNetwork, missingLinkStyle))
+                .toThrow('Link classes are present in the network but no linkStyles are provided.');
+        });
+
+        // Test: Node class missing in nodeStyles
+        it('should throw an error if a node class is missing in networkStyle.nodeStyles', () => {
+            const incompleteNodeStyles: GraphStyleProperties = {
+                nodeStyles: { class1: { stroke: 'blue' } }, // Missing class2
+                linkStyles: validGraphStyle.linkStyles
+            };
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(validNetwork, incompleteNodeStyles))
+                .toThrow('Node class class2 is missing in networkStyle.nodeStyles.');
+        });
+
+        // Test: Link class missing in linkStyles
+        it('should throw an error if a link class is missing in networkStyle.linkStyles', () => {
+            const incompleteLinkStyles: GraphStyleProperties = {
+                nodeStyles: validGraphStyle.nodeStyles,
+                linkStyles: {} // Missing linkClass1
+            };
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(validNetwork, incompleteLinkStyles))
+                .toThrow('Link class linkClass1 is missing in networkStyle.linkStyles.');
+        });
+
+        // Test: No node or link classes present, no styles needed
+        it('should pass if there are no node or link classes and no styles are provided', () => {
+            const simpleNetwork: Network = {
+                id: 'network1',
+                nodes: {
+                    '1': { id: '1', x: 0, y: 0 },
+                    '2': { id: '2', x: 10, y: 10 }
+                },
+                links: []
+            };
+
+            const emptyGraphStyle: GraphStyleProperties = {};
+            expect( () =>  CheckNetwork.checkNetworkStyleFormat(simpleNetwork, emptyGraphStyle)).not.toThrow();
+        });
+
+
+
+    });
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/ConvertFromNetwork.test.ts b/src/composables/__tests__/ConvertFromNetwork.test.ts
index c2f5f3df12846a57bb48f2cefec6261fcfc1bc3f..f6076ff1a1f9d6c2a08c1a88fa3c5600494f9105 100644
--- a/src/composables/__tests__/ConvertFromNetwork.test.ts
+++ b/src/composables/__tests__/ConvertFromNetwork.test.ts
@@ -1,11 +1,8 @@
 // Type imports
-import { Network } from '@metabohub/viz-core/src/types/Network';
-import { Node } from '@metabohub/viz-core/src/types/Node';
-import { Link } from '@metabohub/viz-core/src/types/Link';
+import { Network , Node, Link, GraphStyleProperties} from "../../types/TypeVizCore";
 import { Subgraph, TypeSubgraph } from '../../types/Subgraph';
 import { LinkLayout, NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
 import { Ordering } from '../../types/EnumArgs';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
 import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 
 
@@ -13,12 +10,13 @@ import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 import  * as ConvertFromNetwork from '../ConvertFromNetwork';
 import * as GetSetAttributsNodes from "../GetSetAttributsNodes";
 import * as CalculateRelationCycle from  "../CalculateRelationCycle";
+import * as SubgraphForViz from "../SubgraphForViz";
+import * as CalculateSize from "../CalculateSize";
 
 
 // General imports
 import { Graph } from "@viz-js/viz";
 import * as GDS from 'graph-data-structure';
-import exp from 'constants';
 
 
 // Mocking GDS.Graph
@@ -72,11 +70,6 @@ describe('ConvertFromNetwork', () => {
 
     });
 
-    afterEach(() => {
-        jest.clearAllMocks();
-    });
-
-
 
     describe('networktoNetworkLayout', () => {
 
@@ -175,10 +168,47 @@ describe('ConvertFromNetwork', () => {
     describe('network for Viz', () => {
 
         let subgraphNetwork: SubgraphNetwork;
-        let inCycleMock: jest.SpyInstance;
-        let sortLinksWithAllGroupCycleMock: jest.SpyInstance;
         let cycleGroup:Subgraph;
 
+        let subgraphDotMock: jest.SpyInstance;
+        let addMainChainForVizMock: jest.SpyInstance;
+
+        const getSizeNodePixelMock = jest.spyOn(CalculateSize, 'getSizeNodePixel');
+        getSizeNodePixelMock.mockReturnValue({width: 25, height: 25});
+
+        const pixelsToInchesMock = jest.spyOn(CalculateSize, 'pixelsToInches');
+        pixelsToInchesMock.mockImplementation((value: number) => {
+            if (value) {
+                return 0.35;
+            }
+            return 0;
+        });
+
+        const inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
+        inCycleMock.mockImplementation( (network,id)=>{
+            return id==="node4" || id==="node5";
+        });
+
+        const sortLinksWithAllGroupCycleMock = jest.spyOn(CalculateRelationCycle, 'sortLinksWithAllGroupCycle');
+        sortLinksWithAllGroupCycleMock.mockImplementation(async (subgraphNetworkArg:SubgraphNetwork) => {
+            return {
+                subgraphNetwork:subgraphNetworkArg,
+                linksOrdered:subgraphNetworkArg.network.links
+            };
+        });
+
+        const cycleMetanodeLinkMock = jest.spyOn(CalculateRelationCycle, 'cycleMetanodeLink');
+        cycleMetanodeLinkMock.mockImplementation((link:LinkLayout,cycle?:boolean)=>{
+            if(cycle && link.target.id==="node4"){
+                return {inCycle:["cycleGroup"],tail:link.source.id,head:"cycleGroup"}
+            }
+            if(cycle && link.target.id==="node5" && link.source.id==="node4"){
+                return {inCycle:["cycleGroup"],tail:"cycleGroup",head:"cycleGroup"}
+            }
+            return {inCycle:[],tail:link.source.id,head:link.target.id}
+        }); 
+        
+
         beforeEach(() => {
 
             const mainChain :Subgraph={
@@ -203,11 +233,8 @@ describe('ConvertFromNetwork', () => {
                 [TypeSubgraph.CYCLEGROUP]: {cycleGroup:cycleGroup},
             };
 
-            subgraphNetwork.network.nodes["node4"].metadataLayout={
-                [TypeSubgraph.CYCLEGROUP]:"cycleGroup"
-            };
+            // need to declare the main chain in the node for the case group (instead of cluster)
             subgraphNetwork.network.nodes["node5"].metadataLayout={
-                [TypeSubgraph.CYCLEGROUP]:"cycleGroup",
                 [TypeSubgraph.MAIN_CHAIN]:["mainChain"]
             };
             subgraphNetwork.network.nodes["node2"].metadataLayout={
@@ -217,42 +244,45 @@ describe('ConvertFromNetwork', () => {
                 [TypeSubgraph.MAIN_CHAIN]:["mainChain"]
             };
 
-
-            // MOCK 
-            inCycleMock = jest.spyOn(GetSetAttributsNodes, 'inCycle');
-            inCycleMock.mockImplementation( (network,id)=>{
-                return id==="node4" || id==="node5";
+            // MOCK
+            addMainChainForVizMock = jest.spyOn(SubgraphForViz, 'addMainChainForViz');
+            addMainChainForVizMock.mockImplementation((vizgraph:Graph)=>{
+                vizgraph.subgraphs=[
+                    {"name": "cluster_mainChain", "nodes": [
+                    {"name": "node2"}, 
+                    {"name": "node3"}, 
+                    {"name": "cycleGroup"}
+                    ]}];
+                return vizgraph;
             });
 
-            sortLinksWithAllGroupCycleMock = jest.spyOn(CalculateRelationCycle, 'sortLinksWithAllGroupCycle');
-            sortLinksWithAllGroupCycleMock.mockReturnValue({
-                    subgraphNetwork:subgraphNetwork,
-                    linksOrdered:subgraphNetwork.network.links
-            });
+        });
 
+        afterEach(() => {
+            addMainChainForVizMock.mockClear();
         });
 
-        
 
-        it('should throw error when converting network to DOT string, but no size of cycleGroup', () => {
+        it('should throw error when converting network to DOT string, because no size of cycleGroup', async () => {
             // DATA
             delete cycleGroup.width;
             delete cycleGroup.height;
 
             // EXPECT
-            expect(()=>{ConvertFromNetwork.networkToViz(subgraphNetwork,true)}).toThrow();
+            await expect(ConvertFromNetwork.networkToViz(subgraphNetwork,true)).rejects.toThrow();
         });
 
-        it('should throw error when converting network to DOT string, change order but no ordering', () => {
+        it('should throw error when converting network to DOT string, change order but no ordering', async() => {
 
             // DATA
             delete cycleGroup.ordering;
            
             // EXPECT
-            expect(()=>{ConvertFromNetwork.networkToViz(subgraphNetwork,true,true,"cluster",true)}).toThrow();
+            await expect(ConvertFromNetwork.networkToViz(subgraphNetwork,true,true,"cluster",true)).rejects.toThrow();
         });
 
-        it('should convert network to DOT string, default args', () => {
+        it('should convert network to DOT string, default args', async () => {
+
             // DATA
             const resultExpected:Graph={
                 "graphAttributes":{},
@@ -266,19 +296,29 @@ describe('ConvertFromNetwork', () => {
                     {"name":"node1","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
                     {"name":"node2","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
                     {"name":"node3","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
-                    {"name":"cycleGroup","attributes":{"fixedsize":true,"height":0.01,"width":0.01}}],
+                    {"name":"cycleGroup","attributes":{"fixedsize":true,"height":0.35,"width":0.35}}],
 
             "subgraphs":[{"name":"cluster_mainChain","nodes":[{"name":"node2"},{"name":"node3"},{"name":"cycleGroup"}]}]}
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,true,true,"cluster",false);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,true,true,"cluster",false);
 
             // EXPECT
             expect(result).toEqual(resultExpected);
             
         });
 
-        it('should convert network to DOT string, no cyle', () => {
+        it('should convert network to DOT string, no cyle', async () => {
+            // MOCK
+            addMainChainForVizMock.mockImplementationOnce((vizgraph:Graph)=>{
+                vizgraph.subgraphs=[
+                    {"name": "cluster_mainChain", "nodes": [
+                    {"name": "node2"}, 
+                    {"name": "node3"}, 
+                    {"name": "node5"}]}]
+                return vizgraph;
+            });
+
             // DATA
             const resultExpected:Graph={
                 "graphAttributes": {},
@@ -307,14 +347,15 @@ describe('ConvertFromNetwork', () => {
               
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,false);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,false);
             
             // EXPECT
             expect(result).toEqual(resultExpected);
             
         });
 
-        it('should convert network to DOT string, add no nodes', () => {
+        it('should convert network to DOT string, add no nodes', async() => {
+
             // DATA
             const resultExpected:Graph={
                 "graphAttributes": {},
@@ -325,7 +366,7 @@ describe('ConvertFromNetwork', () => {
                   {"tail": "node3", "head": "cycleGroup"}
                 ],
                 "nodes": [
-                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.01, "width": 0.01}}
+                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}}
                 ],
                 "subgraphs": [
                   {"name": "cluster_mainChain", "nodes": [
@@ -338,13 +379,24 @@ describe('ConvertFromNetwork', () => {
               
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,true,false);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,true,false);
 
             // EXPECT
-            expect(result).toEqual(resultExpected);          
+            expect(result).toEqual(resultExpected);      
         });
 
-        it('should convert network to DOT string, add no nodes et no cycle', () => {
+        it('should convert network to DOT string, add no nodes et no cycle', async () => {
+
+            // MOCK
+            addMainChainForVizMock.mockImplementationOnce((vizgraph:Graph)=>{
+                vizgraph.subgraphs=[
+                    {"name": "cluster_mainChain", "nodes": [
+                    {"name": "node2"}, 
+                    {"name": "node3"}, 
+                    {"name": "node5"}]}]
+                return vizgraph;
+            });
+
             // DATA
             const resultExpected:Graph={
                 "graphAttributes": {},
@@ -367,13 +419,14 @@ describe('ConvertFromNetwork', () => {
               
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,false,false);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,false,false);
 
             // EXPECT
             expect(result).toEqual(resultExpected);          
         });
 
-        it('should convert network to DOT string, group for main chain', () => {
+        it('should convert network to DOT string, group for main chain', async () => {
+
             // DATA
             const resultExpected:Graph={
                 "graphAttributes": {},
@@ -387,21 +440,28 @@ describe('ConvertFromNetwork', () => {
                   {"name": "node1", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true}}, 
                   {"name": "node2", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true, "group": "mainChain"}}, 
                   {"name": "node3", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true, "group": "mainChain"}}, 
-                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.01, "width": 0.01}}
+                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}}
                 ],
                 "subgraphs": []
               }
               
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,true,true, "group");
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,true,true, "group");
 
             // EXPECT
             expect(result).toEqual(resultExpected);
+            expect(addMainChainForVizMock).not.toHaveBeenCalled();
             
         });
 
-        it('should convert network to DOT string, several cycleGroup', () => {
+        it('should convert network to DOT string, several cycleGroup', async() => {
+            // MOCK 
+            cycleMetanodeLinkMock.mockReturnValueOnce({inCycle:["cycleGroup2"],tail:"cycleGroup2",head:"node2"})
+            .mockReturnValueOnce({inCycle:[],tail:"node2",head:"node3"})
+            .mockReturnValueOnce({inCycle:["cycleGroup"],tail:"node3",head:"cycleGroup"})
+            .mockReturnValueOnce({inCycle:["cycleGroup"],tail:"cycleGroup",head:"cycleGroup"});
+
             // DATA
             const cycleGroup2 :Subgraph={
                 name: "cycleGroup2",
@@ -410,9 +470,7 @@ describe('ConvertFromNetwork', () => {
                 height: 3,
                 type: TypeSubgraph.CYCLEGROUP,
             };
-            subgraphNetwork.network.nodes["node1"].metadataLayout={
-                [TypeSubgraph.CYCLEGROUP]:"cycleGroup2"
-            };
+
             if(subgraphNetwork[TypeSubgraph.CYCLEGROUP]){
                 subgraphNetwork[TypeSubgraph.CYCLEGROUP].cycleGroup2=cycleGroup2;
             }else{
@@ -431,8 +489,8 @@ describe('ConvertFromNetwork', () => {
                 {"name": "node1", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}},
                 {"name": "node2", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}},
                 {"name": "node3", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}},
-                {"name": "cycleGroup2", "attributes": {"fixedsize": true, "height": 0.04, "width": 0.04}},
-                {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.01, "width": 0.01}}
+                {"name": "cycleGroup2", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}},
+                {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35}}
                 ],
                 "subgraphs": [
                 {"name": "cluster_mainChain", "nodes": [
@@ -444,16 +502,16 @@ describe('ConvertFromNetwork', () => {
                 }
                 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork);
 
             // EXPECT
             expect(result).toEqual(resultExpected);
             
         });
 
-        it('should convert network to DOT string, order change', () => {
+        it('should convert network to DOT string, order change', async() => {
             // MOCK 
-            sortLinksWithAllGroupCycleMock.mockReturnValue({
+            sortLinksWithAllGroupCycleMock.mockReturnValueOnce(Promise.resolve({
                 subgraphNetwork:subgraphNetwork,
                 linksOrdered: [
                     { id: 'link', source: nodes.node3 , target:nodes.node4 },
@@ -461,7 +519,7 @@ describe('ConvertFromNetwork', () => {
                     { id: 'link', source: nodes.node2 , target:nodes.node3 },
                     { id: 'link', source: nodes.node4 , target:nodes.node5 }
                 ]
-            });
+            }));
 
             // DATA
             const resultExpected:Graph={
@@ -477,7 +535,7 @@ describe('ConvertFromNetwork', () => {
                   {"name": "node1", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true}}, 
                   {"name": "node2", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true}}, 
                   {"name": "node3", "attributes": {"height": 0.35, "width": 0.35, "fixedsize": true}}, 
-                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.01, "width": 0.01, "ordering": "in"}}
+                  {"name": "cycleGroup", "attributes": {"fixedsize": true, "height": 0.35, "width": 0.35, "ordering": "in"}}
                 ],
                 "subgraphs": [
                   {"name": "cluster_mainChain", "nodes": [
@@ -490,7 +548,7 @@ describe('ConvertFromNetwork', () => {
               
 
             // TEST
-            const result = ConvertFromNetwork.networkToViz(subgraphNetwork,true,true, "cluster",true);
+            const result = await ConvertFromNetwork.networkToViz(subgraphNetwork,true,true, "cluster",true);
 
             // EXPECT
             expect(result).toEqual(resultExpected);    
@@ -498,6 +556,10 @@ describe('ConvertFromNetwork', () => {
     
          
         test('graphVizToDot', () => {
+            // MOCK
+            subgraphDotMock = jest.spyOn(SubgraphForViz, 'subgraphDot');
+            subgraphDotMock.mockReturnValue("subgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\n");
+
             // DATA 
             const vizGraph:Graph={
                 "graphAttributes":{},
@@ -511,12 +573,12 @@ describe('ConvertFromNetwork', () => {
                     {"name":"node1","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
                     {"name":"node2","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
                     {"name":"node3","attributes":{"height":0.35,"width":0.35,"fixedsize":true}},
-                    {"name":"cycleGroup","attributes":{"fixedsize":true,"height":0.01,"width":0.01}}],
+                    {"name":"cycleGroup","attributes":{"fixedsize":true,"height":0.35,"width":0.35}}],
 
             "subgraphs":[{"name":"cluster_mainChain","nodes":[{"name":"node2"},{"name":"node3"},{"name":"cycleGroup"}]}]}
 
-            const resultExpected = "strict digraph G {\n graph []\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.01\", width=\"0.01\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\n}"
-            const resultExpectedSubgraphLast ="strict digraph G {\n graph []\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.01\", width=\"0.01\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\n}"
+            const resultExpected = "strict digraph G {\n graph []\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.35\", width=\"0.35\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\n}"
+            const resultExpectedSubgraphLast ="strict digraph G {\n graph []\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.35\", width=\"0.35\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\n}"
 
             // TEST
             const result = ConvertFromNetwork.graphVizToDot(vizGraph,true);
@@ -528,12 +590,16 @@ describe('ConvertFromNetwork', () => {
 
         });
 
-        test('networkToDOT', () => {
+        test('networkToDOT', async () => {
+            // MOCK
+            subgraphDotMock = jest.spyOn(SubgraphForViz, 'subgraphDot');
+            subgraphDotMock.mockReturnValue("subgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\n");
+        
             // DATA
-            const resultExpected = "strict digraph G {\n graph []\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.01\", width=\"0.01\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\n}"
+            const resultExpected = "strict digraph G {\n graph []\nsubgraph cluster_mainChain {\nnode2;node3;cycleGroup;}\nnode1  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode2  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\nnode3  [height=\"0.35\", width=\"0.35\", fixedsize=\"true\"];\ncycleGroup  [fixedsize=\"true\", height=\"0.35\", width=\"0.35\"];\nnode1 -> node2;\nnode2 -> node3;\nnode3 -> cycleGroup;\n}"
 
             // TEST
-            const result = ConvertFromNetwork.networkToDOT(subgraphNetwork);
+            const result = await ConvertFromNetwork.networkToDOT(subgraphNetwork);
 
             // EXPECT
             expect(result).toEqual(resultExpected);
diff --git a/src/composables/__tests__/ConvertToNetwork.test.ts b/src/composables/__tests__/ConvertToNetwork.test.ts
index ea3bbd222f375895031890802c7eca42f92348c8..46e9aa4241bb651eab3f989e4c0c6c96b3385e90 100644
--- a/src/composables/__tests__/ConvertToNetwork.test.ts
+++ b/src/composables/__tests__/ConvertToNetwork.test.ts
@@ -1,5 +1,5 @@
 // Type imports
-import { Network } from '@metabohub/viz-core/src/types/Network';
+import { Network } from "../../types/TypeVizCore";
 import {  NetworkLayout } from '../../types/NetworkLayout';
 import { JsonViz } from '../../types/FormatJsonViz';
 import { SubgraphNetwork } from '../../types/SubgraphNetwork';
@@ -57,7 +57,7 @@ describe('ConvertToNetwork', () => {
 
     describe('changeNetworkFromViz', () => {
 
-        it('should throw an error if node from JSON is not in the network', async () => {
+        it('should throw an error if node from JSON is not in the network (case 1)', async () => {
             // DATA
             const jsonViz: JsonViz = {
                 objects: [{ name: 'nonexistentNode', pos: '10,20' }],
@@ -78,6 +78,69 @@ describe('ConvertToNetwork', () => {
             await expect(ConvertToNetwork.changeNetworkFromViz(jsonViz,subgraphNetwork)).rejects.toThrow();
         });
 
+        it('should throw an error if node from JSON is not in the network (case 2)', async () => {
+            // DATA
+            const jsonViz: JsonViz = {
+                objects: [{ name: 'nonexistentNode', pos: '10,20', nodes:[1] }],
+                edges: []
+            };
+    
+            const subgraphNetwork: SubgraphNetwork = {
+                network: {
+                    id: 'network',
+                    nodes: {},
+                    links: []
+                },
+                networkStyle :{},
+                [TypeSubgraph.CYCLEGROUP]: {}
+            };
+    
+            // TEST & EXPECT
+            await expect(ConvertToNetwork.changeNetworkFromViz(jsonViz,subgraphNetwork)).rejects.toThrow();
+        });
+
+        it('should not throw error when id not found in network because it is a subgraph', async () => {
+            // MOCK 
+            const assignRankOrderMock = jest.spyOn(CalculateStartNodes, 'assignRankOrder');
+            assignRankOrderMock.mockImplementation( ()=>{
+                return ;
+            });
+
+
+            // DATA
+            const jsonViz: JsonViz = {
+                objects: [
+                    { name: 'nodeNotNetwork', pos: '10,20', nodes:[5,6] },
+                    { name: 'node2', pos: '30,40' , nodes:[2]},
+                    { name: 'groupCycle', pos: '10,30' }
+                ],
+                edges: []
+            };
+
+            const subgraph:Subgraph={
+                name: "groupCycle",
+                nodes: ["node2"],
+                type: TypeSubgraph.CYCLEGROUP
+            };
+    
+            const subgraphNetwork: SubgraphNetwork = {
+                network: {
+                    id: 'network',
+                    nodes: {
+                        node1: { id: 'node1', x: 0, y: 0 },
+                        node2: { id: 'node2', x: 0, y: 0 }
+                    },
+                    links: []
+                },
+                networkStyle :{},
+                [TypeSubgraph.CYCLEGROUP]: {[subgraph.name]:subgraph}
+            };
+    
+            // TEST & EXPECT
+            await expect(ConvertToNetwork.changeNetworkFromViz(jsonViz,subgraphNetwork,false)).resolves.not.toThrow();
+
+        });
+
 
         it('should update network nodes with positions from JSON, no assign rank', async () => {
             // MOCK 
diff --git a/src/composables/__tests__/GetSetAttributsNodes.test.ts b/src/composables/__tests__/GetSetAttributsNodes.test.ts
index 677733670187250afc598bfe9c5f255edc81266b..a0ec06ab11ccfa5071e0d042d713c5525771264b 100644
--- a/src/composables/__tests__/GetSetAttributsNodes.test.ts
+++ b/src/composables/__tests__/GetSetAttributsNodes.test.ts
@@ -1,7 +1,5 @@
 // Type imports
-import { Node } from "@metabohub/viz-core/src/types/Node";
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Link } from "@metabohub/viz-core/src/types/Link";
+import { Node, Network, Link } from "../../types/TypeVizCore";
 import { NetworkLayout } from "../../types/NetworkLayout";
 import { TypeSubgraph } from "../../types/Subgraph";
 
@@ -293,81 +291,6 @@ describe('Duplicate', () => {
 // 2.  Reversible *****************************************************************
 
 describe('Reversible', () => {
-     test("addMetadataReversibleWithClass",  async () => {
-
-        const addReversibleNetworkMock = jest.spyOn(GetSetAttributsNodes, 'addReversibleNetwork');
-        // DATA 
-
-        let network:Network = {
-            id:"networkTest",
-            nodes: {
-                node1 :{
-                    id: "node1",
-                    x: 10,
-                    y: 10,
-                },
-                node2: {
-                    id: "node2",
-                    x: 20,
-                    y: 20,
-                    classes: [GetSetAttributsNodes.classReversible+"_test"]
-                },
-                node3: {
-                    id: "node3",
-                    x: 30,
-                    y: 30,
-                    classes: [GetSetAttributsNodes.classReversible]
-                },
-                node4: {
-                    id: "node4",
-                    x: 40,
-                    y: 40,
-                    classes: [GetSetAttributsNodes.classReversible+"_test",GetSetAttributsNodes.classReversible]
-                }
-            },
-            links: []
-        };
-
-        let networkExpected:Network = {
-            id:"networkTest",
-            nodes: {
-                node1 :{
-                    id: "node1",
-                    x: 10,
-                    y: 10,
-                },
-                node2: {
-                    id: "node2",
-                    x: 20,
-                    y: 20,
-                    classes: [GetSetAttributsNodes.classReversible+"_test"]
-                },
-                node3: {
-                    id: "node3",
-                    x: 30,
-                    y: 30,
-                    classes: [GetSetAttributsNodes.classReversible],
-                    metadata: {[GetSetAttributsNodes.reversibleAttribute]:true}
-                },
-                node4: {
-                    id: "node4",
-                    x: 40,
-                    y: 40,
-                    classes: [GetSetAttributsNodes.classReversible+"_test",GetSetAttributsNodes.classReversible],
-                    metadata: {[GetSetAttributsNodes.reversibleAttribute]:true}
-                }
-            },
-            links: []
-        };
-
-        // TEST
-        await GetSetAttributsNodes.addMetadataReversibleWithClass(network);
-
-        // EXPECT
-        //expect(addReversibleNetworkMock).toHaveBeenCalled();
-        expect(network).toEqual(networkExpected);
-
-     });
 
      test("addReversibleNetwork",  () => {
         // DATA
diff --git a/src/composables/__tests__/LayoutDrawCycle.test.ts b/src/composables/__tests__/LayoutDrawCycle.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b53705e2aaf6d003cc6fb30085855d1917163e74
--- /dev/null
+++ b/src/composables/__tests__/LayoutDrawCycle.test.ts
@@ -0,0 +1,1226 @@
+// Type imports
+import { SubgraphNetwork } from '../../types/SubgraphNetwork';
+import { NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
+import { Subgraph, TypeSubgraph } from '../../types/Subgraph';
+import { CoordinateNull } from '../../types/CoordinatesSize';
+
+
+// Composable imports
+import * as LayoutDrawCycle from '../LayoutDrawCycle';
+import * as CalculateRelationCycle from '../CalculateRelationCycle';
+import * as SubgraphForSubgraphNetwork from '../SubgraphForSubgraphNetwork';
+import * as CalculateOverlaps from '../CalculateOverlaps';
+import * as CalculateSize from '../CalculateSize';
+
+// General imports
+import * as d3 from 'd3';
+
+
+jest.mock('d3', () => {
+    return {
+        forceSimulation:jest.fn(),
+        forceLink: jest.fn(() => ({
+            id: jest.fn().mockReturnThis(),
+            links: jest.fn().mockReturnThis(),
+            distance: jest.fn().mockReturnThis(),
+            strength: jest.fn().mockReturnThis(),
+        })),
+        forceManyBody: jest.fn(() => ({
+            strength: jest.fn().mockReturnThis(),
+        })),
+    };
+});
+
+const forcesimulationMock=jest.fn((nodes) => {
+    nodes[3].x=34;
+    nodes[3].y=34;
+    return {
+    force: jest.fn().mockReturnThis(),  
+    alphaMin: jest.fn().mockReturnThis(),
+    stop: jest.fn().mockReturnThis(),
+    tick: jest.fn().mockReturnThis(),
+    alpha: jest.fn(() => {return 0.3}), 
+};});
+
+(d3.forceSimulation as jest.Mock).mockImplementation(forcesimulationMock);
+
+
+function getNodeWithMinYInFirstCycleGroup(subgraphNetwork: SubgraphNetwork): string | null {
+
+    let minYNodeId: string | null = null;
+    let minY: number = Infinity;
+
+    let cycleGroup: Subgraph;
+    if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP]) return null;
+    else {
+        cycleGroup=subgraphNetwork[TypeSubgraph.CYCLEGROUP]["cycle_group_0"];
+        const position= cycleGroup.precalculatedNodesPosition;
+        for (const nodeId in position) {
+            const pos = position[nodeId];
+            if (pos.y && pos.y < minY) {
+                minY = pos.y;
+                minYNodeId = nodeId;
+            }
+        }
+    }
+
+    return minYNodeId;
+}
+
+describe('LayoutDrawCycle',()=>{
+
+    let parentNodeNotInCycleMock:jest.SpyInstance;
+    let childNodeNotInCycleMock:jest.SpyInstance;
+    let getListNodeLinksForCycleGroupAsArrayMock:jest.SpyInstance;
+    let getListNodeLinksForCycleGroupAsObjectMock:jest.SpyInstance;
+    let getNodesPlacedInGroupCycleAsObjectMock:jest.SpyInstance;
+    let isIntersectionGraphMock:jest.SpyInstance;
+    let isOverlapNodesMock:jest.SpyInstance;
+    let isOverlapNodesEdgesMock:jest.SpyInstance;
+    let updateNodeMetadataSubgraphMock:jest.SpyInstance;
+    let getMeanNodesSizePixelMock:jest.SpyInstance;
+    let medianEdgeLengthMock:jest.SpyInstance;
+    let rectangleSizeMock:jest.SpyInstance;
+
+    getMeanNodesSizePixelMock = jest.spyOn(CalculateSize, 'getMeanNodesSizePixel');
+    getMeanNodesSizePixelMock.mockReturnValue(Promise.resolve({width:2,height:2}));
+
+    medianEdgeLengthMock= jest.spyOn(CalculateSize, 'medianEdgeLength');
+    medianEdgeLengthMock.mockReturnValue(5);
+
+    
+    beforeEach(() => {
+    
+        parentNodeNotInCycleMock = jest.spyOn(CalculateRelationCycle, 'parentNodeNotInCycle');
+        parentNodeNotInCycleMock.mockReturnValue([]);
+
+        childNodeNotInCycleMock = jest.spyOn(CalculateRelationCycle, 'childNodeNotInCycle');
+        childNodeNotInCycleMock.mockReturnValue([]);
+
+        updateNodeMetadataSubgraphMock = jest.spyOn(SubgraphForSubgraphNetwork, 'updateNodeMetadataSubgraph');
+        updateNodeMetadataSubgraphMock.mockImplementation();
+
+        isIntersectionGraphMock = jest.spyOn(CalculateOverlaps, 'isIntersectionGraph');
+        isIntersectionGraphMock.mockReturnValue(false);
+
+        isOverlapNodesMock = jest.spyOn(CalculateOverlaps, 'isOverlapNodes');
+        isOverlapNodesMock.mockReturnValue(false);
+
+        isOverlapNodesEdgesMock = jest.spyOn(CalculateOverlaps, 'isOverlapNodesEdges');
+        isOverlapNodesEdgesMock.mockReturnValue(false);
+
+        getListNodeLinksForCycleGroupAsArrayMock = jest.spyOn(CalculateRelationCycle, 'getListNodeLinksForCycleGroupAsArray');
+        getListNodeLinksForCycleGroupAsArrayMock.mockReturnValue({});
+
+        getListNodeLinksForCycleGroupAsObjectMock = jest.spyOn(CalculateRelationCycle, 'getListNodeLinksForCycleGroupAsObject');
+        getListNodeLinksForCycleGroupAsObjectMock.mockReturnValue({});
+
+        getNodesPlacedInGroupCycleAsObjectMock = jest.spyOn(CalculateRelationCycle, 'getNodesPlacedInGroupCycleAsObject');
+        getNodesPlacedInGroupCycleAsObjectMock.mockReturnValue({});
+
+        rectangleSizeMock = jest.spyOn(CalculateSize, 'rectangleSize');
+        rectangleSizeMock.mockReturnValue({width:0,height:0,center:{x:0,y:0}});
+
+    });
+
+    afterEach(() => {
+        parentNodeNotInCycleMock.mockRestore();
+        childNodeNotInCycleMock.mockRestore();
+        updateNodeMetadataSubgraphMock.mockRestore();
+        isIntersectionGraphMock.mockRestore();
+        isOverlapNodesMock.mockRestore();
+        isOverlapNodesEdgesMock.mockRestore();
+        getListNodeLinksForCycleGroupAsArrayMock.mockRestore();
+        getListNodeLinksForCycleGroupAsObjectMock.mockRestore();
+        getNodesPlacedInGroupCycleAsObjectMock.mockRestore();
+        rectangleSizeMock.mockRestore();
+        forcesimulationMock.mockClear();
+    });
+
+    describe('coordinateAllCycles',()=>{
+        
+        it('should throw error because all cycle have parent subgraph of type cycle',async ()=>{        
+        
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0"],
+                parentSubgraph:{name:"cycleParent",type:TypeSubgraph.CYCLE}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:{node0:{id:"node0",x:0,y:0}},
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+            // TEST & EXPECT
+           await expect(LayoutDrawCycle.coordinateAllCycles(subgraphNetwork)).rejects.toThrow();
+
+        });
+
+        it('should throw error because cycle have no  node',async ()=>{  
+        
+        
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:[],
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:{},
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+            // TEST & EXPECT
+            await expect(LayoutDrawCycle.coordinateAllCycles(subgraphNetwork)).rejects.toThrow();
+
+        });
+
+        it('should throw error because cycle have non existant node',async()=>{        
+        
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0"],
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:{},
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+            // TEST & EXPECT
+            await expect(LayoutDrawCycle.coordinateAllCycles(subgraphNetwork)).rejects.toThrow();
+
+        });
+
+        it('should calculate coordinates for a cycle without parents and children, and get size of metanode', async()=>{        
+        
+            // MOCK 
+            getNodesPlacedInGroupCycleAsObjectMock = jest.spyOn(CalculateRelationCycle, 'getNodesPlacedInGroupCycleAsObject');
+            getNodesPlacedInGroupCycleAsObjectMock.mockImplementation( () => {
+                if (!subgraphNetwork[TypeSubgraph.CYCLEGROUP]) return;
+                const position=subgraphNetwork[TypeSubgraph.CYCLEGROUP]["cycle_group_0"].precalculatedNodesPosition;
+                if (position && position.node0.x === 0 && position.node0.y === -6 &&
+                    position.node1.x === 5.2 && position.node1.y === 3 &&
+                    position.node2.x === -5.2 && position.node2.y === 3) {
+                    return {node0:{x:0,y:-6},node1:{x:5.2,y:3},node2:{x:-5.2,y:3}};
+                }
+              }
+            );
+            
+            rectangleSizeMock = jest.spyOn(CalculateSize, 'rectangleSize');
+            rectangleSizeMock.mockReturnValue({width:10.4,height:9,center:{x:0,y:-1.5}});
+
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={"network":{"id":"network",
+                "nodes":{"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}}},
+                "links":[]},
+                "networkStyle":{},
+                "cycles":{"cycle":{"name":"cycle","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node2"],"radiusCycle":6,"centroidCycle":{"x":0,"y":0}}},
+                "cyclesGroup":{"cycle_group_0":{"name":"cycle_group_0","nodes":["cycle"],"type":TypeSubgraph.CYCLEGROUP,"precalculatedNodesPosition":{"node0":{"x":0,"y":-6},"node1":{"x":5.2,"y":3},"node2":{"x":-5.2,"y":3}},"width":10.4,"height":9,"originalPosition":{"x":0,"y":-1.5}}}}
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(updateNodeMetadataSubgraphMock).toHaveBeenCalledTimes(3);
+            expect(rectangleSizeMock).toHaveBeenCalledTimes(1);
+            expect(result).toEqual(subgraphNetworkExpected);
+
+        });
+
+        it('should find top node for a cycle with parents at different levels', async()=>{        
+        
+            // MOCK 
+            parentNodeNotInCycleMock.mockReturnValue([
+                [],["node3"],["node4"]
+            ])
+            
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:-1},
+                node4:{id:"node4",x:0,y:-2}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[
+                    {id:"link0",source:nodes.node1,target:nodes.node3},
+                    {id:"link1",source:nodes.node2,target:nodes.node4}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+            const topNode=getNodeWithMinYInFirstCycleGroup(result);
+
+            // EXPECT
+            expect(topNode).toEqual("node2");
+            
+        });
+
+        it('should find top node for a cycle with parents at same levels', async()=>{        
+        
+            // MOCK 
+            parentNodeNotInCycleMock.mockReturnValue([
+                [],["node3"],["node4"]
+            ])
+            
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:-1},
+                node4:{id:"node4",x:0,y:-1}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[
+                    {id:"link0",source:nodes.node1,target:nodes.node3},
+                    {id:"link1",source:nodes.node2,target:nodes.node4}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+            const topNode=getNodeWithMinYInFirstCycleGroup(result);
+
+            // EXPECT
+            expect(topNode).toEqual("node1");
+            
+        });
+
+        it('should find top node for a cycle with child at different levels', async()=>{        
+        
+            // MOCK 
+            childNodeNotInCycleMock.mockReturnValue([
+                [],["node3"],["node4"]
+            ])
+            
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:1},
+                node4:{id:"node4",x:0,y:2}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[
+                    {id:"link0",source:nodes.node1,target:nodes.node3},
+                    {id:"link1",source:nodes.node2,target:nodes.node4}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph}
+            };
+
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+            const topNode=getNodeWithMinYInFirstCycleGroup(result);
+
+            // EXPECT
+            expect(topNode).toEqual("node0"); // the opposite of the bottom node (node2)
+            
+        });
+
+        it('should calculate coordinates for 2 cycles related : as line (but no force)', async()=>{
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2","node3"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2","node4"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0},
+                node4:{id:"node4",x:10,y:10}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={"network":{"id":"network",
+                "nodes":{"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node3":{"id":"node3","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node4":{"id":"node4","x":10,"y":10,"metadataLayout":{"isFixed":true}}},"links":[]},
+                "networkStyle":{},
+                "cycles":{"cycle":{"name":"cycle","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node2","node3"],"radiusCycle":8,"centroidCycle":{"x":0,"y":0}},
+                "cycle2":{"name":"cycle2","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node2","node4"]}},
+            "cyclesGroup":{"cycle_group_0":{"name":"cycle_group_0","nodes":["cycle","cycle2"],"type":TypeSubgraph.CYCLEGROUP,"precalculatedNodesPosition":{"node0":{"x":0,"y":-8},"node1":{"x":8,"y":0},"node2":{"x":0,"y":8},"node3":{"x":-8,"y":0},"node4":{"x":0,"y":0}},"width":0,"height":0,"originalPosition":{"x":0,"y":0}}}}
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(result).toEqual(subgraphNetworkExpected);
+            expect(forcesimulationMock).not.toHaveBeenCalled();
+
+
+        });
+
+        it('should calculate coordinates for 2 cycles related : as line and interval unfixed at the end and beginning of cycle (but no force)', async()=>{
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0", "node1", "node2", "node3", "node4"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node6","node0","node1","node5"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0},
+                node4:{id:"node4",x:0,y:0},
+                node5:{id:"node5",x:0,y:0},
+                node6:{id:"node6",x:0,y:0}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "node0": { "id": "node0", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node1": { "id": "node1", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node2": { "id": "node2", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node3": { "id": "node3", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node4": { "id": "node4", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node5": { "id": "node5", "x": 0, "y": 0, "metadataLayout": { "isFixed": true } },
+                    "node6": { "id": "node6", "x": 0, "y": 0, "metadataLayout": { "isFixed": true } }
+                  },
+                  "links": []
+                },
+                "networkStyle": {},
+                "cycles": {
+                  "cycle": {
+                    "name": "cycle",
+                    "type": TypeSubgraph.CYCLE,
+                    "nodes": ["node0", "node1", "node2", "node3", "node4"],
+                    "radiusCycle": 10,
+                    "centroidCycle": { "x": 0, "y": 0 }
+                  },
+                  "cycle2": {
+                    "name": "cycle2",
+                    "type": TypeSubgraph.CYCLE,
+                    "nodes": ["node6", "node0", "node1", "node5"]
+                  }
+                },
+                "cyclesGroup": {
+                  "cycle_group_0": {
+                    "name": "cycle_group_0",
+                    "nodes": ["cycle", "cycle2"],
+                    "type": TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition": {
+                      "node0": { "x": 0, "y": -10 },
+                      "node1": { "x": 9.51, "y": -3.09 },
+                      "node2": { "x": 5.88, "y": 8.09 },
+                      "node3": { "x": -5.88, "y": 8.09 },
+                      "node4": { "x": -9.51, "y": -3.09 },
+                      "node5": { "x": 6.34, "y": -5.39 },
+                      "node6": { "x": 3.17, "y": -7.7 }
+                    },
+                    "width": 0,
+                    "height": 0,
+                    "originalPosition": { "x": 0, "y": 0 }
+                  }
+                }
+              }
+              
+              
+            
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(result).toEqual(subgraphNetworkExpected);
+            expect(forcesimulationMock).not.toHaveBeenCalled();
+
+        });
+
+    
+
+        it('should calculate coordinates for 2 cycles related : as tangent (but no force)', async()=>{
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2",],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node3","node4"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0},
+                node4:{id:"node4",x:0,y:0}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={"network":{"id":"network",
+                "nodes":{"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node3":{"id":"node3","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle2"}},
+                "node4":{"id":"node4","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle2"}}},
+                "links":[]},"networkStyle":{},
+                "cycles":{"cycle":{"name":"cycle","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node2"],"radiusCycle":6,"centroidCycle":{"x":0,"y":0}},
+                "cycle2":{"name":"cycle2","type":TypeSubgraph.CYCLE,"nodes":["node0","node3","node4"],"radiusCycle":6,"centroidCycle":{"x":0,"y":-12}}},
+            "cyclesGroup":{"cycle_group_0":{"name":"cycle_group_0","nodes":["cycle","cycle2"],"type":TypeSubgraph.CYCLEGROUP,"precalculatedNodesPosition":{"node0":{"x":0,"y":-6},"node1":{"x":5.2,"y":3},"node2":{"x":-5.2,"y":3},"node3":{"x":-5.2,"y":-15},"node4":{"x":5.2,"y":-15}},"width":0,"height":0,"originalPosition":{"x":0,"y":0}}}}
+
+            
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(result).toEqual(subgraphNetworkExpected);
+            expect(forcesimulationMock).not.toHaveBeenCalled();
+
+
+        });
+
+        it('should calculate coordinates when force layout neccessary (case line) : intersection of edges', async()=>{
+
+            // MOCK
+            isIntersectionGraphMock.mockReturnValue(true);
+
+            getListNodeLinksForCycleGroupAsArrayMock.mockReturnValue({
+                nodes: [
+                  { id: 'node0', fx: 0, fy: -6 },
+                  { id: 'node1', fx: 5.2, fy: 3 },
+                  { id: 'node2', fx: -5.2, fy: 3 },
+                  { id: 'node3' }
+                ],
+                links: []
+            });
+                                  
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node3"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0}, // will be placed at (34,34) by force
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={"network":{"id":"network",
+                "nodes":{"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node3":{"id":"node3","x":0,"y":0,"metadataLayout":{"isFixed":true}}},
+                "links":[]},
+                "networkStyle":{},
+                "cycles":{"cycle":{"name":"cycle","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node2"],"radiusCycle":6,"centroidCycle":{"x":0,"y":0}},
+                "cycle2":{"name":"cycle2","type":TypeSubgraph.CYCLE,"nodes":["node0","node1","node3"]}},
+                "cyclesGroup":{"cycle_group_0":{"name":"cycle_group_0","nodes":["cycle","cycle2"],"type":TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition":{"node0":{"x":0,"y":-6},"node1":{"x":5.2,"y":3},"node2":{"x":-5.2,"y":3},"node3":{"x":34,"y":34}},
+                "width":0,"height":0,"originalPosition":{"x":0,"y":0}}}}
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(getListNodeLinksForCycleGroupAsArrayMock).toHaveBeenCalledTimes(1);
+            expect(result).toEqual(subgraphNetworkExpected);
+            expect(forcesimulationMock).toHaveBeenCalledTimes(1);
+
+
+        });
+
+        it('should test if force layout used (case line) : node overlap ', async()=>{
+
+            // MOCK
+            isOverlapNodesMock.mockReturnValue(true);
+
+            getListNodeLinksForCycleGroupAsArrayMock.mockReturnValue({
+                nodes: [
+                  { id: 'node0', fx: 0, fy: -6 },
+                  { id: 'node1', fx: 5.2, fy: 3 },
+                  { id: 'node2', fx: -5.2, fy: 3 },
+                  { id: 'node3' }
+                ],
+                links: []
+            });
+                                  
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node3"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0}, // will be placed at (34,34) by force
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+            let posNode3:CoordinateNull={x:null,y:null};
+
+            if(result.cyclesGroup && result[TypeSubgraph.CYCLEGROUP]["cycle_group_0"] 
+                && result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition
+            && result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition.node3){
+                posNode3=result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition.node3;
+            }
+
+            // EXPECT
+            expect(posNode3).toBeDefined();
+            expect(posNode3.x).toEqual(34);
+            expect(posNode3.y).toEqual(34);
+            expect(forcesimulationMock).toHaveBeenCalledTimes(1);
+
+
+        });
+
+        it('should test if force layout used (case line) : node-edge overlap ', async()=>{
+
+            // MOCK
+            isOverlapNodesEdgesMock.mockReturnValue(true);
+
+            getListNodeLinksForCycleGroupAsArrayMock.mockReturnValue({
+                nodes: [
+                  { id: 'node0', fx: 0, fy: -6 },
+                  { id: 'node1', fx: 5.2, fy: 3 },
+                  { id: 'node2', fx: -5.2, fy: 3 },
+                  { id: 'node3' }
+                ],
+                links: []
+            });
+                                  
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node3"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0}, // will be placed at (34,34) by force
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+            let posNode3:CoordinateNull={x:null,y:null};
+
+            if(result.cyclesGroup && result[TypeSubgraph.CYCLEGROUP]["cycle_group_0"] 
+                && result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition
+            && result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition.node3){
+                posNode3=result.cyclesGroup["cycle_group_0"].precalculatedNodesPosition.node3;
+            }
+
+            // EXPECT
+            expect(posNode3).toBeDefined();
+            expect(posNode3.x).toEqual(34);
+            expect(posNode3.y).toEqual(34);
+            expect(forcesimulationMock).toHaveBeenCalledTimes(1);
+
+        });
+
+
+        it('should calculate coordinates for more complex case : 1 cycle group with 1 line and 1 tangent (but no force)', async()=>{
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node6","node7","node8","node9"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node1","node2","node3","node4","node5"],
+            };
+            const subgraph3:Subgraph={
+                name:"cycle3",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node6","node7","node8","node2"],
+            };
+
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0},
+                node4:{id:"node4",x:0,y:0},
+                node5:{id:"node5",x:0,y:0},
+                node6:{id:"node6",x:0,y:0},
+                node7:{id:"node7",x:0,y:0},
+                node8:{id:"node8",x:0,y:0},
+                node9:{id:"node9",x:0,y:0}
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2, "cycle3":subgraph3}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "node0": { "id": "node0", "x": 0, "y": 0 },
+                    "node1": { "id": "node1", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node2": { "id": "node2", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node3": { "id": "node3", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node4": { "id": "node4", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node5": { "id": "node5", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node6": { "id": "node6", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle3" } },
+                    "node7": { "id": "node7", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle3" } },
+                    "node8": { "id": "node8", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle3" } },
+                    "node9": { "id": "node9", "x": 0, "y": 0, "metadataLayout": { "isFixed": true } }
+                  },
+                  "links": []
+                },
+                "networkStyle": {},
+                "cycles": {
+                  "cycle": { "name": "cycle", "type": TypeSubgraph.CYCLE, "nodes": ["node6", "node7", "node8", "node9"] },
+                  "cycle2": { "name": "cycle2", "type": TypeSubgraph.CYCLE, "nodes": ["node1", "node2", "node3", "node4", "node5"], "radiusCycle": 10, "centroidCycle": { "x": 0, "y": 0 } },
+                  "cycle3": { "name": "cycle3", "type": TypeSubgraph.CYCLE, "nodes": ["node6", "node7", "node8", "node2"], "radiusCycle": 8, "centroidCycle": { "x": 17.12, "y": -5.56 } }
+                },
+                "cyclesGroup": {
+                  "cycle_group_0": {
+                    "name": "cycle_group_0",
+                    "nodes": ["cycle2", "cycle3", "cycle"],
+                    "type": TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition": {
+                      "node1": { "x": 0, "y": -10 },
+                      "node2": { "x": 9.51, "y": -3.09 },
+                      "node3": { "x": 5.88, "y": 8.09 },
+                      "node4": { "x": -5.88, "y": 8.09 },
+                      "node5": { "x": -9.51, "y": -3.09 },
+                      "node6": { "x": 14.65, "y": -13.17 },
+                      "node7": { "x": 24.73, "y": -8.03 },
+                      "node8": { "x": 19.59, "y": 2.05 },
+                      "node9": { "x": 17.12, "y": -5.56 }
+                    },
+                    "width": 0,
+                    "height": 0,
+                    "originalPosition": { "x": 0, "y": 0 }
+                  }
+                }
+              }
+              
+            
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            // EXPECT
+            expect(result).toEqual(subgraphNetworkExpected);
+            expect(forcesimulationMock).not.toHaveBeenCalled();
+
+
+        });
+
+
+        it('should calculate coordinates for 2 cycles independant', async()=>{
+
+            // DATA
+            const subgraph:Subgraph={
+                name:"cycle",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node0","node1","node2"],
+            };
+            const subgraph2:Subgraph={
+                name:"cycle2",
+                type:TypeSubgraph.CYCLE,
+                nodes:["node3","node4","node5"],
+            };
+            const nodes:{[key:string]:NodeLayout}={
+                node0:{id:"node0",x:0,y:0},
+                node1:{id:"node1",x:0,y:0},
+                node2:{id:"node2",x:0,y:0},
+                node3:{id:"node3",x:0,y:0},
+                node4:{id:"node4",x:0,y:0},
+                node5:{id:"node5",x:0,y:0},
+            };
+
+            const network:NetworkLayout={
+                id:"network",
+                nodes:nodes,
+                links:[]
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                network:network,
+                networkStyle:{},
+                [TypeSubgraph.CYCLE]:{"cycle":subgraph, "cycle2":subgraph2}
+            };
+
+            const subgraphNetworkExpected:SubgraphNetwork={
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "node0": { "id": "node0", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node1": { "id": "node1", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node2": { "id": "node2", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node3": { "id": "node3", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node4": { "id": "node4", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } },
+                    "node5": { "id": "node5", "x": 0, "y": 0, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } }
+                  },
+                  "links": []
+                },
+                "networkStyle": {},
+                "cycles": {
+                  "cycle": { "name": "cycle", "type": TypeSubgraph.CYCLE, "nodes": ["node0", "node1", "node2"], "radiusCycle": 6, "centroidCycle": { "x": 0, "y": 0 } },
+                  "cycle2": { "name": "cycle2", "type": TypeSubgraph.CYCLE, "nodes": ["node3", "node4", "node5"], "radiusCycle": 6, "centroidCycle": { "x": 0, "y": 0 } }
+                },
+                "cyclesGroup": {
+                  "cycle_group_0": {
+                    "name": "cycle_group_0",
+                    "nodes": ["cycle"],
+                    "type": TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition": {
+                      "node0": { "x": 0, "y": -6 },
+                      "node1": { "x": 5.2, "y": 3 },
+                      "node2": { "x": -5.2, "y": 3 }
+                    },
+                    "width": 0,"height": 0,"originalPosition": { "x": 0, "y": 0 }
+                  },
+                  "cycle_group_1": {
+                    "name": "cycle_group_1",
+                    "nodes": ["cycle2"],
+                    "type": TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition": {
+                      "node3": { "x": 0, "y": -6 },
+                      "node4": { "x": 5.2, "y": 3 },
+                      "node5": { "x": -5.2, "y": 3 }
+                    },
+                    "width": 0,"height": 0,"originalPosition": { "x": 0, "y": 0 }
+                  }
+                }
+              };        
+
+            // TEST
+            const result=await LayoutDrawCycle.coordinateAllCycles(subgraphNetwork);
+
+            
+            // EXPECT
+            expect(getNodesPlacedInGroupCycleAsObjectMock).toHaveBeenCalledTimes(2);
+            expect(result).toEqual(subgraphNetworkExpected);
+
+        });
+
+        
+    });
+
+
+    describe('drawAllCyclesGroup',()=>{
+
+        it('should draw all cycle group',()=>{
+
+            // MOCK
+            getNodesPlacedInGroupCycleAsObjectMock
+                .mockReturnValueOnce(
+                    {
+                    node0: { x: 0, y: -6 },
+                    node1: { x: 5.2, y: 3 },
+                    node2: { x: -5.2, y: 3 }
+                })
+                .mockReturnValueOnce({ node3: { x: 6, y: -6 } })
+
+
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node3":{"id":"node3","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle2"}}};
+
+            const subgraphNetwork:SubgraphNetwork={
+                "network":{
+                    "id":"network",
+                    nodes:nodes,
+                    "links":[]},
+                "networkStyle":{},
+                "cyclesGroup":{cycle_group_0:
+                    {"name":"cycle_group_0","nodes":["cycle"],
+                    "type":TypeSubgraph.CYCLEGROUP,
+                    "position":{x:10,y:10},
+                    "precalculatedNodesPosition":{
+                        "node0":{"x":0,"y":-6},
+                        "node1":{"x":5.2,"y":3},
+                        "node2":{"x":-5.2,"y":3}},
+                        "width":10.4,"height":9,
+                        "originalPosition":{"x":0,"y":-1.5}},
+                    cycle_group_1:
+                    {"name":"cycle_group_1","nodes":["cycle2"],
+                    "type":TypeSubgraph.CYCLEGROUP,
+                    "position":{x:-10,y:-10},
+                    "precalculatedNodesPosition":{
+                        "node3":{"x":6,"y":-6}},
+                        "width":10.4,"height":9,
+                        "originalPosition":{"x":0,"y":-1.5}}}};
+
+            
+
+            const networkExpected : NetworkLayout ={
+                "id": "network",
+                "nodes": {
+                    "node0": { "id": "node0", "x": 10, "y": 5.5, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node1": { "id": "node1", "x": 15.2, "y": 14.5, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node2": { "id": "node2", "x": 4.8, "y": 14.5, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle" } },
+                    "node3": { "id": "node3", "x": -4, "y": -14.5, "metadataLayout": { "isFixed": true, "fixedInCircle": "cycle2" } }
+                },
+                "links": []
+                };
+                
+            // TEST
+            LayoutDrawCycle.drawAllCyclesGroup(subgraphNetwork);
+
+            // EXPECT
+            expect(subgraphNetwork.network).toEqual(networkExpected);
+
+        });
+
+        it('should throw error because no original position of cyclegroup',()=>{
+
+            // MOCK
+            getNodesPlacedInGroupCycleAsObjectMock
+                .mockReturnValueOnce(
+                    {
+                    node0: { x: 0, y: -6 },
+                    node1: { x: 5.2, y: 3 },
+                    node2: { x: -5.2, y: 3 }
+                })
+                .mockReturnValueOnce({ node3: { x: 6, y: -6 } })
+
+
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                "network":{
+                    "id":"network",
+                    nodes:nodes,
+                    "links":[]},
+                "networkStyle":{},
+                "cyclesGroup":{cycle_group_0:
+                    {"name":"cycle_group_0","nodes":["cycle"],
+                    "type":TypeSubgraph.CYCLEGROUP,
+                    "position":{x:10,y:10},
+                    "precalculatedNodesPosition":{
+                        "node0":{"x":0,"y":-6},
+                        "node1":{"x":5.2,"y":3},
+                        "node2":{"x":-5.2,"y":3}},
+                        "width":10.4,"height":9,
+                }}
+            };
+
+            // TEST & EXPECT
+            expect(() => {
+                LayoutDrawCycle.drawAllCyclesGroup(subgraphNetwork);
+            }).toThrow();
+
+        });
+
+        it('should throw error because no position of metanode',()=>{
+
+            // MOCK
+            getNodesPlacedInGroupCycleAsObjectMock
+                .mockReturnValueOnce(
+                    {
+                    node0: { x: 0, y: -6 },
+                    node1: { x: 5.2, y: 3 },
+                    node2: { x: -5.2, y: 3 }
+                })
+                .mockReturnValueOnce({ node3: { x: 6, y: -6 } })
+
+
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node2":{"id":"node2","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                "network":{
+                    "id":"network",
+                    nodes:nodes,
+                    "links":[]},
+                "networkStyle":{},
+                "cyclesGroup":{cycle_group_0:
+                    {"name":"cycle_group_0","nodes":["cycle"],
+                    "type":TypeSubgraph.CYCLEGROUP,
+                    "precalculatedNodesPosition":{
+                        "node0":{"x":0,"y":-6},
+                        "node1":{"x":5.2,"y":3},
+                        "node2":{"x":-5.2,"y":3}},
+                        "width":10.4,"height":9,
+                        "originalPosition":{"x":0,"y":-1.5}
+                }}
+            };
+
+            // TEST & EXPECT
+            expect(() => {
+                LayoutDrawCycle.drawAllCyclesGroup(subgraphNetwork);
+            }).toThrow();
+
+        });
+
+        it('should throw error because node in metadata but not in network',()=>{
+
+            // MOCK
+            getNodesPlacedInGroupCycleAsObjectMock
+                .mockReturnValueOnce(
+                    {
+                    node0: { x: 0, y: -6 },
+                    node1: { x: 5.2, y: 3 },
+                    node2: { x: -5.2, y: 3 }
+                })
+                .mockReturnValueOnce({ node3: { x: 6, y: -6 } })
+
+
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={"node0":{"id":"node0","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+                "node1":{"id":"node1","x":0,"y":0,"metadataLayout":{"isFixed":true,"fixedInCircle":"cycle"}},
+            };
+
+            const subgraphNetwork:SubgraphNetwork={
+                "network":{
+                    "id":"network",
+                    nodes:nodes,
+                    "links":[]},
+                "networkStyle":{},
+                "cyclesGroup":{cycle_group_0:
+                    {"name":"cycle_group_0","nodes":["cycle"],
+                    "type":TypeSubgraph.CYCLEGROUP,
+                    "position":{x:10,y:10},
+                    "precalculatedNodesPosition":{
+                        "node0":{"x":0,"y":-6},
+                        "node1":{"x":5.2,"y":3},
+                        "node2":{"x":-5.2,"y":3}},
+                        "width":10.4,"height":9,
+                        "originalPosition":{"x":0,"y":-1.5}
+                }}
+            };
+
+            // TEST & EXPECT
+            expect(() => {
+                LayoutDrawCycle.drawAllCyclesGroup(subgraphNetwork);
+            }).toThrow();
+
+        });
+
+
+
+    });
+   
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/LayoutFindCycle.test.ts b/src/composables/__tests__/LayoutFindCycle.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5197fd3500199f46fb94082939c47816672e2071
--- /dev/null
+++ b/src/composables/__tests__/LayoutFindCycle.test.ts
@@ -0,0 +1,360 @@
+// Type imports
+import { NodeLayout } from "../../types/NetworkLayout";
+import { Subgraph, TypeSubgraph } from "../../types/Subgraph";
+import { SubgraphNetwork } from "../../types/SubgraphNetwork";
+import { Network,Node } from  "../../types/TypeVizCore";
+
+// Composable imports
+import * as LayoutFindCycle from '../LayoutFindCycle';
+import * as LayoutReversibleReactions from '../LayoutReversibleReactions';
+import * as SubgraphForSubgraphNetwork from '../SubgraphForSubgraphNetwork'; 
+
+
+describe('LayoutFindCycle', () => {
+
+    let addSubgraphToNetworkMock: jest.SpyInstance;
+    let createSubgraphMock: jest.SpyInstance;
+    let updateNodeMetadataSubgraphMock: jest.SpyInstance;
+    let updateParentSubgraphOfMock: jest.SpyInstance;
+    let keepFirstReversibleNodeMock: jest.SpyInstance;
+    let renameAllIDNodeMock: jest.SpyInstance;
+
+    const minsize=3;
+    let nodes:{[key:string]:NodeLayout} ;
+
+    renameAllIDNodeMock = jest.spyOn(LayoutReversibleReactions, 'renameAllIDNode');
+    renameAllIDNodeMock.mockImplementation(()=>{});
+
+    updateNodeMetadataSubgraphMock = jest.spyOn(SubgraphForSubgraphNetwork, 'updateNodeMetadataSubgraph');
+    updateNodeMetadataSubgraphMock.mockImplementation(()=>{});
+
+    createSubgraphMock = jest.spyOn(SubgraphForSubgraphNetwork, 'createSubgraph');
+    createSubgraphMock.mockImplementation((name:string,nodes:string[])=>{
+        return {
+            name: name,
+            nodes: nodes,
+            type: TypeSubgraph.CYCLE,
+        }
+});
+
+    beforeEach(() => {
+
+        nodes={
+            A:{id:'A',x:0,y:0,metadataLayout:{reversibleNodeVersion:"A_rev"}},
+            B:{id:'B',x:0,y:0},
+            C:{id:'C',x:0,y:0},
+            D:{id:'D',x:0,y:0},
+            E:{id:'E',x:0,y:0},
+            F:{id:'F',x:0,y:0},
+            A_rev:{id:'A_rev',x:0,y:0,metadataLayout:{reversibleNodeVersion:"A"}},
+        }
+
+        keepFirstReversibleNodeMock = jest.spyOn(LayoutReversibleReactions, 'keepFirstReversibleNode');
+        keepFirstReversibleNodeMock.mockImplementation(()=>{});
+
+        addSubgraphToNetworkMock = jest.spyOn(SubgraphForSubgraphNetwork, 'addSubgraphToNetwork');
+        addSubgraphToNetworkMock.mockImplementation((subgraphNetwork:SubgraphNetwork,subgraph:Subgraph,type:TypeSubgraph)=>{
+            if (!subgraphNetwork[type]) subgraphNetwork[type]={};
+            if(!(subgraph.name in subgraphNetwork[type])){
+                subgraphNetwork[type][subgraph.name]=subgraph;
+            }
+            return subgraphNetwork;
+        });
+
+        updateParentSubgraphOfMock = jest.spyOn(SubgraphForSubgraphNetwork, 'updateParentSubgraphOf');
+        updateParentSubgraphOfMock.mockImplementation(()=>{});
+
+    });
+
+    afterEach(() => {
+        addSubgraphToNetworkMock.mockRestore();
+        keepFirstReversibleNodeMock.mockRestore();
+        updateParentSubgraphOfMock.mockRestore();
+    });
+
+    describe('addDirectedCycleToSubgraphNetwork', () => {
+
+
+        it('should find only one directed cycles when there is a permutation', () => {            
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A},
+                    {id:"link", source:nodes.B,target:nodes.A},
+                    {id:"link", source:nodes.C,target:nodes.B},
+                    {id:"link", source:nodes.A,target:nodes.C}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+            const cyclesExpected : {[key:string]:Subgraph}= {"cycle_0":{"name":"cycle_0","nodes":["A","B","C"],"type":TypeSubgraph.CYCLE}};
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+            
+            // EXPECT
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(1);
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected)
+                
+        });
+
+        it('should find directed cycles that are independants', () => {
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A},
+                    {id:"link", source:nodes.D,target:nodes.E},
+                    {id:"link", source:nodes.E,target:nodes.F},
+                    {id:"link", source:nodes.F,target:nodes.D}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+           const cyclesExpected : {[key:string]:Subgraph}= {
+            cycle_0: { name: 'cycle_0', nodes: [ 'A', 'B', 'C' ], type: TypeSubgraph.CYCLE },
+            cycle_1: { name: 'cycle_1', nodes: [ 'D', 'E', 'F' ], type: TypeSubgraph.CYCLE}
+          }
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(2);
+            expect(updateParentSubgraphOfMock).toHaveBeenCalledTimes(0);
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected);
+                
+        });
+
+        it('should find directed cycles that have one node in common', () => {
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A},
+                    {id:"link", source:nodes.D,target:nodes.E},
+                    {id:"link", source:nodes.E,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.D}
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+           const cyclesExpected : {[key:string]:Subgraph}= {
+            cycle_0: { name: 'cycle_0', nodes: [ 'A', 'B', 'C' ], type: TypeSubgraph.CYCLE },
+            cycle_1: { name: 'cycle_1', nodes: [ 'B', 'D', 'E' ], type: TypeSubgraph.CYCLE }
+          }
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(2);
+            expect(updateParentSubgraphOfMock).toHaveBeenCalledTimes(0);
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected);
+
+        });
+
+        it('should find directed cycles that have several nodes in common', () => {
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A},
+                    {id:"link", source:nodes.D,target:nodes.E},
+                    {id:"link", source:nodes.E,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.D},
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+           const cyclesExpected : {[key:string]:Subgraph}= {
+            cycle_1: { name: 'cycle_1', nodes: [ 'B', 'C', 'D', 'E' ], type: TypeSubgraph.CYCLE },
+            cycle_0: {
+              name: 'cycle_0',
+              nodes: [ 'A', 'B', 'C' ],
+              type: TypeSubgraph.CYCLE,
+              parentSubgraph: { name: 'cycle_1', type: TypeSubgraph.CYCLE }
+            }
+          }
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(2);
+            expect(updateParentSubgraphOfMock).toHaveBeenCalledTimes(1);
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected);
+
+        });
+
+        it('shouln d find cycles when reversible reactions are duplicated', () => {
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A_rev},
+                    {id:"link", source:nodes.A_rev,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.A},
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(keepFirstReversibleNodeMock).toHaveBeenCalledTimes(0);
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual({});
+
+        });
+
+        it('should find cycles when reversible reactions are duplicated, and rename node', () => {
+
+            // MOCK
+            keepFirstReversibleNodeMock.mockImplementation((subgraphNetwork:SubgraphNetwork,nodeOrder:string[])=>{
+                if (nodeOrder.includes("A_rev") && !nodeOrder.includes("A")){
+                    return {"A_rev":"A"};
+                }
+                return {};
+
+            })
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.C},
+                    {id:"link", source:nodes.C,target:nodes.A_rev},
+                    {id:"link", source:nodes.A_rev,target:nodes.B},
+                    {id:"link", source:nodes.B,target:nodes.A},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+            const cyclesExpected={
+                cycle_0: { name: 'cycle_0', nodes: [ 'A_rev', 'B', 'C' ], type: 'cycles' }
+              };
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(keepFirstReversibleNodeMock).toHaveBeenCalledTimes(1);
+            expect(renameAllIDNodeMock).toHaveBeenCalledWith(expect.anything(),{"A_rev":"A"})
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected);
+
+        });
+
+        it('should remove a non existant cycle when reversible reactions are duplicated', () => {
+
+            // MOCK
+            keepFirstReversibleNodeMock.mockImplementation((subgraphNetwork:SubgraphNetwork,nodeOrder:string[])=>{
+                if (nodeOrder.includes("A_rev") && !nodeOrder.includes("A")){
+                    delete subgraphNetwork.network.nodes.A;
+                }
+                if (nodeOrder.includes("A") && !nodeOrder.includes("A_rev")){
+                    delete subgraphNetwork.network.nodes.A_rev;
+                }
+                return subgraphNetwork;
+            })
+
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: [
+                    {id:"link", source:nodes.A,target:nodes.D},
+                    {id:"link", source:nodes.C,target:nodes.D},
+                    {id:"link", source:nodes.B,target:nodes.C},
+                    {id:"link", source:nodes.B,target:nodes.A},
+                    {id:"link", source:nodes.A_rev,target:nodes.B},
+                    {id:"link", source:nodes.D,target:nodes.A_rev},
+                    {id:"link", source:nodes.E,target:nodes.A_rev},
+                    {id:"link", source:nodes.A,target:nodes.E},
+                    {id:"link", source:nodes.A_rev,target:nodes.F},
+                    {id:"link", source:nodes.F,target:nodes.A},
+                    {id:"link", source:nodes.E,target:nodes.F},
+                ]
+            };
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network:network,
+                networkStyle:{},
+            }
+
+            const cyclesExpected={
+                cycle_1: {
+                  name: 'cycle_1',
+                  nodes: [ 'A_rev', 'B', 'C', 'D' ],
+                  type: 'cycles'
+                }
+            };
+
+            // TEST
+            const result=LayoutFindCycle.addDirectedCycleToSubgraphNetwork(subgraphNetwork,minsize);
+
+            // EXPECT
+            expect(keepFirstReversibleNodeMock).toHaveBeenCalledTimes(1);
+            expect(renameAllIDNodeMock).toHaveBeenCalledWith(expect.anything(),{"A_rev":"A"})
+            expect(result[TypeSubgraph.CYCLE]).toBeDefined();
+            expect(result[TypeSubgraph.CYCLE]).toEqual(cyclesExpected);
+            expect(result.network.nodes.A).not.toBeDefined();
+
+        });
+
+
+    });
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/LayoutMain.test.ts b/src/composables/__tests__/LayoutMain.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a8296f9910c9016ea858e9b92fd0ffb4f7b2d93d
--- /dev/null
+++ b/src/composables/__tests__/LayoutMain.test.ts
@@ -0,0 +1,522 @@
+// Type imports
+import { defaultParameters,Parameters } from "../../types/Parameters";
+import { GraphStyleProperties, Link, Network } from "../../types/TypeVizCore";
+import { NetworkLayout } from "../../types/NetworkLayout";
+import { SubgraphNetwork } from "../../types/SubgraphNetwork";
+import { TypeSubgraph } from "../../types/Subgraph";
+import {Node} from "../../types/TypeVizCore";
+
+// Composable imports
+import * as LayoutMain from "../LayoutMain";
+import * as LayoutManageSideCompounds from "../LayoutManageSideCompounds";
+import * as LayoutReversibleReactions from "../LayoutReversibleReactions";
+import * as LayoutSugiyamaForce from "../LayoutSugiyama";
+import * as LayoutFindCycle from "../LayoutFindCycle";
+import * as LayoutMainChain from "../LayoutMainChain";
+import * as LayoutDrawCycle from "../LayoutDrawCycle";
+import * as CalculateSize from "../CalculateSize";
+import * as CalculateStartNodes from "../CalculateStartNodes";
+import * as ConvertFromNetwork from "../ConvertFromNetwork";
+import * as ConvertToNetwork from "../ConvertToNetwork";
+import * as CheckNetwork from "../CheckNetwork";
+import exp from "constants";
+
+
+jest.mock('d3',( ) => {});
+
+
+describe('LayoutMain', () => {
+
+
+    afterEach(() => {
+        jest.clearAllMocks();
+    });
+
+
+    describe('allSteps', () => {
+
+        let subgraphNetwork:SubgraphNetwork;
+        let parameters:Parameters;
+
+        let putDuplicatedSideCompoundAsideMock:jest.SpyInstance;
+        let vizLayoutMock:jest.SpyInstance;
+        let duplicateReversibleReactionsMock:jest.SpyInstance;
+        let addDirectedCycleToSubgraphNetworkMock:jest.SpyInstance;
+        let getStartNodesMock:jest.SpyInstance;
+        let chooseReversibleReactionMock:jest.SpyInstance;
+        let addMainChainFromSourcesMock:jest.SpyInstance;
+        let addMiniBranchToMainChainMock:jest.SpyInstance;
+        let coordinateAllCyclesMock:jest.SpyInstance;
+        let drawAllCyclesGroupMock:jest.SpyInstance;
+        let reinsertionSideCompoundsMock:jest.SpyInstance;
+        let shiftAllToGetTopLeftCoordMock:jest.SpyInstance;
+
+
+        beforeEach(() => {
+
+            // DATA
+
+            const networkStyle:GraphStyleProperties = {};
+            const networkLayout:NetworkLayout = {
+                id: 'network',
+                nodes: {},
+                links : []
+            };
+
+            subgraphNetwork={
+                network:networkLayout,
+                networkStyle:networkStyle
+                }
+
+            parameters = {
+                ...defaultParameters,
+                doDuplicateSideCompounds: true, 
+                doPutAsideSideCompounds: true, 
+                doReactionReversible: true, 
+                doMainChain: true,
+                doMiniBranch: true, 
+                doCycle: true,         
+                shiftCoord: true,
+            };
+
+            // MOCKS
+            putDuplicatedSideCompoundAsideMock = jest.spyOn(LayoutManageSideCompounds, 'putDuplicatedSideCompoundAside');
+            putDuplicatedSideCompoundAsideMock.mockReturnValue(Promise.resolve(subgraphNetwork));
+
+            vizLayoutMock = jest.spyOn(LayoutSugiyamaForce, 'vizLayout');
+            vizLayoutMock.mockReturnValue(Promise.resolve(subgraphNetwork));
+
+            duplicateReversibleReactionsMock = jest.spyOn(LayoutReversibleReactions, 'duplicateReversibleReactions');
+            duplicateReversibleReactionsMock.mockReturnValue(Promise.resolve());
+
+            addDirectedCycleToSubgraphNetworkMock = jest.spyOn(LayoutFindCycle, 'addDirectedCycleToSubgraphNetwork');
+            addDirectedCycleToSubgraphNetworkMock.mockImplementation((subgraphNetwork:SubgraphNetwork) => {
+                subgraphNetwork[TypeSubgraph.CYCLE] = {
+                    "cycle":{name:"cycle", nodes:[],type:TypeSubgraph.CYCLE}
+                };
+                return subgraphNetwork;
+            });
+
+            getStartNodesMock = jest.spyOn(CalculateStartNodes, 'getStartNodes');
+            getStartNodesMock.mockReturnValue(Promise.resolve([] as string[]));
+
+            chooseReversibleReactionMock = jest.spyOn(LayoutReversibleReactions, 'chooseReversibleReaction');
+            chooseReversibleReactionMock.mockReturnValue(Promise.resolve(subgraphNetwork));
+
+            addMainChainFromSourcesMock = jest.spyOn(LayoutMainChain, 'addMainChainFromSources');
+            addMainChainFromSourcesMock.mockReturnValue(subgraphNetwork);
+
+            addMiniBranchToMainChainMock = jest.spyOn(LayoutMainChain, 'addMiniBranchToMainChain');
+            addMiniBranchToMainChainMock.mockReturnValue(subgraphNetwork);
+
+            coordinateAllCyclesMock = jest.spyOn(LayoutDrawCycle, 'coordinateAllCycles');
+            coordinateAllCyclesMock.mockReturnValue(Promise.resolve(subgraphNetwork));
+
+            drawAllCyclesGroupMock = jest.spyOn(LayoutDrawCycle, 'drawAllCyclesGroup');
+            drawAllCyclesGroupMock.mockImplementation(() => {});
+
+            reinsertionSideCompoundsMock = jest.spyOn(LayoutManageSideCompounds, 'reinsertionSideCompounds');
+            reinsertionSideCompoundsMock.mockReturnValue(Promise.resolve(subgraphNetwork));
+
+            shiftAllToGetTopLeftCoordMock = jest.spyOn(CalculateSize, 'shiftAllToGetTopLeftCoord');
+            shiftAllToGetTopLeftCoordMock.mockImplementation(() => {});
+
+        });
+
+        afterAll(() => {
+            jest.restoreAllMocks();
+        });
+
+        
+       it('should call all steps', async () => {
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+
+       it("shouldn't do main chain", async () => {
+            // DATA
+            parameters.doMainChain = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(1);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).not.toHaveBeenCalled();
+            expect(addMiniBranchToMainChainMock).not.toHaveBeenCalled();
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+
+       it("shouldn't do reaction reversible", async () => {
+            // DATA
+            parameters.doReactionReversible = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+            
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).not.toHaveBeenCalled();
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(1);
+            expect(chooseReversibleReactionMock).not.toHaveBeenCalled();
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledWith(expect.anything(),expect.anything(),false);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+
+       it("shouldn't do reaction reversible and main chain", async () => {
+            // DATA
+            parameters.doReactionReversible = false;
+            parameters.doMainChain = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+            
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(2);
+            expect(duplicateReversibleReactionsMock).not.toHaveBeenCalled();
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).not.toHaveBeenCalled();
+            expect(chooseReversibleReactionMock).not.toHaveBeenCalled();
+            expect(addMainChainFromSourcesMock).not.toHaveBeenCalled();
+            expect(addMiniBranchToMainChainMock).not.toHaveBeenCalled();
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledWith(expect.anything(),expect.anything(),false);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+
+       it("shouldn't do cycle", async () => {
+            // DATA
+            parameters.doCycle = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+            
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(2);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).not.toHaveBeenCalled();
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).not.toHaveBeenCalled();
+            expect(drawAllCyclesGroupMock).not.toHaveBeenCalled();
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+        
+        it("should do cycle but no cycle found", async () => {
+            // MOCK
+            addDirectedCycleToSubgraphNetworkMock.mockImplementation((subgraphNetwork:SubgraphNetwork) => {
+                return subgraphNetwork;
+            });
+    
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+        
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(2);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).not.toHaveBeenCalled();
+            expect(drawAllCyclesGroupMock).not.toHaveBeenCalled();
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+    
+        });
+
+        it("shouldn't do mini branch", async () => {
+            // DATA
+            parameters.doMiniBranch = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).not.toHaveBeenCalled();
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+        });
+
+        it("shouldn't shift coordinates", async () => {
+            // DATA
+            parameters.shiftCoord = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).toHaveBeenCalledTimes(1);
+            expect(shiftAllToGetTopLeftCoordMock).not.toHaveBeenCalled();
+
+        });
+
+        it("shouldn't duplicate side compound", async () => {
+            // DATA
+            parameters.doDuplicateSideCompounds = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledWith(expect.anything(),false,expect.anything());
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).not.toHaveBeenCalled();
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+       });
+
+       it("shouldn't put aside side compound", async () => {
+            // DATA
+            parameters.doPutAsideSideCompounds = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledWith(expect.anything(),expect.anything(),false);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).not.toHaveBeenCalled();
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+        });
+
+        it("shouldn't put aside side and duplicate compound", async () => {
+            // DATA
+            parameters.doDuplicateSideCompounds = false;
+            parameters.doPutAsideSideCompounds = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledWith(expect.anything(),false,false);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(3);
+            expect(duplicateReversibleReactionsMock).toHaveBeenCalledTimes(1);
+            expect(addDirectedCycleToSubgraphNetworkMock).toHaveBeenCalledTimes(1);
+            expect(getStartNodesMock).toHaveBeenCalledTimes(2);
+            expect(chooseReversibleReactionMock).toHaveBeenCalledTimes(1);
+            expect(addMainChainFromSourcesMock).toHaveBeenCalledTimes(1);
+            expect(addMiniBranchToMainChainMock).toHaveBeenCalledTimes(1);
+            expect(coordinateAllCyclesMock).toHaveBeenCalledTimes(1);
+            expect(drawAllCyclesGroupMock).toHaveBeenCalledTimes(1);
+            expect(reinsertionSideCompoundsMock).not.toHaveBeenCalled();
+            expect(shiftAllToGetTopLeftCoordMock).toHaveBeenCalledTimes(1);
+
+        });
+
+
+        it("shouldn't do any special step", async () => {
+            // DATA
+            parameters.doDuplicateSideCompounds = false;
+            parameters.doPutAsideSideCompounds = false;
+            parameters.doReactionReversible = false;
+            parameters.doMainChain = false;
+            parameters.doMiniBranch = false;
+            parameters.doCycle = false;
+            parameters.shiftCoord = false;
+
+            // TEST
+            await LayoutMain.allSteps(subgraphNetwork, parameters);
+
+            // EXPECT
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledTimes(1);
+            expect(putDuplicatedSideCompoundAsideMock).toHaveBeenCalledWith(expect.anything(),false,false);
+            expect(vizLayoutMock).toHaveBeenCalledTimes(1);
+            expect(duplicateReversibleReactionsMock).not.toHaveBeenCalled();
+            expect(addDirectedCycleToSubgraphNetworkMock).not.toHaveBeenCalled();
+            expect(getStartNodesMock).not.toHaveBeenCalled();
+            expect(chooseReversibleReactionMock).not.toHaveBeenCalled();
+            expect(addMainChainFromSourcesMock).not.toHaveBeenCalled();
+            expect(addMiniBranchToMainChainMock).not.toHaveBeenCalled();
+            expect(coordinateAllCyclesMock).not.toHaveBeenCalled();
+            expect(drawAllCyclesGroupMock).not.toHaveBeenCalled();
+            expect(reinsertionSideCompoundsMock).not.toHaveBeenCalled();
+            expect(shiftAllToGetTopLeftCoordMock).not.toHaveBeenCalled();
+
+       });
+
+    });
+
+
+    describe('algorithmOnNetwork', () => {
+
+        let networktoNetworkLayoutMock:jest.SpyInstance;
+        let networkLayoutToNetworkMock:jest.SpyInstance;
+        let checkNetworkFormat:jest.SpyInstance;
+
+        let nodes:{ [key: string]: Node };
+
+        let links:Link[];
+
+        let networkLayout:NetworkLayout;
+
+        beforeEach(() => {
+
+            nodes={
+                'A':{ id: 'A', x: 0, y: 0 },
+                'B':{ id: 'B', x: 0, y: 0 },
+            };
+
+            links=[
+                {id:"link",source:nodes.A,target:nodes.B}
+            ]
+
+            networkLayout = {
+                id: 'network',
+                nodes: nodes,
+                links : links
+            };
+
+            // MOCK
+
+            networktoNetworkLayoutMock = jest.spyOn(ConvertFromNetwork, 'networktoNetworkLayout');
+
+            networkLayoutToNetworkMock = jest.spyOn(ConvertToNetwork, 'networkLayoutToNetwork');
+
+            checkNetworkFormat = jest.spyOn(CheckNetwork, 'checkNetworkFormat');
+
+        });
+
+        afterEach(() => {
+            networktoNetworkLayoutMock.mockRestore();
+            networkLayoutToNetworkMock.mockRestore();
+        });
+
+        it ('should throw error if incorrect network', async () => {
+            // MOCK
+            checkNetworkFormat.mockImplementationOnce(() => {
+                throw new Error("Incorrect network format");
+            });
+
+            await expect(LayoutMain.layoutOnNetwork(networkLayout, {})).rejects.toThrow("Incorrect network format");
+
+        });
+
+        it ('should throw error if network is empty', async () => {
+
+            const emptyNetwork: Network = { id:"network", nodes: {}, links: [] };
+
+            await expect(LayoutMain.layoutOnNetwork(emptyNetwork, {})).rejects.toThrow('The network is not defined, has no nodes or no links : the algorithm will not be executed');
+        });
+
+        it ('should throw error if network has no links', async () => {
+            const noNodesNetwork: Network = { id:"network", nodes:nodes, links: [] };
+
+            await expect(LayoutMain.layoutOnNetwork(noNodesNetwork, {})).rejects.toThrow('The network is not defined, has no nodes or no links : the algorithm will not be executed');
+
+        });
+
+
+        it ('should apply both fonction of conversion', async () => {
+            // MOCK
+            networktoNetworkLayoutMock.mockImplementationOnce((network:Network)=>{return network });
+            networkLayoutToNetworkMock.mockImplementationOnce((network:NetworkLayout)=>{return network});
+
+            const result= await LayoutMain.layoutOnNetwork(networkLayout, {});
+
+            expect(networktoNetworkLayoutMock).toHaveBeenCalledTimes(1);
+            expect(networkLayoutToNetworkMock).toHaveBeenCalledTimes(1);
+  
+        });
+
+        it('should call all steps and change at least one position', async () => {
+
+            const result= await LayoutMain.layoutOnNetwork(networkLayout, {});
+
+            let changed = false;
+            for (const node of Object.values(result.nodes)) {
+                if (node.x !== 0 || node.y !== 0) {
+                    changed = true;
+                    break;
+                }
+            }
+            expect(changed).toBe(true);
+        });
+
+
+    });
+
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/LayoutMainChain.test.ts b/src/composables/__tests__/LayoutMainChain.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51d5e3fc887856f95314f0d32e0cf55dbe0c7d35
--- /dev/null
+++ b/src/composables/__tests__/LayoutMainChain.test.ts
@@ -0,0 +1,764 @@
+// Type imports
+import { PathType, StartNodesType } from "../../types/EnumArgs";
+import { SubgraphNetwork } from "../../types/SubgraphNetwork";
+import {TypeSubgraph, type Subgraph} from "../../types/Subgraph";
+import { Network } from  "../../types/TypeVizCore";
+
+// Composable imports
+import * as LayoutMainChain from "../LayoutMainChain";
+import * as AlgorithmDFS from "../AlgorithmDFS";
+import * as ConvertFromNetwork from "../ConvertFromNetwork";
+import * as CalculateStartNodes from "../CalculateStartNodes";
+import * as AlgorithmBFS from "../AlgorithmBFS";
+import * as SubgraphForSubgraphNetwork from '../SubgraphForSubgraphNetwork';
+import { before } from "node:test";
+
+
+describe('LayoutMainChain', () => {
+
+    const createSubgraphMock = jest.spyOn(SubgraphForSubgraphNetwork, 'createSubgraph');
+    const networkToGDSGraphMock = jest.spyOn(ConvertFromNetwork, 'networkToGDSGraph');
+
+    const addSubgraphToNetworkMock=jest.spyOn(SubgraphForSubgraphNetwork, 'addSubgraphToNetwork');
+    addSubgraphToNetworkMock.mockImplementation((subgraphNetwork:SubgraphNetwork,subgraph:Subgraph,type:TypeSubgraph) => {
+        if (!subgraphNetwork[type]) subgraphNetwork[type]={};
+        if(!(subgraph.name in subgraphNetwork[type])){
+            subgraphNetwork[type][subgraph.name]=subgraph;
+        }
+        return subgraphNetwork;
+    });
+
+    let network: Network;
+    let subgraphNetwork: SubgraphNetwork;
+
+    beforeEach(() => {
+        network = {
+            id:"network",
+            nodes:{},
+            links:[]
+        };
+
+        subgraphNetwork={
+            network: network,
+            networkStyle: {},
+        };
+    });
+
+    afterEach(() => {
+        jest.clearAllMocks();
+    });
+
+    describe('addMainChainFromSources', () => {
+
+        const getStartNodesMock= jest.spyOn(CalculateStartNodes, 'getStartNodes');
+        getStartNodesMock.mockReturnValue(Promise.resolve(["A", "B"]));
+ 
+        beforeEach(() => {
+
+            createSubgraphMock.mockImplementation((name:string,nodes:string[])=>{
+                return {
+                    name: name,
+                    nodes: nodes,
+                    type: TypeSubgraph.MAIN_CHAIN,
+                }
+    
+            });
+
+        });
+
+        
+        it('shouldn t use getStartNodes because already list of sources', async () => {
+            // DATA
+            const sources=["A", "B"];
+            const getMainChains= async function(){
+                return {};
+            }
+            
+            // TEST
+            await LayoutMainChain.addMainChainFromSources(subgraphNetwork, sources,getMainChains);
+
+            // EXPECT
+            expect(getStartNodesMock).not.toHaveBeenCalled();
+        });
+
+        it('should use getStartNodes because already list of sources', async () => {
+            // DATA
+            const sources=StartNodesType.RANK_ONLY;
+            const getMainChains= async function(){
+                return {};
+            }
+            
+             // TEST
+             await LayoutMainChain.addMainChainFromSources(subgraphNetwork, sources,getMainChains);
+ 
+             // EXPECT
+             expect(getStartNodesMock).toHaveBeenCalledTimes(1);
+        });
+
+        it('should add 2 mainChain', async () => {
+            // DATA
+            const getMainChains= async function(network: Network, sources: Array<string>):
+            Promise<{[key:string]:{nodes:Array<string>, height:number}}>{
+                return {mainChain1:{nodes:["A", "B"],height:8}, mainChain2:{nodes:["C", "D"],height:8}};
+            }
+            const mainChainExpected : {[key:string]:Subgraph}={
+                mainChain__mainChain1: {
+                  name: 'mainChain__mainChain1',
+                  nodes: [ 'A', 'B' ],
+                  type: TypeSubgraph.MAIN_CHAIN
+                },
+                mainChain__mainChain2: {
+                  name: 'mainChain__mainChain2',
+                  nodes: [ 'C', 'D' ],
+                  type: TypeSubgraph.MAIN_CHAIN
+                }
+              };
+
+            // TEST
+            await LayoutMainChain.addMainChainFromSources(subgraphNetwork, StartNodesType.RANK_ONLY,getMainChains);
+
+            // EXPECT
+            expect(createSubgraphMock).toHaveBeenCalledTimes(2);
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(2);
+            expect(subgraphNetwork[TypeSubgraph.MAIN_CHAIN]).toBeDefined();
+            expect(subgraphNetwork[TypeSubgraph.MAIN_CHAIN]).toEqual(mainChainExpected);
+
+        });
+
+        it('should only add 1 mainChain bacause one is to small', async () => {
+            // DATA
+            const minHeight=8;
+            const getMainChains= async function(network: Network, sources: Array<string>):
+            Promise<{[key:string]:{nodes:Array<string>, height:number}}>{
+                return {mainChain1:{nodes:["A", "B"],height:3}, mainChain2:{nodes:["C", "D"],height:8}};
+            }
+            const mainChainExpected : {[key:string]:Subgraph}={
+                mainChain__mainChain2: {
+                  name: 'mainChain__mainChain2',
+                  nodes: [ 'C', 'D' ],
+                  type: TypeSubgraph.MAIN_CHAIN
+                }
+              };
+
+            // TEST
+            await LayoutMainChain.addMainChainFromSources(subgraphNetwork, StartNodesType.RANK_ONLY,getMainChains,true,PathType.ALL,minHeight);
+
+            // EXPECT
+            expect(createSubgraphMock).toHaveBeenCalledTimes(1);
+            expect(addSubgraphToNetworkMock).toHaveBeenCalledTimes(1);
+            expect(subgraphNetwork[TypeSubgraph.MAIN_CHAIN]).toBeDefined();
+            expect(subgraphNetwork[TypeSubgraph.MAIN_CHAIN]).toEqual(mainChainExpected);
+
+        });
+
+
+    });
+
+    describe('addMiniBranchToMainChain', () => {
+
+        beforeEach(() => {
+
+            createSubgraphMock.mockImplementation((name:string,nodes:string[],classes: Array<string>, type: TypeSubgraph, parentSubgraph?: {name:string,type:TypeSubgraph})=>{
+                return {
+                    name: name,
+                    nodes: nodes,
+                    type: TypeSubgraph.SECONDARY_CHAIN,
+                    parentSubgraph: parentSubgraph
+                }
+    
+            });
+
+            networkToGDSGraphMock.mockImplementation(async (network)=>{
+                return {
+                    outdegree: jest.fn((id: string) => {
+                        if (id === 'F'|| id === 'E') return 0;
+                        return 1;
+                    }),
+                    adjacent:jest.fn((id: string) => {
+                        switch (id) {
+                            case 'A':
+                                return ['B', 'D'];
+                            case 'B':
+                                return ['C'];
+                            case 'C':
+                                return ['F'];
+                            case 'D':
+                                return ['E'];
+                            default:
+                                return [];
+                        }
+    
+                    })
+                }
+            });
+
+        });
+
+        it('shouldn t add miniBranch because no mainChain', async () => {
+
+            // TEST
+            await LayoutMainChain.addMiniBranchToMainChain(subgraphNetwork);
+
+            // EXPECT
+            expect(networkToGDSGraphMock).not.toHaveBeenCalled();
+
+        });
+
+        it('shouldn t add miniBranch because no product of main chain has outdegree of 0', async () => {
+            // DATA
+            subgraphNetwork[TypeSubgraph.MAIN_CHAIN]={
+                mainChain1: {
+                    name: 'mainChain1',
+                    nodes: [ 'A' ,'B'],
+                    type: TypeSubgraph.MAIN_CHAIN
+                  }
+            };
+
+            // TEST
+            await LayoutMainChain.addMiniBranchToMainChain(subgraphNetwork);
+
+            // EXPECT
+            expect(networkToGDSGraphMock).toHaveBeenCalledTimes(1);
+            if(subgraphNetwork[TypeSubgraph.SECONDARY_CHAIN]){
+                expect(subgraphNetwork[TypeSubgraph.SECONDARY_CHAIN]).toEqual({});
+            }
+
+        });
+
+
+        it('should add miniBranch', async () => {
+            // DATA
+            subgraphNetwork[TypeSubgraph.MAIN_CHAIN]={
+                mainChain1: {
+                    name: 'mainChain1',
+                    nodes: [ 'A' ,'C'],
+                    type: TypeSubgraph.MAIN_CHAIN
+                  },
+                  mainChain2: {
+                    name: 'mainChain2',
+                    nodes: [ 'D' ],
+                    type: TypeSubgraph.MAIN_CHAIN
+                  }
+            };
+
+            const secondaryChainExpected : {[key:string]:Subgraph}={
+                minibranch_mainChain1: {
+                  name: 'minibranch_mainChain1',
+                  nodes: [ 'F' ],
+                  type: TypeSubgraph.SECONDARY_CHAIN,
+                  parentSubgraph: { name: 'mainChain1', type: TypeSubgraph.MAIN_CHAIN }
+                },
+                minibranch_mainChain2: {
+                  name: 'minibranch_mainChain2',
+                  nodes: [ 'E' ],
+                  type: TypeSubgraph.SECONDARY_CHAIN,
+                  parentSubgraph: { name: 'mainChain2', type: TypeSubgraph.MAIN_CHAIN }
+                }
+              }
+
+            // TEST
+            await LayoutMainChain.addMiniBranchToMainChain(subgraphNetwork);
+
+            // EXPECT
+            expect(networkToGDSGraphMock).toHaveBeenCalledTimes(1);
+            expect(createSubgraphMock).toHaveBeenCalledTimes(2);
+            expect(subgraphNetwork[TypeSubgraph.SECONDARY_CHAIN]).toBeDefined();
+            expect(subgraphNetwork[TypeSubgraph.SECONDARY_CHAIN]).toEqual(secondaryChainExpected);
+            
+        });
+
+
+    });
+
+    describe('getPathSourcesToTargetNode', () => {
+
+        let DFSsourceDAGMock = jest.spyOn(AlgorithmDFS, 'DFSsourceDAG');
+        let BFSMock = jest.spyOn(AlgorithmBFS, 'BFS');
+
+        let sources: Array<string>;
+        let graphGDS:{[key:string]:Function};
+
+        
+        describe('case one source with one target', () => {
+
+            beforeEach(() => {
+                // DATA
+                sources=["A"];
+
+                // MOCK
+                graphGDS={
+                    adjacent:jest.fn((id: string) => {
+                        switch (id) {
+                            case 'A':
+                                return ['B', 'F','E'];
+                            case 'B':
+                                return ['C'];
+                            case 'C':
+                                return ['D'];
+                            case 'E':
+                                return ['D'];
+                            case 'F':
+                                return ['G'];
+                            case 'G':
+                                return ['D'];
+                            default:
+                                return [];
+                        }
+
+                    }),
+                    getEdgeWeight:jest.fn(() => (1)),
+                    nodes: jest.fn(() => (['A', 'B', 'C', 'D', 'E', 'F', 'G']))
+                }
+
+                networkToGDSGraphMock.mockImplementation(async ()=>{
+                    return graphGDS;
+                });
+
+                DFSsourceDAGMock.mockReturnValue(Promise.resolve(
+                    {dfs:[
+                            'A', 'F', 'G',
+                            'E', 'B', 'C',
+                            'D'
+                        ], 
+                        graph:graphGDS}
+                ));
+            });
+
+
+            test ('all longest path', async () => {
+    
+                BFSMock.mockReturnValue([ 'D', 'G', 'C', 'F', 'B', 'A' ]);
+    
+                // DATA
+                const pathExpected = { A: { nodes: [ 'D', 'G', 'C', 'F', 'B', 'A' ], height: 4 } };
+    
+    
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL_LONGEST);
+    
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledWith({
+                    A: [],
+                    F: [ 'A' ],
+                    G: [ 'F' ],
+                    E: [ 'A' ],
+                    B: [ 'A' ],
+                    C: [ 'B' ],
+                    D: [ 'G', 'C' ]
+                  }, 'D');
+                expect(result).toEqual(pathExpected);
+
+    
+            });
+        
+
+
+            test ('longest path', async () => {
+
+                BFSMock.mockReturnValue([ 'D', 'G', 'F','A' ]);
+
+                // DATA
+                const pathExpected = { A: { nodes: [ 'D', 'G', 'F','A' ], height: 4 } };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.LONGEST);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledWith({
+                    A: [],
+                    F: [ 'A' ],
+                    G: [ 'F' ],
+                    E: [ 'A' ],
+                    B: [ 'A' ],
+                    C: [ 'B' ],
+                    D: [ 'G' ]
+                }, 'D');
+                expect(result).toEqual(pathExpected);
+
+
+            });
+
+            test ('all path', async () => {
+
+                BFSMock.mockReturnValue([ 'D','G','E', 'C', 'F','A','B' ]);
+
+                // DATA
+                const pathExpected = {
+                    A: { nodes: [ 'D','G','E', 'C', 'F','A','B' ], height: 4 }
+                };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledWith({
+                    A: [],
+                    F: [ 'A' ],
+                    G: [ 'F' ],
+                    E: [ 'A' ],
+                    B: [ 'A' ],
+                    C: [ 'B' ],
+                    D: [ 'G', 'E', 'C' ]
+                }, 'D');
+                expect(result).toEqual(pathExpected);
+
+
+            });
+
+        });
+
+        describe('case one source with several targets', () => {
+
+
+            beforeEach(() => {
+                // DATA
+                sources=["A"];
+
+                // MOCK
+                graphGDS={
+                    adjacent:jest.fn((id: string) => {
+                        switch (id) {
+                            case 'A':
+                                return ['B','D'];
+                            case 'B':
+                                return ['C'];
+                            case 'D':
+                                return ['E'];
+                            default:
+                                return [];
+                        }
+
+                    }),
+                    getEdgeWeight:jest.fn(() => (1)),
+                    nodes: jest.fn(() => (['A', 'B', 'C', 'D', 'E']))
+                }
+
+                networkToGDSGraphMock.mockImplementation(async ()=>{
+                    return graphGDS;
+                });
+
+                DFSsourceDAGMock.mockReturnValue(Promise.resolve(
+                    {dfs:[
+                            'A','D','E', 'B', 'C',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                
+            });
+
+            test('case of merge', async () => {
+                // MOCK
+                BFSMock.mockReturnValueOnce([ 'C','B','A']);
+                BFSMock.mockReturnValueOnce([ 'E','D','A']);
+               
+                // DATA
+                const pathExpected = {
+                    A: { nodes: [  'C','B','A','E','D'  ], height: 3 }
+                };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL_LONGEST);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledTimes(2);
+                expect(BFSMock).toHaveBeenCalledWith({ A: [], D: [ 'A' ], E: [ 'D' ], B: [ 'A' ], C: [ 'B' ] }, 'C');
+                expect(BFSMock).toHaveBeenLastCalledWith({ A: [], D: [ 'A' ], E: [ 'D' ], B: [ 'A' ], C: [ 'B' ] }, 'E');
+                expect(result).toEqual(pathExpected);
+
+            });
+
+            test('case of no merge', async () => {
+                // MOCK
+                BFSMock.mockReturnValueOnce([ 'C','B','A']);
+                BFSMock.mockReturnValueOnce([ 'E','D','A']);
+               
+                // DATA
+                const pathExpected = {
+                    A: { nodes: [  'C','B','A' ], height: 3 }
+                };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, false ,PathType.ALL_LONGEST);
+                
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(1);
+                expect(BFSMock).toHaveBeenCalledTimes(2);
+                expect(BFSMock).toHaveBeenCalledWith({ A: [], D: [ 'A' ], E: [ 'D' ], B: [ 'A' ], C: [ 'B' ] }, 'C');
+                expect(BFSMock).toHaveBeenLastCalledWith({ A: [], D: [ 'A' ], E: [ 'D' ], B: [ 'A' ], C: [ 'B' ] }, 'E');
+                expect(result).toEqual(pathExpected);
+
+            });
+
+         });
+
+        describe('case multiple sources with one target for each', () => {
+
+            beforeEach(() => {
+                 // DATA
+                 sources=["A","D"];
+
+                // MOCK 
+                graphGDS={
+                adjacent:jest.fn((id: string) => {
+                    switch (id) {
+                        case 'A':
+                            return ['B'];
+                        case 'B':
+                            return ['C','F'];
+                        case 'D':
+                            return ['G'];
+                        case 'E':
+                            return ['F'];
+                        case 'G':
+                            return ['E'];
+                        case 'F':
+                            return ['H'];
+                        default:
+                            return [];
+                    }
+
+                }),
+                getEdgeWeight:jest.fn(() => (1)),
+                nodes: jest.fn(() => (['A', 'B', 'C', 'D', 'E', 'F','G','H']))
+            }
+
+            networkToGDSGraphMock.mockImplementation(async ()=>{
+                return graphGDS;
+            });
+
+            });
+
+            test ('independant case', async () => {
+                // MOCK 
+                graphGDS={
+                    adjacent:jest.fn((id: string) => {
+                        switch (id) {
+                            case 'A':
+                                return ['B'];
+                            case 'B':
+                                return ['C'];
+                            case 'D':
+                                return ['E'];
+                            case 'E':
+                                return ['F'];
+                            default:
+                                return [];
+                        }
+
+                    }),
+                    getEdgeWeight:jest.fn(() => (1)),
+                    nodes: jest.fn(() => (['A', 'B', 'C', 'D', 'E', 'F']))
+                }
+
+                networkToGDSGraphMock.mockImplementation(async ()=>{
+                    return graphGDS;
+                });
+
+                // DFS start : A
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'A', 'B', 'C',
+                        ], 
+                        graph:graphGDS}
+                ));
+                // DFS start : D
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'D', 'E', 'F',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // BFS start : C
+                BFSMock.mockReturnValueOnce([ 'C','B','A' ]);
+                // BFS start : F
+                BFSMock.mockReturnValueOnce([  'F','E','D' ]);
+
+                // DATA
+                const pathExpected = {
+                    A: { nodes: [ 'C','B','A' ], height: 3 },
+                    D: { nodes: [ 'F','E','D' ], height: 3 }
+                };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(2);
+                expect(DFSsourceDAGMock).toHaveBeenCalledWith(expect.anything(),['A']);
+                expect(DFSsourceDAGMock).toHaveBeenLastCalledWith(expect.anything(),['D']);
+                expect(BFSMock).toHaveBeenCalledTimes(2);
+                expect(BFSMock).toHaveBeenCalledWith({ A: [], B: [ 'A' ], C: [ 'B' ] }, 'C');
+                expect(BFSMock).toHaveBeenLastCalledWith({ D: [], E: [ 'D' ], F: [ 'E' ]}, 'F');
+                expect(result).toEqual(pathExpected);
+            });
+
+            test ('dependant case (same unique target), merge = true', async () => {
+                // MOCK
+                // DFS start : A
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'A', 'B','F','H', 'C',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // DFS start : D
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'D', 'G','E', 'F','H',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // BFS start : H (DFS with A)
+                BFSMock.mockReturnValueOnce(['H','F','B','A' ]);
+                // BFS start : H (DFS with D)
+                BFSMock.mockReturnValueOnce([ 'H','F','E','G','D' ]);
+
+                const pathExpected = { A__D: { nodes: [ 'H','F','B','A','E','G','D' ], height: 5 } };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(2);
+                expect(DFSsourceDAGMock).toHaveBeenCalledWith(expect.anything(),['A']);
+                expect(DFSsourceDAGMock).toHaveBeenLastCalledWith(expect.anything(),['D']);
+                expect(BFSMock).toHaveBeenCalledTimes(2);
+                expect(BFSMock).toHaveBeenCalledWith({ A: [], B: [ 'A' ], F: [ 'B' ], H: [ 'F' ], C: [ 'B' ] }, 'H');
+                expect(BFSMock).toHaveBeenLastCalledWith({ D: [], G: [ 'D' ], E: [ 'G' ], F: [ 'E' ], H: [ 'F' ] }, 'H');
+                expect(result).toEqual(pathExpected);
+
+            });
+
+            test ('dependant case, merge = false', async () => {
+                // MOCK
+                // DFS start : A
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'A', 'B','F','H', 'C',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // DFS start : D
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'D', 'G','E', 'F','H',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // BFS start : H (DFS with A)
+                BFSMock.mockReturnValueOnce(['H','F','B','A' ]);
+                // BFS start : H (DFS with D)
+                BFSMock.mockReturnValueOnce([ 'H','F','E','G','D' ]);
+
+                const pathExpected = { D: { nodes: ['H','F','E','G','D' ], height: 5 } };
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, false ,PathType.ALL);
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(2);
+                expect(DFSsourceDAGMock).toHaveBeenCalledWith(expect.anything(),['A']);
+                expect(DFSsourceDAGMock).toHaveBeenLastCalledWith(expect.anything(),['D']);
+                expect(BFSMock).toHaveBeenCalledTimes(2);
+                expect(BFSMock).toHaveBeenCalledWith({ A: [], B: [ 'A' ], F: [ 'B' ], H: [ 'F' ], C: [ 'B' ] }, 'H');
+                expect(BFSMock).toHaveBeenLastCalledWith({ D: [], G: [ 'D' ], E: [ 'G' ], F: [ 'E' ], H: [ 'F' ] }, 'H');
+                expect(result).toEqual(pathExpected);
+            });
+
+
+        });
+
+        describe('case multiple sources with several target possible for each', () => {
+
+            beforeEach(() => {
+                // DATA
+                sources=["A","C"];
+
+               // MOCK 
+               graphGDS={
+               adjacent:jest.fn((id: string) => {
+                   switch (id) {
+                       case 'A':
+                           return ['B'];
+                       case 'D':
+                           return ['E','F'];
+                       case 'C':
+                           return ['D'];
+                       default:
+                           return [];
+                   }
+
+               }),
+               getEdgeWeight:jest.fn(() => (1)),
+               nodes: jest.fn(() => (['A', 'B', 'C', 'D', 'E', 'F']))
+           }
+
+           networkToGDSGraphMock.mockImplementation(async ()=>{
+               return graphGDS;
+           });
+
+           });
+
+            test ('case with one start nodes associated with 2 targets, and one start with one target, merge = true', async () => {
+                // MOCK
+                // DFS start : A
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'A', 'B',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // DFS start : C
+                DFSsourceDAGMock.mockReturnValueOnce(Promise.resolve(
+                    {dfs:[
+                            'C', 'D','F', 'E',
+                        ], 
+                        graph:graphGDS}
+                ));
+
+                // BFS start : B (DFS with A)
+                BFSMock.mockReturnValueOnce(['B','A' ]);
+                // BFS start : E (DFS with C)
+                BFSMock.mockReturnValueOnce([ 'E','D','C' ]);
+                // BFS start : F (DFS with C)
+                BFSMock.mockReturnValueOnce([ 'F','D','C' ]);
+
+                const pathExpected =  {"A":{"nodes":["B","A"],"height":2},"C":{"nodes":["E","D","C","F"],"height":3}};
+
+                // TEST
+                const result = await LayoutMainChain.getPathSourcesToTargetNode(network, sources, true ,PathType.ALL);
+                console.log(JSON.stringify(result));
+
+                // EXPECT
+                expect(DFSsourceDAGMock).toHaveBeenCalledTimes(2);
+                expect(DFSsourceDAGMock).toHaveBeenCalledWith(expect.anything(),['A']);
+                expect(DFSsourceDAGMock).toHaveBeenLastCalledWith(expect.anything(),['C']);
+                expect(BFSMock).toHaveBeenCalledTimes(3);
+                expect(BFSMock).toHaveBeenCalledWith({"A":[],"B":["A"]}, 'B');
+                expect(BFSMock).toHaveBeenCalledWith({"C":[],"D":["C"],"F":["D"],"E":["D"]}, 'E');
+                expect(BFSMock).toHaveBeenLastCalledWith({"C":[],"D":["C"],"F":["D"],"E":["D"]}, 'F');
+                expect(result).toEqual(pathExpected);
+
+            });
+
+
+        });
+
+
+    });
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/LayoutManageSideCompounds.test.ts b/src/composables/__tests__/LayoutManageSideCompounds.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91dba9d1118288a127556de8fc4e3fdb64937725
--- /dev/null
+++ b/src/composables/__tests__/LayoutManageSideCompounds.test.ts
@@ -0,0 +1,760 @@
+// Type imports
+import { SubgraphNetwork } from "../../types/SubgraphNetwork";
+import { Network, Node, Link } from "../../types/TypeVizCore";
+import { VizArgs } from "../../types/EnumArgs";
+import { NetworkLayout, NodeLayout } from "../../types/NetworkLayout";
+
+
+// Composable imports
+import * as LayoutManageSideCompounds from "../LayoutManageSideCompounds";
+import {sideCompoundAttribute} from "../GetSetAttributsNodes";
+import * as VizCoreFunctions from "../VizCoreFunctions";
+import * as CalculateSize from "../CalculateSize";
+import * as GetSetAttributsNodes from "../GetSetAttributsNodes";
+
+
+
+describe("LayoutManageSideCompounds", () => {
+
+    describe('putDuplicatedSideCompoundAside', () => {
+
+        let network: Network;
+        let subgraphNetwork: SubgraphNetwork;
+        let nodes: {[key: string]: Node};
+        let links:Link[];
+
+        let duplicateAllNodesByAttributMock: jest.SpyInstance;
+        let removeAllSelectedNodesMock: jest.SpyInstance;
+        let isSideCompoundMock: jest.SpyInstance;
+        let setAsSideCompoundMock: jest.SpyInstance;
+        let isDuplicateMock: jest.SpyInstance;
+
+        beforeEach(() => {
+
+            nodes={
+                nodeA:{id: 'nodeA',x: 0, y: 0},
+                nodeC:{id: 'nodeC',x: 2, y: 2,metadata:{[sideCompoundAttribute]:true}},
+                nodeD:{id: 'nodeD',x: 3, y: 3},
+                nodeE:{id: 'nodeE',x: 4, y: 4,metadata:{[sideCompoundAttribute]:true}},
+                nodeF:{id: 'nodeF',x: 5, y: 5},
+                nodeG:{id: 'nodeG',x: 6, y: 6},
+            };
+
+            links=[
+                {id:"link", source: nodes.nodeA, target: nodes.nodeF},
+                {id:"link", source: nodes.nodeC, target: nodes.nodeF},
+                {id:"link", source: nodes.nodeF, target: nodes.nodeD},
+                {id:"link", source: nodes.nodeF, target: nodes.nodeE},
+                {id:"link", source: nodes.nodeC, target: nodes.nodeG},
+            ]
+
+            network={
+                id: 'network',
+                nodes: nodes,
+                links: links,
+            };
+
+            subgraphNetwork={
+                network: network,
+                networkStyle: {}
+            };
+
+            // MOCK
+            duplicateAllNodesByAttributMock = jest.spyOn(VizCoreFunctions, 'duplicateAllNodesByAttribut');
+            duplicateAllNodesByAttributMock.mockImplementation(() => {
+                network.nodes['nodeC0'] = {id: 'nodeC0',x: 2, y: 2,classes: ['duplicate']};
+                network.nodes['nodeC1'] = {id: 'nodeC1',x: 2, y: 2,classes: ['duplicate']};
+                delete network.nodes['nodeC'];
+                network.nodes['nodeE0'] = {id: 'nodeE0',x: 4, y: 4,classes: ['duplicate']};
+                delete network.nodes['nodeE'];
+                network.links[1].source = network.nodes['nodeC0'];
+                network.links[4].source = network.nodes['nodeC1'];
+                network.links[3].target = network.nodes['nodeE0'];
+            });
+
+            removeAllSelectedNodesMock = jest.spyOn(VizCoreFunctions, 'removeAllSelectedNodes');
+            isSideCompoundMock = jest.spyOn(GetSetAttributsNodes, 'isSideCompound');
+            isSideCompoundMock.mockImplementation((node: NodeLayout) => {
+                return node.id === 'nodeC' || node.id === 'nodeC0'|| node.id === 'nodeC1' ||
+                 node.id === 'nodeE' || node.id === 'nodeE0';
+            });
+
+            setAsSideCompoundMock = jest.spyOn(GetSetAttributsNodes, 'setAsSideCompound');
+            setAsSideCompoundMock.mockImplementation((network:Network,nodeID: string) => {
+                if (network.nodes[nodeID].metadata){
+                network.nodes[nodeID].metadata[sideCompoundAttribute]=true;
+                }else{
+                    network.nodes[nodeID].metadata={[sideCompoundAttribute]:true};
+                }
+            });
+
+            isDuplicateMock = jest.spyOn(GetSetAttributsNodes, 'isDuplicate');
+            isDuplicateMock.mockImplementation((network:Network,nodeID: string) => {
+                return  nodeID === 'nodeC0'|| nodeID === 'nodeC1' || nodeID=== 'nodeE0';
+            });
+
+        });
+
+        afterEach(() => {
+            jest.clearAllMocks();
+        });
+
+        it('should only duplicate side compound', async () => {
+            
+            // DATA
+            const resultExpected = {
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                    "nodeD": {"id": "nodeD", "x": 3, "y": 3},
+                    "nodeF": {"id": "nodeF", "x": 5, "y": 5},
+                    "nodeG": {"id": "nodeG", "x": 6, "y": 6},
+                    "nodeC0": {"id": "nodeC0", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                    "nodeC1": {"id": "nodeC1", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                    "nodeE0": {"id": "nodeE0", "x": 4, "y": 4, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                  },
+                  "links": [
+                    {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5}},
+                    {"id": "link", "source": {"id": "nodeC0", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5}},
+                    {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5}, "target": {"id": "nodeD", "x": 3, "y": 3}},
+                    {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5}, "target": {"id": "nodeE0", "x": 4, "y": 4, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}},
+                    {"id": "link", "source": {"id": "nodeC1", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeG", "x": 6, "y": 6}}
+                  ]
+                },
+                "networkStyle": {}
+              }
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.putDuplicatedSideCompoundAside(subgraphNetwork,true,false);
+
+            // EXPECT
+            expect(result).toEqual(resultExpected);
+
+        });
+
+        it('should put side compound aside', async () => {
+
+            // MOCK 
+            removeAllSelectedNodesMock.mockImplementation(() => {
+                delete network.nodes.nodeC;
+                 delete network.nodes.nodeE;
+                 const links = network.links.filter((link) => {
+                 if (link.source.id !== "nodeC" && link.target.id !== "nodeC" &&
+                     link.source.id !== "nodeE" && link.target.id !== "nodeE" 
+                 ) {
+                     return link;
+                 }
+                 });
+                 network.links = links;
+             });
+
+            // DATA
+             const resultExpected :SubgraphNetwork= {
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                    "nodeD": {"id": "nodeD", "x": 3, "y": 3},
+                    "nodeF": {"id": "nodeF", "x": 5, "y": 5},
+                    "nodeG": {"id": "nodeG", "x": 6, "y": 6}
+                  },
+                  "links": [
+                    {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5}},
+                    {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5}, "target": {"id": "nodeD", "x": 3, "y": 3}}
+                  ]
+                },
+                "networkStyle": {},
+                "sideCompounds": {
+                  "nodeF": {
+                    "reactants": [
+                      {"id": "nodeC", "x": 2, "y": 2, "metadata": {"isSideCompound": true}}
+                    ],
+                    "products": [
+                      {"id": "nodeE", "x": 4, "y": 4, "metadata": {"isSideCompound": true}}
+                    ]
+                  },
+                  "nodeG": {
+                    "reactants": [
+                      {"id": "nodeC", "x": 2, "y": 2, "metadata": {"isSideCompound": true}}
+                    ],
+                    "products": []
+                  }
+                }
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.putDuplicatedSideCompoundAside(subgraphNetwork,false,true);
+
+            // EXPECT
+            expect(result).toEqual(resultExpected);
+
+        });
+
+        it('should duplicate side compound and put them aside', async() => {
+            // MOCK
+            removeAllSelectedNodesMock.mockImplementation(() => {
+               delete network.nodes.nodeC0;
+                delete network.nodes.nodeC1;
+                delete network.nodes.nodeE0;
+                const links = network.links.filter((link) => {
+                if (link.source.id !== "nodeC0" && link.target.id !== "nodeC0" &&
+                    link.source.id !== "nodeC1" && link.target.id !== "nodeC1" &&
+                    link.source.id !== "nodeE0" && link.target.id !== "nodeE0" 
+                ) {
+                    return link;
+                }
+                });
+                network.links = links;
+            });
+
+            // DATA
+            const resultExpected :SubgraphNetwork= {
+                "network": {
+                  "id": "network",
+                  "nodes": {
+                    "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                    "nodeD": {"id": "nodeD", "x": 3, "y": 3},
+                    "nodeF": {"id": "nodeF", "x": 5, "y": 5},
+                    "nodeG": {"id": "nodeG", "x": 6, "y": 6}
+                  },
+                  "links": [
+                    {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5}},
+                    {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5}, "target": {"id": "nodeD", "x": 3, "y": 3}}
+                  ]
+                },
+                "networkStyle": {},
+                "sideCompounds": {
+                  "nodeF": {
+                    "reactants": [
+                      {"id": "nodeC0", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                    ],
+                    "products": [
+                      {"id": "nodeE0", "x": 4, "y": 4, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                    ]
+                  },
+                  "nodeG": {
+                    "reactants": [
+                      {"id": "nodeC1", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                    ],
+                    "products": []
+                  }
+                }
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.putDuplicatedSideCompoundAside(subgraphNetwork,true,true);
+
+            // EXPECT
+            expect(result).toEqual(resultExpected);
+
+        });
+
+    });
+
+
+    describe('reinsertionSideCompounds', () => {
+
+        let subgraphNetworkDupliAside: SubgraphNetwork;
+        let minEdgeLengthMock: jest.SpyInstance;
+        let getMeanNodesSizePixelMock: jest.SpyInstance;
+        let inchesToPixelsMock: jest.SpyInstance;
+
+        beforeEach(() => {
+
+            const nodes:{[key:string]:NodeLayout}= {
+                    "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                    "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                    "nodeF": {"id": "nodeF", "x": 5, "y": 5, metadataLayout:{isReversedVersion:true}},
+                };
+            subgraphNetworkDupliAside = {
+                "network": {
+                  "id": "network",
+                  nodes:nodes,
+                  "links": [
+                    {"id": "link",source: nodes.nodeA, "target": nodes.nodeF},
+                    {"id": "link", source: nodes.nodeF, "target": nodes.nodeD}
+                  ]
+                },
+                "networkStyle": {},
+                "sideCompounds": {
+                  "nodeF": {
+                    "reactants": [
+                      {"id": "nodeC0", "x": 2, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                    ],
+                    "products": [
+                      {"id": "nodeE0", "x": 4, "y": 4, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                    ]
+                  }
+                }
+              };
+
+              // MOCK
+              minEdgeLengthMock = jest.spyOn(CalculateSize, 'minEdgeLength');
+              minEdgeLengthMock.mockReturnValue(3);
+
+              getMeanNodesSizePixelMock = jest.spyOn(CalculateSize, 'getMeanNodesSizePixel');
+              getMeanNodesSizePixelMock.mockReturnValue(Promise.resolve({height:2,width:2}));
+
+              inchesToPixelsMock = jest.spyOn(CalculateSize, 'inchesToPixels');
+              inchesToPixelsMock.mockReturnValue(2);
+           
+        });
+
+        afterEach(() => {
+            jest.clearAllMocks();
+        });
+
+
+        it('should throw error because reaction node in sideCompound but not in netwotk for update when reversed version', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.network.nodes.nodeF;
+
+            // TEST & EXPECT
+            await expect(LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true)).rejects.toThrow("Reaction in side compounds but not in network");
+        
+        });
+
+        it('should throw error because reaction node in sideCompound but not in network for initializeReactionSideCompounds (no update rev)', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.network.nodes.nodeF;
+
+            // TEST & EXPECT
+            await expect(LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,false)).rejects.toThrow();
+        
+        });
+
+
+        it('should t change network because no product or reactant in reaction in side compounds', async () => {
+            // DATA
+            if (subgraphNetworkDupliAside.sideCompounds && subgraphNetworkDupliAside.sideCompounds.nodeF){
+                subgraphNetworkDupliAside.sideCompounds.nodeF.products=[];
+                subgraphNetworkDupliAside.sideCompounds.nodeF.reactants=[];
+            }
+            
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT 
+            expect(result.network).toEqual(subgraphNetworkDupliAside.network);
+        
+        });
+
+        it('should t change because no side compounds', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.sideCompounds;
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT 
+            expect(result).toEqual(subgraphNetworkDupliAside);
+        
+        });
+
+        it('should t change because side compounds empty', async () => {
+            // DATA
+            subgraphNetworkDupliAside.sideCompounds={};
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT 
+            expect(result).toEqual(subgraphNetworkDupliAside);
+        
+        });
+
+         it('should use min edge length by default, when no rank and node sep', async () => {
+            // MOCK
+            minEdgeLengthMock.mockReturnValue(NaN);
+
+            // DATA
+            const networkExpected:NetworkLayout  = {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {
+                    "id": "nodeE0",
+                    "x": 3.006,
+                    "y": 5.161,
+                    "classes": ["duplicate"],
+                    "metadata": {"isSideCompound": true}
+                  },
+                  "nodeC0": {
+                    "id": "nodeC0",
+                    "x": 5.478,
+                    "y": 6.942,
+                    "classes": ["duplicate"],
+                    "metadata": {"isSideCompound": true}
+                  }
+                },
+                "links": [
+                  {
+                    "id": "link",
+                    "source": {"id": "nodeA", "x": 0, "y": 0},
+                    "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}
+                  },
+                  {
+                    "id": "link",
+                    "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                    "target": {"id": "nodeD", "x": 7, "y": 6}
+                  },
+                  {
+                    "id": "nodeE0--nodeF",
+                    "source": {
+                      "id": "nodeE0",
+                      "x": 3.006,
+                      "y": 5.161,
+                      "classes": ["duplicate"],
+                      "metadata": {"isSideCompound": true}
+                    },
+                    "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}
+                  },
+                  {
+                    "id": "nodeF--nodeC0",
+                    "source": {
+                      "id": "nodeF",
+                      "x": 5,
+                      "y": 5,
+                      "metadataLayout": {"isReversedVersion": true}
+                    },
+                    "target": {
+                      "id": "nodeC0",
+                      "x": 5.478,
+                      "y": 6.942,
+                      "classes": ["duplicate"],
+                      "metadata": {"isSideCompound": true}
+                    }
+                  }
+                ]
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+            // => type interval : 0
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        });
+
+        it('should use min edge length by default, when rank and node sep', async () => {
+            // MOCK
+            minEdgeLengthMock.mockReturnValue(NaN);
+
+            // DATA
+            subgraphNetworkDupliAside.attributs={};
+            subgraphNetworkDupliAside.attributs[VizArgs.RANKSEP]=2;
+            subgraphNetworkDupliAside.attributs[VizArgs.NODESEP]=1;
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 3.006, "y": 5.161, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 5.478, "y": 6.942, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 7, "y": 6}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 3.006, "y": 5.161, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 5.478, "y": 6.942, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              }
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        });
+        
+
+
+        it('should reinsert side compounds when case of biggest interval includes angle 0 (test 1)', async () => {
+            // DATA
+            subgraphNetworkDupliAside.network.nodes.nodeD.x=6;
+            subgraphNetworkDupliAside.network.nodes.nodeD.y=7;
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeD": {"id": "nodeD", "x": 6, "y": 7},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 5.242, "y": 2.01, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 7.913, "y": 5.716, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {
+                    "id": "link",
+                    "source": {"id": "nodeA", "x": 0, "y": 0},
+                    "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}
+                  },
+                  {
+                    "id": "link",
+                    "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                    "target": {"id": "nodeD", "x": 6, "y": 7}
+                  },
+                  {
+                    "id": "nodeE0--nodeF",
+                    "source": {"id": "nodeE0", "x": 5.242, "y": 2.01, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                    "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}
+                  },
+                  {
+                    "id": "nodeF--nodeC0",
+                    "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                    "target": {"id": "nodeC0", "x": 7.913, "y": 5.716, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                  }
+                ]
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+            // => type interval : 2
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        
+        });
+
+        it('should reinsert side compounds but position of reactant and product is the other way', async () => {
+            // DATA 
+            subgraphNetworkDupliAside.network.nodes.nodeD.x=0;
+            subgraphNetworkDupliAside.network.nodes.nodeD.y=0;
+            subgraphNetworkDupliAside.network.nodes.nodeA.x=7;
+            subgraphNetworkDupliAside.network.nodes.nodeA.y=6;
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 7, "y": 6},
+                  "nodeD": {"id": "nodeD", "x": 0, "y": 0},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 5.716, "y": 7.913, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 7, "y": 6}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 0, "y": 0}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 5.716, "y": 7.913, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              }
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+            // => type interval : 1
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+        
+        });
+
+        it('should reinsert side compounds when case of biggest interval includes angle 0 (test 2)', async () => {
+
+            // DATA 
+            subgraphNetworkDupliAside.network.nodes.nodeD.x=0;
+            subgraphNetworkDupliAside.network.nodes.nodeD.y=0;
+            subgraphNetworkDupliAside.network.nodes.nodeA.x=6;
+            subgraphNetworkDupliAside.network.nodes.nodeA.y=7;
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 6, "y": 7},
+                  "nodeD": {"id": "nodeD", "x": 0, "y": 0},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 7.913, "y": 5.716, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 5.242, "y": 2.01, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 6, "y": 7}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 0, "y": 0}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 7.913, "y": 5.716, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 5.242, "y": 2.01, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+            
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+            // => type interval : 3
+            
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+        
+        });
+
+
+        it('should reinsert side compounds when only product in reaction', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.network.nodes.nodeA;
+            const newLinks=subgraphNetworkDupliAside.network.links.filter(link => link.source.id !== "nodeA");
+            subgraphNetworkDupliAside.network.links=newLinks;
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 3.658, "y": 7.683, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 6.341, "y": 2.316, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 7, "y": 6}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 3.658, "y": 7.683, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 6.341, "y": 2.316, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+            
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        });
+
+        it('should reinsert side compounds when only reactant in reaction', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.network.nodes.nodeD;
+            const newLinks=subgraphNetworkDupliAside.network.links.filter(link => link.target.id !== "nodeD");
+            subgraphNetworkDupliAside.network.links=newLinks;
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 7.122, "y": 2.879, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 2.879, "y": 7.122, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 7.122, "y": 2.879, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 2.879, "y": 7.122, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        });
+
+        it('should reinsert side compounds when no reactant or product in reaction', async () => {
+            // DATA
+            delete subgraphNetworkDupliAside.network.nodes.nodeD;
+            delete subgraphNetworkDupliAside.network.nodes.nodeA;
+            subgraphNetworkDupliAside.network.links=[];
+
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 4.999, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 4.999, "y": 8, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 4.999, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 4.999, "y": 8, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+                 
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+        
+        });
+
+        
+
+        it('should reinsert side compounds with several side compound of same type (reactant or product)', async () => {
+            
+            // DATA
+            const nodeB0:NodeLayout={"id": "nodeB0", "x": 1, "y": 2, "classes": ["duplicate"], "metadata": {"isSideCompound": true}};
+            if (subgraphNetworkDupliAside.sideCompounds){
+                subgraphNetworkDupliAside.sideCompounds.nodeF.reactants.push(nodeB0);
+            }
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 6.517, "y": 7.588, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeB0": {"id": "nodeB0", "x": 4.859, "y": 7.997, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 7, "y": 6}},
+                  {"id": "nodeE0--nodeF", "source": {"id": "nodeE0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeC0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeC0", "x": 6.517, "y": 7.588, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}},
+                  {"id": "nodeF--nodeB0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeB0", "x": 4.859, "y": 7.997, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,true);
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        
+        });
+
+        it('should reinsert side compounds but no update of reaction reversible', async () => {
+            // DATA
+            const networkExpected:NetworkLayout= {
+                "id": "network",
+                "nodes": {
+                  "nodeA": {"id": "nodeA", "x": 0, "y": 0},
+                  "nodeD": {"id": "nodeD", "x": 7, "y": 6},
+                  "nodeF": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}},
+                  "nodeC0": {"id": "nodeC0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}},
+                  "nodeE0": {"id": "nodeE0", "x": 5.716, "y": 7.913, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}
+                },
+                "links": [
+                  {"id": "link", "source": {"id": "nodeA", "x": 0, "y": 0}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "link", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeD", "x": 7, "y": 6}},
+                  {"id": "nodeC0--nodeF", "source": {"id": "nodeC0", "x": 2.01, "y": 5.242, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}, "target": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}},
+                  {"id": "nodeF--nodeE0", "source": {"id": "nodeF", "x": 5, "y": 5, "metadataLayout": {"isReversedVersion": true}}, "target": {"id": "nodeE0", "x": 5.716, "y": 7.913, "classes": ["duplicate"], "metadata": {"isSideCompound": true}}}
+                ]
+              };
+              
+
+            // TEST
+            const result = await LayoutManageSideCompounds.reinsertionSideCompounds(subgraphNetworkDupliAside,1,false);
+
+            // EXPECT
+            expect(result.network).toEqual(networkExpected);
+
+        });
+
+
+
+    });
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/LayoutReversibleReactions.test.ts b/src/composables/__tests__/LayoutReversibleReactions.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa65c0f3e4246b1d357d89a92af31fe7b25eb55b
--- /dev/null
+++ b/src/composables/__tests__/LayoutReversibleReactions.test.ts
@@ -0,0 +1,572 @@
+// Type imports
+import { Link, Node, Network } from "../../types/TypeVizCore";
+import { SubgraphNetwork } from "../../types/SubgraphNetwork";
+import { TypeSubgraph } from "../../types/Subgraph";
+import { StartNodesType } from "../../types/EnumArgs";
+import { NetworkLayout, NodeLayout } from "../../types/NetworkLayout";
+
+// Composable imports
+import * as GetSetAttributsNodes from "../GetSetAttributsNodes";
+import * as LayoutReversibleReactions from "../LayoutReversibleReactions";
+import * as VizCoreFunctions from "../VizCoreFunctions";
+
+
+describe('LayoutReversibleReactions', () => {
+
+    let nodes: {[key:string]:NodeLayout};
+    let linksDirection1: Link[];
+    let linksDirection2: Link[];
+    const classReversible=GetSetAttributsNodes.classReversible;
+    const reversibleAttribute=GetSetAttributsNodes.reversibleAttribute;
+    let nodesDuplicated: {[key:string]:NodeLayout};
+    let networkDuplicated: NetworkLayout;
+
+    let addMetadataReversibleWithClassMock: jest.SpyInstance;
+    let addLinkClassReversibleMock: jest.SpyInstance;
+    let isReactionMock: jest.SpyInstance;
+    let isReversibleMock: jest.SpyInstance;
+    let removeAllSelectedNodesMock:jest.SpyInstance;
+
+    beforeEach(() => {
+        
+        // DATA
+        nodes={
+            nodeA: {id:"nodeA", x:0, y:0, classes:["metabolite"]},
+            nodeB: {id:"nodeB", x:0, y:0, classes:["reaction"]},
+            nodeC: {id:"nodeC", x:0, y:0, classes:["metabolite"]},
+            nodeD: {id:"nodeD", x:0, y:0, classes:["reaction"],"metadata": {"reversible": true}},
+            nodeE: {id:"nodeE", x:0, y:0, classes:["metabolite"]},
+            nodeF: {id:"nodeF", x:0, y:0, classes:["metabolite"]},
+        }
+
+        linksDirection1 = [
+            {id:"link", source:nodes.nodeA, target:nodes.nodeB},
+            {id:"link", source:nodes.nodeB, target:nodes.nodeC},
+            {id:"link", source:nodes.nodeC, target:nodes.nodeD},
+            {id:"link", source:nodes.nodeD, target:nodes.nodeF},
+            {id:"link", source:nodes.nodeE, target:nodes.nodeD},
+        ]
+        linksDirection2 = [
+            {id:"link", source:nodes.nodeA, target:nodes.nodeB},
+            {id:"link", source:nodes.nodeB, target:nodes.nodeC},
+            {id:"link", source:nodes.nodeD, target:nodes.nodeC},
+            {id:"link", source:nodes.nodeF, target:nodes.nodeD},
+            {id:"link", source:nodes.nodeD, target:nodes.nodeE},
+        ]
+
+         nodesDuplicated= {
+            "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+            "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+            "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+            "nodeD": {"id": "nodeD", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true},"metadataLayout": {"reversibleNodeVersion": "nodeD_rev"}},
+            "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+            "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+            "nodeD_rev": {"id": "nodeD_rev", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true}, "metadataLayout": {"reversibleNodeVersion": "nodeD", "isReversedVersion": true}}
+          };
+
+          networkDuplicated= {
+            "id": "network",
+            "nodes": nodesDuplicated,
+            "links": [
+              {"id": "link","source": nodesDuplicated.nodeA, "target": nodesDuplicated.nodeB},
+              {"id": "link", "source": nodesDuplicated.nodeB, "target": nodesDuplicated.nodeC},
+              {"id": "link", "source": nodesDuplicated.nodeC, "target": nodesDuplicated.nodeD,"classes": ["reversible"]},
+              {"id": "link", "source": nodesDuplicated.nodeD, "target": nodesDuplicated.nodeF,"classes": ["reversible"]},
+              {"id": "link", "source": nodesDuplicated.nodeE, "target": nodesDuplicated.nodeD,"classes": ["reversible"]},
+              {"id": "nodeD_rev--nodeC", "source": nodesDuplicated.nodeD_rev, "target": nodesDuplicated.nodeC, "classes": ["reversible"]},
+              {"id": "nodeF--nodeD_rev", "source": nodesDuplicated.nodeF, "target": nodesDuplicated.nodeD_rev, "classes": ["reversible"]},
+              {"id": "nodeD_rev--nodeE", "source":  nodesDuplicated.nodeD_rev, "target": nodesDuplicated.nodeE, "classes": ["reversible"]}
+            ]
+          }
+
+        // MOCK
+        addLinkClassReversibleMock = jest.spyOn(GetSetAttributsNodes, 'addLinkClassReversible');
+        addLinkClassReversibleMock.mockImplementation( (link:Link) => {
+            link.classes=[classReversible];
+            return link;
+        });
+
+        isReactionMock = jest.spyOn(GetSetAttributsNodes, 'isReaction');
+        isReactionMock.mockImplementation( (node:Node) => {
+            return node.id==="nodeD" || node.id==="nodeD_rev" || node.id==="nodeB";
+        });
+
+        isReversibleMock = jest.spyOn(GetSetAttributsNodes, 'isReversible');
+        isReversibleMock.mockImplementation( (network:Network,id:string) => {
+            return id==="nodeD" || id==="nodeD_rev";
+        });
+
+        removeAllSelectedNodesMock=jest.spyOn(VizCoreFunctions,'removeAllSelectedNodes');
+        removeAllSelectedNodesMock.mockImplementation((reactionToRemove:string[],network:Network) => {
+            delete network.nodes[reactionToRemove[0]];
+            const links = network.links.filter((link) => {
+                if (link.source.id !== reactionToRemove[0] && link.target.id !== reactionToRemove[0]) {
+                return link;
+                }
+            });
+            network.links = links;
+        });
+
+
+
+    });
+
+
+    afterEach(() => {
+        jest.clearAllMocks();
+    });
+
+
+    describe('duplicateReversibleReactions', () => {
+
+        it('should duplicate reversible reactions', async () => {
+            // DATA
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: linksDirection1
+            }
+
+            // TEST
+            await LayoutReversibleReactions.duplicateReversibleReactions(network);
+
+            // EXPECT
+            expect(network).toEqual(networkDuplicated);
+
+
+        });
+        it('should duplicate reversible reactions when node is a reversed version', async () => {
+
+            // DATA
+            nodes.nodeD.metadataLayout={isReversedVersion:true};
+            const network:Network = {
+                id:"network",
+                nodes: nodes,
+                links: linksDirection1
+            }
+
+            const nodesDuplicated:{[key:string]:NodeLayout}= {
+                "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+                "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD": {"id": "nodeD", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true},"metadataLayout": {"reversibleNodeVersion": "nodeD_rev","isReversedVersion": true}},
+                "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD_rev": {"id": "nodeD_rev", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true}, "metadataLayout": {"reversibleNodeVersion": "nodeD", "isReversedVersion": false}}
+              };
+    
+            const networkDuplicated:NetworkLayout= {
+                "id": "network",
+                "nodes": nodesDuplicated,
+                "links": [
+                  {"id": "link","source": nodesDuplicated.nodeA, "target": nodesDuplicated.nodeB},
+                  {"id": "link", "source": nodesDuplicated.nodeB, "target": nodesDuplicated.nodeC},
+                  {"id": "link", "source": nodesDuplicated.nodeC, "target": nodesDuplicated.nodeD,"classes": ["reversible"]},
+                  {"id": "link", "source": nodesDuplicated.nodeD, "target": nodesDuplicated.nodeF,"classes": ["reversible"]},
+                  {"id": "link", "source": nodesDuplicated.nodeE, "target": nodesDuplicated.nodeD,"classes": ["reversible"]},
+                  {"id": "nodeD_rev--nodeC", "source": nodesDuplicated.nodeD_rev, "target": nodesDuplicated.nodeC, "classes": ["reversible"]},
+                  {"id": "nodeF--nodeD_rev", "source": nodesDuplicated.nodeF, "target": nodesDuplicated.nodeD_rev, "classes": ["reversible"]},
+                  {"id": "nodeD_rev--nodeE", "source":  nodesDuplicated.nodeD_rev, "target": nodesDuplicated.nodeE, "classes": ["reversible"]}
+                ]
+              }
+
+            // TEST
+            await LayoutReversibleReactions.duplicateReversibleReactions(network);
+
+            // EXPECT
+            expect(network).toEqual(networkDuplicated);
+
+        });
+
+
+    });
+
+    describe('renameAllIDNode', () => {
+
+        it('should rename all nodes in network, no subgraph involved',( ) => {
+            // DATA
+            const nodesBeforeRename:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, classes:["metabolite"]},
+                nodeB: {id:"nodeB", x:0, y:0, classes:["reaction"]},
+                nodeC: {id:"nodeC", x:0, y:0, classes:["metabolite"]}
+            }
+            const nodesAfterRename:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, classes:["metabolite"]},
+                newName: {id:"newName", x:0, y:0, classes:["reaction"]},
+                nodeC: {id:"nodeC", x:0, y:0, classes:["metabolite"]}
+            }
+            const networkBeforeRename:Network = {
+                id:"network",
+                nodes: nodesBeforeRename,
+                links: [
+                    {id:"link", source:nodesBeforeRename.nodeA, target:nodesBeforeRename.nodeB},
+                    {id:"link", source:nodesBeforeRename.nodeB, target:nodesBeforeRename.nodeC},
+                ]
+            }
+            const networkAfterRename:Network = {
+                id:"network",
+                nodes: nodesBeforeRename,
+                links: [
+                    {id:"link", source:nodesAfterRename.nodeA, target:nodesAfterRename.newName},
+                    {id:"link", source:nodesAfterRename.newName, target:nodesAfterRename.nodeC},
+                ]
+            }
+            let subgraphNetwork:SubgraphNetwork = {
+                network: networkBeforeRename,
+                networkStyle: {}
+            }
+            let subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkAfterRename,
+                networkStyle: {}
+            }
+            const nodesToRename:{[key: string]: string}={"nodeB":"newName"};
+            
+            // TEST
+            subgraphNetwork=LayoutReversibleReactions.renameAllIDNode(subgraphNetwork,nodesToRename);
+
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+
+        });
+
+        it('should rename all nodes in network, with subgraph involved',( ) => {
+            // DATA
+            const nodesBeforeRename:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, classes:["metabolite"],
+                    metadataLayout:{[TypeSubgraph.SECONDARY_CHAIN]:["secondaryChain"],
+                    [TypeSubgraph.MAIN_CHAIN]:["mainChain"],
+                    }},
+                nodeB: {id:"nodeB", x:0, y:0, classes:["reaction"],
+                    metadataLayout:{[TypeSubgraph.CYCLE]:["cycle"],
+                    [TypeSubgraph.SECONDARY_CHAIN]:["secondaryChain"],
+                    }},
+                 }
+            const nodesAfterRename:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, classes:["metabolite"],
+                    metadataLayout:{[TypeSubgraph.SECONDARY_CHAIN]:["secondaryChain"],
+                    [TypeSubgraph.MAIN_CHAIN]:["mainChain"],
+                    }},
+                    newName: {id:"newName", x:0, y:0, classes:["reaction"],
+                    metadataLayout:{[TypeSubgraph.CYCLE]:["cycle"],
+                    [TypeSubgraph.SECONDARY_CHAIN]:["secondaryChain"],
+                    }}
+            }
+        
+            const networkBeforeRename:Network = {
+                id:"network",
+                nodes: nodesBeforeRename,
+                links: [
+                    {id:"link", source:nodesBeforeRename.nodeA, target:nodesBeforeRename.nodeB},
+                ]
+            }
+            const networkAfterRename:Network = {
+                id:"network",
+                nodes: nodesBeforeRename,
+                links: [
+                    {id:"link", source:nodesAfterRename.nodeA, target:nodesAfterRename.newName},
+                ]
+            }
+            let subgraphNetwork:SubgraphNetwork = {
+                network: networkBeforeRename,
+                networkStyle: {},
+                [TypeSubgraph.CYCLE]:{"cycle":{name:"cycle",nodes:["nodeB"],type:TypeSubgraph.CYCLE}},
+                [TypeSubgraph.MAIN_CHAIN]:{"mainChain":{name:"mainChain",nodes:["nodeA"],type:TypeSubgraph.MAIN_CHAIN}},
+                [TypeSubgraph.SECONDARY_CHAIN]:{"secondaryChain":{name:"secondaryChain",nodes:["nodeB","nodeA"],type:TypeSubgraph.SECONDARY_CHAIN}}
+            }
+            let subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkAfterRename,
+                networkStyle: {},
+                [TypeSubgraph.CYCLE]:{"cycle":{name:"cycle",nodes:["newName"],type:TypeSubgraph.CYCLE}},
+                [TypeSubgraph.MAIN_CHAIN]:{"mainChain":{name:"mainChain",nodes:["nodeA"],type:TypeSubgraph.MAIN_CHAIN}},
+                [TypeSubgraph.SECONDARY_CHAIN]:{"secondaryChain":{name:"secondaryChain",nodes:["newName","nodeA"],type:TypeSubgraph.SECONDARY_CHAIN}}
+           
+            }
+            const nodesToRename:{[key: string]: string}={"nodeB":"newName"};
+            
+            // TEST
+            subgraphNetwork=LayoutReversibleReactions.renameAllIDNode(subgraphNetwork,nodesToRename);
+
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+
+        });
+        
+    });
+
+
+    describe('keepFirstReversibleNode', () => {
+        
+        it('should keep the first reversible node, when original node first, no rename so return nodeToRename', () => {
+            // DATA 
+            const nodeOrder:string[]=["nodeA","nodeB","nodeC","nodeD","nodeE","nodeF","nodeD_rev"];
+            const subgraphNetwork:SubgraphNetwork = {
+                network: networkDuplicated,
+                networkStyle: {}
+            }
+            const nodesExpected:{[key:string]:NodeLayout}={
+                "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+                "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD": {"id": "nodeD", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true},"metadataLayout": {}},
+                "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+            };
+            const linksExpected:Link[]= [
+                {"id": "link","source": nodesExpected.nodeA, "target": nodesExpected.nodeB},
+                {"id": "link", "source": nodesExpected.nodeB, "target": nodesExpected.nodeC},
+                {"id": "link", "source": nodesExpected.nodeC, "target": nodesExpected.nodeD,"classes": ["reversible"]},
+                {"id": "link", "source": nodesExpected.nodeD, "target": nodesExpected.nodeF,"classes": ["reversible"]},
+                {"id": "link", "source": nodesExpected.nodeE, "target": nodesExpected.nodeD,"classes": ["reversible"]},
+            ]
+            const networkExpected:NetworkLayout = {
+                "id": "network",
+                "nodes": nodesExpected,
+                "links": linksExpected
+            }
+            const subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkExpected,
+                networkStyle: {}
+            }
+            const nodeToRenameExpected:{[key:string]:string}={};
+
+            // TEST 
+            const nodeToRename=LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false) as SubgraphNetwork;
+
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+            expect(nodeToRename).toEqual(nodeToRenameExpected);
+
+        });
+       
+        it('should keep the first reversible node, when reversed node first, no rename so return nodeToRename', () => {
+            // DATA 
+            const nodeOrder:string[]=["nodeA","nodeB","nodeC","nodeD_rev","nodeE","nodeF","nodeD"];
+            const subgraphNetwork:SubgraphNetwork = {
+                network: networkDuplicated,
+                networkStyle: {}
+            }
+            const nodesExpected:{[key:string]:NodeLayout}={
+                "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+                "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD_rev": {"id": "nodeD_rev", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true}, "metadataLayout": {"isReversedVersion": true}}
+              };
+            const linksExpected:Link[]= [
+                {"id": "link","source": nodesExpected.nodeA, "target": nodesExpected.nodeB},
+                {"id": "link", "source": nodesExpected.nodeB, "target": nodesExpected.nodeC},
+                {"id": "nodeD_rev--nodeC", "source": nodesExpected.nodeD_rev, "target": nodesExpected.nodeC, "classes": ["reversible"]},
+                {"id": "nodeF--nodeD_rev", "source": nodesExpected.nodeF, "target": nodesExpected.nodeD_rev, "classes": ["reversible"]},
+                {"id": "nodeD_rev--nodeE", "source":  nodesExpected.nodeD_rev, "target": nodesExpected.nodeE, "classes": ["reversible"]}
+              ]
+            const networkExpected:NetworkLayout = {
+                "id": "network",
+                "nodes": nodesExpected,
+                "links": linksExpected
+            }
+            const subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkExpected,
+                networkStyle: {}
+            }
+            const nodeToRenameExpected:{[key:string]:string}={"nodeD_rev":"nodeD"};
+
+            // TEST 
+            const nodeToRename=LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false) as SubgraphNetwork;
+
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+            expect(nodeToRename).toEqual(nodeToRenameExpected);
+
+        });
+
+        it('should keep the first reversible node, when reversed node first, rename so return subgraphNetwork', () => {
+            // DATA 
+            const nodeOrder:string[]=["nodeA","nodeB","nodeC","nodeD_rev","nodeE","nodeF","nodeD"];
+            let subgraphNetwork:SubgraphNetwork = {
+                network: networkDuplicated,
+                networkStyle: {}
+            }
+            const nodesExpected:{[key:string]:NodeLayout}={
+                "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+                "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD": {"id": "nodeD", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true}, "metadataLayout": {"isReversedVersion": true}}
+              };
+            const linksExpected:Link[]= [
+                {"id": "link","source": nodesExpected.nodeA, "target": nodesExpected.nodeB},
+                {"id": "link", "source": nodesExpected.nodeB, "target": nodesExpected.nodeC},
+                {"id": "nodeD_rev--nodeC", "source": nodesExpected.nodeD, "target": nodesExpected.nodeC, "classes": ["reversible"]},
+                {"id": "nodeF--nodeD_rev", "source": nodesExpected.nodeF, "target": nodesExpected.nodeD, "classes": ["reversible"]},
+                {"id": "nodeD_rev--nodeE", "source":  nodesExpected.nodeD, "target": nodesExpected.nodeE, "classes": ["reversible"]}
+              ]
+            const networkExpected:NetworkLayout = {
+                "id": "network",
+                "nodes": nodesExpected,
+                "links": linksExpected
+            }
+            const subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkExpected,
+                networkStyle: {}
+            }
+
+            // TEST 
+            subgraphNetwork=LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,true) as SubgraphNetwork;
+
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+
+        });
+
+        it('shouldn t find a node of orderNode in the network, and throw an error', () => {
+            // DATA
+            const network:NetworkLayout = {
+                id:"network",
+                nodes: {},
+                links: []
+            }
+            const subgraphNetwork:SubgraphNetwork = {
+                network: network,
+                networkStyle: {}
+            }
+            const nodeOrder:string[]=["nodeA"];
+
+            // TEST && EXPECT
+            expect(()=>LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false)).toThrow();
+
+        });
+        it('shouldn t find the reverse version of a node in the network, and throw an error', () => {
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, metadataLayout:{reversibleNodeVersion:"nodeA_rev"}}
+            }
+
+            const network:NetworkLayout = {
+                id:"network",
+                nodes: nodes,
+                links: []
+            }
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network: network,
+                networkStyle: {}
+            }
+            
+            const nodeOrder:string[]=["nodeA"];
+
+            // TEST && EXPECT
+            expect(()=>LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false)).toThrow();
+
+        });
+        it('shouldn t find the attribut isReversedVersion for both node, and throw an error', () => {
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, metadataLayout:{reversibleNodeVersion:"nodeA_rev"}},
+                nodeA_rev: {id:"nodeA_rev", x:0, y:0, metadataLayout:{reversibleNodeVersion:"nodeA"}}
+            }
+
+            const network:NetworkLayout = {
+                id:"network",
+                nodes: nodes,
+                links: []
+            }
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network: network,
+                networkStyle: {}
+            }
+            
+            const nodeOrder:string[]=["nodeA","nodeA_rev"];
+
+            // TEST && EXPECT
+            expect(()=>LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false)).toThrow();
+
+        });
+
+        it('should find the attribut isReversedVersion for both node, and throw an error', () => {
+            // DATA
+            const nodes:{[key:string]:NodeLayout}={
+                nodeA: {id:"nodeA", x:0, y:0, metadataLayout:{reversibleNodeVersion:"nodeA_rev", isReversedVersion:true}},
+                nodeA_rev: {id:"nodeA_rev", x:0, y:0, metadataLayout:{reversibleNodeVersion:"nodeA",isReversedVersion:true}}
+            }
+
+            const network:NetworkLayout = {
+                id:"network",
+                nodes: nodes,
+                links: []
+            }
+
+            const subgraphNetwork:SubgraphNetwork = {
+                network: network,
+                networkStyle: {}
+            }
+            
+            const nodeOrder:string[]=["nodeA","nodeA_rev"];
+
+            // TEST && EXPECT
+            expect(()=>LayoutReversibleReactions.keepFirstReversibleNode(subgraphNetwork,nodeOrder,false)).toThrow();
+
+        });
+
+
+
+    });
+
+    describe('chooseReversibleReaction', () => {
+
+        it('should choose the version of a node and suppress the other one', async () => {
+
+            // DATA
+            const nodeOrder = function (network: Network, sources: Array<string> | StartNodesType): Promise<string[]> {
+                if (sources.length === 1 && sources[0] === "nodeA") {
+                    return Promise.resolve(["nodeD_rev","nodeD"]);
+                }else{
+                    throw new Error("wrong input passed to nodeOrder");
+                }
+            };
+            const sources: Array<string> = ["nodeA"];
+
+            let subgraphNetwork:SubgraphNetwork = {
+                network: networkDuplicated,
+                networkStyle: {}
+            };
+
+            const nodesExpected:{[key:string]:NodeLayout}={
+                "nodeA": {"id": "nodeA", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeB": {"id": "nodeB", "x": 0, "y": 0, "classes": ["reaction"]},
+                "nodeC": {"id": "nodeC", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeE": {"id": "nodeE", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeF": {"id": "nodeF", "x": 0, "y": 0, "classes": ["metabolite"]},
+                "nodeD": {"id": "nodeD", "x": 0, "y": 0, "classes": ["reaction"], "metadata": {"reversible": true}, "metadataLayout": {"isReversedVersion": true}}
+              };
+            const linksExpected:Link[]= [
+                {"id": "link","source": nodesExpected.nodeA, "target": nodesExpected.nodeB},
+                {"id": "link", "source": nodesExpected.nodeB, "target": nodesExpected.nodeC},
+                {"id": "nodeD_rev--nodeC", "source": nodesExpected.nodeD, "target": nodesExpected.nodeC, "classes": ["reversible"]},
+                {"id": "nodeF--nodeD_rev", "source": nodesExpected.nodeF, "target": nodesExpected.nodeD, "classes": ["reversible"]},
+                {"id": "nodeD_rev--nodeE", "source":  nodesExpected.nodeD, "target": nodesExpected.nodeE, "classes": ["reversible"]}
+              ]
+            const networkExpected:NetworkLayout = {
+                "id": "network",
+                "nodes": nodesExpected,
+                "links": linksExpected
+            }
+            const subgraphNetworkExpected:SubgraphNetwork = {
+                network: networkExpected,
+                networkStyle: {}
+            }
+
+            // TEST
+            subgraphNetwork=await LayoutReversibleReactions.chooseReversibleReaction(subgraphNetwork,sources,nodeOrder);
+            
+            // EXPECT
+            expect(subgraphNetwork).toEqual(subgraphNetworkExpected);
+        });
+
+    });
+
+
+});
+
diff --git a/src/composables/__tests__/LayoutSugiyama.test.ts b/src/composables/__tests__/LayoutSugiyama.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4dd3dc794338e27910575c8c4379297133bf5a65
--- /dev/null
+++ b/src/composables/__tests__/LayoutSugiyama.test.ts
@@ -0,0 +1,170 @@
+// Type imports
+import { Network, Node, Link, GraphStyleProperties } from "../../types/TypeVizCore";
+import { SubgraphNetwork } from '../../types/SubgraphNetwork';
+
+
+// Composable imports
+import * as LayoutSugiyama from '../LayoutSugiyama';
+import * as ConvertFromNetwork from '../ConvertFromNetwork';
+import * as ConvertToNetwork from '../ConvertToNetwork';
+import * as CalculateSize from "../CalculateSize";
+
+
+// General imports
+import { instance } from "@viz-js/viz";
+import { JsonViz } from '../../types/FormatJsonViz';
+
+
+
+// MOCK VIZ and DATA for mock
+
+const jsonViz:JsonViz ={
+    directed: true,
+    objects: [
+        {
+        name: 'node1',
+        pos: '12.6,12.6',
+        },
+        {
+        name: 'node2',
+        pos: '12.6,325.8',
+        },
+        {
+        name: 'node3',
+        pos: '12.6,639',
+        },
+        {
+        name: 'node4',
+        pos: '12.6,952.2',
+        },
+        {
+        name: 'node5',
+        pos: '12.6,1265.4',
+        }
+    ],
+    edges: []
+    };
+
+const DOT:string=`strict digraph G {
+        graph [rankdir="BT", newrank="true", compound="true", splines="false", ranksep="4", nodesep="4", dpi="72"]
+        node1  [height="0.35", width="0.35", fixedsize="true"];
+        node2  [height="0.35", width="0.35", fixedsize="true"];
+        node3  [height="0.35", width="0.35", fixedsize="true"];
+        node4  [height="0.35", width="0.35", fixedsize="true"];
+        node5  [height="0.35", width="0.35", fixedsize="true"];
+        node1 -> node2;
+        node2 -> node3;
+        node3 -> node4;
+        node4 -> node5;
+}`;
+
+jest.mock('@viz-js/viz', () => ({
+    instance: jest.fn(
+    async() => ({
+        renderJSON: jest.fn(
+            (dot) => {
+                if (dot === DOT) {
+                    return jsonViz
+                }else{
+                    throw new Error('No right input renderJSON');
+                }
+            }
+        )
+    }))
+}));
+
+
+
+describe('LayoutSugiyama', () => {
+
+    // DATA
+
+    const networkStyle: GraphStyleProperties = {};
+
+    const nodes:{[key:string]:Node}= {
+        node1: { id: 'node1', x:1, y:2 },
+        node2: { id: 'node2', x:3, y:4 },
+        node3: { id: 'node3', x:5, y:6 },
+        node4: { id: 'node4', x:7, y:8 },
+        node5: { id: 'node5', x:9, y:10 },
+    };
+    const links:Link[]= [
+        { id: 'link', source: nodes.node1 , target:nodes.node2 },
+        { id: 'link', source: nodes.node2 , target:nodes.node3 },
+        { id: 'link', source: nodes.node3 , target:nodes.node4 },
+        { id: 'link', source: nodes.node4 , target:nodes.node5 },
+    ]
+    const network :Network= {
+        id: 'network',
+        nodes: nodes,
+        links: links,  
+    };
+    const subgraphNetwork:SubgraphNetwork = {
+        network: network,
+        networkStyle: networkStyle,
+    };
+
+    const nodesViz:{[key:string]:Node}= {
+        node1: { id: 'node1', x: 12.6, y: 12.6 },
+        node2: { id: 'node2', x: 12.6, y: 325.8 },
+        node3: { id: 'node3', x: 12.6, y: 639 },
+        node4: { id: 'node4', x: 12.6, y: 952.2 },
+        node5: { id: 'node5', x: 12.6, y: 1265.4 }
+        };
+    const linksViz:Link[] = [
+        { id: 'link', source: nodesViz.node1 , target:nodesViz.node2 },
+        { id: 'link', source: nodesViz.node2 , target:nodesViz.node3 },
+        { id: 'link', source: nodesViz.node3 , target:nodesViz.node4 },
+        { id: 'link', source: nodesViz.node4 , target:nodesViz.node5 },
+    ];
+    const networkViz:Network = {
+        id: 'network',
+        nodes: nodesViz,
+        links: linksViz
+    };
+    
+
+    // MOCK
+    
+    const getSepAttributesInchesMock = jest.spyOn(CalculateSize, 'getSepAttributesInches');
+    getSepAttributesInchesMock.mockReturnValue(Promise.resolve({rankSep: 4, nodeSep: 4}));
+
+    const networkToDOTMock = jest.spyOn(ConvertFromNetwork, 'networkToDOT');
+    networkToDOTMock.mockReturnValue(Promise.resolve(DOT));
+
+    const changeNetworkFromVizMock = jest.spyOn(ConvertToNetwork, 'changeNetworkFromViz');
+    changeNetworkFromVizMock.mockImplementation(async(json,subgraphNetwork,assignRank)=>{
+        if (JSON.stringify(json) === JSON.stringify(jsonViz)) {
+            const network = subgraphNetwork.network;
+            network.nodes.node1.x = 12.6;
+            network.nodes.node1.y = 12.6;
+            network.nodes.node2.x = 12.6;
+            network.nodes.node2.y = 325.8;
+            network.nodes.node3.x = 12.6;
+            network.nodes.node3.y = 639;
+            network.nodes.node4.x = 12.6;
+            network.nodes.node4.y = 952.2;
+            network.nodes.node5.x = 12.6;
+            network.nodes.node5.y = 1265.4;
+        }else{
+            throw new Error('No right input');
+        }
+        return subgraphNetwork;
+    });
+
+
+    test('vizLayout with default parameters', async () => {
+
+        // TEST
+        await LayoutSugiyama.vizLayout(subgraphNetwork);
+
+        // EXPECT
+        expect(getSepAttributesInchesMock).toHaveBeenCalledTimes(1);
+        expect(networkToDOTMock).toHaveBeenCalledTimes(1);
+        expect(changeNetworkFromVizMock).toHaveBeenCalledTimes(1);
+        expect(subgraphNetwork.network).toEqual(networkViz);
+
+    });
+
+
+});
\ No newline at end of file
diff --git a/src/composables/__tests__/SubgraphForSubgraphNetwork.test.ts b/src/composables/__tests__/SubgraphForSubgraphNetwork.test.ts
index 4ef32dfcef7274669e9a3d00d3f6c911c612d29f..d391fece80c65fd242f499977dcda3853a8ec42f 100644
--- a/src/composables/__tests__/SubgraphForSubgraphNetwork.test.ts
+++ b/src/composables/__tests__/SubgraphForSubgraphNetwork.test.ts
@@ -1,6 +1,5 @@
 // Type imports
-import { Link } from '@metabohub/viz-core/src/types/Link';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
+import { Link, GraphStyleProperties } from "../../types/TypeVizCore";
 import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 import { NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
 import { Subgraph, TypeSubgraph } from '../../types/Subgraph';
diff --git a/src/composables/__tests__/SubgraphForViz.test.ts b/src/composables/__tests__/SubgraphForViz.test.ts
index 79653160486f85299d7a6d29ff26c7457946716c..ffcb9fee51a9f769b90e7510adb724fd2ebaf67d 100644
--- a/src/composables/__tests__/SubgraphForViz.test.ts
+++ b/src/composables/__tests__/SubgraphForViz.test.ts
@@ -1,6 +1,5 @@
 // Type imports
-import { Link } from '@metabohub/viz-core/src/types/Link';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
+import { Link, GraphStyleProperties } from "../../types/TypeVizCore";
 import { SubgraphNetwork } from '../../types/SubgraphNetwork';
 import { NetworkLayout, NodeLayout } from '../../types/NetworkLayout';
 import { Subgraph, TypeSubgraph } from '../../types/Subgraph';
diff --git a/src/composables/__tests__/VizCoreFunctions.test.ts b/src/composables/__tests__/VizCoreFunctions.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba82ada4ee03714dc360f993f23f7073e6ad5d9c
--- /dev/null
+++ b/src/composables/__tests__/VizCoreFunctions.test.ts
@@ -0,0 +1,214 @@
+// Type imports
+import { GraphStyleProperties, Link, Network } from  "../../types/TypeVizCore";
+
+// Composable imports
+import * as VizCoreFunctions from '../VizCoreFunctions';
+
+
+describe('VizCoreFunctions', () => {
+
+    let network: Network;
+    let networkStyle: GraphStyleProperties;
+
+    beforeEach(() => {
+        network = {
+            id: 'testNetwork',
+            nodes: {
+              "A": {
+                id: "A",
+                label: "A",
+                x: 0,
+                y: 0,
+                metadata:{toDuplicate: true}
+              },
+              "B": {
+                id: "B",
+                label: "B",
+                x: 10,
+                y: 10,
+                metadata:{toDuplicate: true}
+              },
+              "C": {
+                id: "C",
+                label: "C",
+                x: 20,
+                y: 20,
+                metadata:{toDuplicate: false}
+              },
+              "D": {
+                id: "D",
+                label: "D",
+                x: 30,
+                y: 30
+              },
+              "E": {
+                id: "E",
+                label: "E",
+                x: 40,
+                y: 40,
+                metadata:{toDuplicate: true}
+              },
+              "F": {
+                id: "F",
+                label: "F",
+                x: 50,
+                y: 50
+              }
+            },
+            links: [
+              {
+                id: 'A - B',
+                source: {
+                  id: "A",
+                  label: "A",
+                  x: 0,
+                  y: 0
+                },
+                target: {
+                  id: "B",
+                  label: "B",
+                  x: 10,
+                  y: 10
+                }
+              },
+              {
+                id: 'B - C',
+                source: {
+                  id: "B",
+                  label: "B",
+                  x: 10,
+                  y: 10
+                },
+                target: {
+                  id: "C",
+                  label: "C",
+                  x: 20,
+                  y: 20
+                }
+              },
+              {
+                id: 'C - A',
+                source: {
+                  id: "C",
+                  label: "C",
+                  x: 20,
+                  y: 20
+                },
+                target: {
+                  id: "A",
+                  label: "A",
+                  x: 0,
+                  y: 0
+                }
+              },
+              {
+                id: 'D - F',
+                source: {
+                  id: "D",
+                  label: "D",
+                  x: 30,
+                  y: 30
+                },
+                target: {
+                  id: "F",
+                  label: "F",
+                  x: 50,
+                  y: 50
+                }
+              },
+              {
+                id: 'D - E',
+                source: {
+                  id: "D",
+                  label: "D",
+                  x: 30,
+                  y: 30
+                },
+                target: {
+                  id: "E",
+                  label: "E",
+                  x: 40,
+                  y: 40
+                }
+              }
+            ]
+          }
+          
+        networkStyle= {
+            nodeStyles: {},
+            linkStyles: {}
+          }
+    });
+      
+
+    describe('duplicateNode', () => {
+
+        it('should duplicate node and suppress the original', () => {
+            // TEST
+            VizCoreFunctions.duplicateNode('D', network, networkStyle)
+
+            // EXPECT
+            expect(Object.keys(network.nodes).includes('D')).not.toBeTruthy();
+            network.links.forEach((link: Link) => {
+                expect(link.source.id).not.toEqual('D');
+                expect(link.target.id).not.toEqual('D');
+            });
+
+            expect(Object.keys(network.nodes).includes('D0')).toBeTruthy();
+            expect(Object.keys(network.nodes).includes('D1')).toBeTruthy();
+
+            let newLinksCount = 0;
+            network.links.forEach((link: Link) => {
+            if (
+                link.source.id === 'D0' || 
+                link.source.id === 'D1' ||
+                link.target.id === 'D0' ||
+                link.target.id === 'D1'
+            ) {
+                newLinksCount += 1;
+            }
+            });
+
+            expect(newLinksCount).toEqual(2);
+        });
+
+    });
+
+    describe('duplicateAllNodesByAttribut', () => {
+
+        it('should duplicate all nodes by attribut', () => {
+            // DATA
+            const nodesIDExpected:string[] = ['A0', 'A1', 'B0', 'B1','C','D', 'E0','F' ];
+
+            // TEST
+            VizCoreFunctions.duplicateAllNodesByAttribut(network, networkStyle, 'toDuplicate');
+
+            // EXPECT
+            expect(Object.keys(network.nodes).sort()).toEqual(nodesIDExpected.sort());
+
+        });
+
+
+    });
+
+    describe('removeAllSelectedNodes', () => {
+        it('should remove all selected nodes', () => {
+            // TEST
+            VizCoreFunctions.removeAllSelectedNodes(['A', 'B'], network);
+
+            // EXPECT
+            expect(Object.keys(network.nodes).includes('A')).not.toBeTruthy();
+            expect(Object.keys(network.nodes).includes('B')).not.toBeTruthy();
+            network.links.forEach((link: Link) => {
+                expect(link.source.id).not.toEqual('A');
+                expect(link.target.id).not.toEqual('A');
+
+                expect(link.source.id).not.toEqual('B');
+                expect(link.target.id).not.toEqual('B');
+            });
+        });
+
+    });
+
+});
+
diff --git a/src/composables/countIntersections.ts b/src/composables/countIntersections.ts
deleted file mode 100644
index f833b1a2cbbb22888eeeb9fe157d3f3b81dba9ec..0000000000000000000000000000000000000000
--- a/src/composables/countIntersections.ts
+++ /dev/null
@@ -1,429 +0,0 @@
-import { Node } from '@metabohub/viz-core/src/types/Node';
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { checkIntersection } from 'line-intersect';
-import { Link } from '@metabohub/viz-core/src/types/Link';
-import { c } from 'vite/dist/node/types.d-aGj9QkWt';
-import { GraphStyleProperties } from '@metabohub/viz-core/src/types/GraphStyleProperties';
-import { getSizeNodePixel } from './CalculateSize';
-import { link } from 'fs';
-import { Coordinate, Size } from '../types/CoordinatesSize';
-
-
-// CHNAGE THE CODE : SEE METRICSNETWORK (for end of internship : keep this one)
-
-/**
- * Check if the coordinates (x, y) are the same as the node's coordinates
- * @param node the node
- * @param x coordinate
- * @param y coordinate
- * @returns a boolean
- */
-function isNodeCoord(node: {x:number,y:number}, x: number, y: number): boolean {
-    return (node.x == x && node.y == y);
-}
-
-
-
-//______________________Intersection in the network______________________
-/**
- * Check if the 2 edges are crossing. 
- * Coming from the same node or going to the same node doesn't count as crossing.
- * @param link1 an edge
- * @param link2 an edge
- * @returns a boolean
- */
-// function edgesIntersectionLink(link1: Link, link2: Link,style:GraphStyleProperties): boolean {
-
-//     // case of common node
-//     if (commonNodeBetween2Links(link1,link2)) {
-//         return false;
-//     }
-
-//     let x1: Node = link1.source;
-//     const x1Center=AdjustCoordNodeToCenter(x1,style);
-//     let x2: Node = link1.target;
-//     const x2Center=AdjustCoordNodeToCenter(x2,style);
-//     let x3: Node = link2.source;
-//     const x3Center=AdjustCoordNodeToCenter(x3,style);
-//     let x4: Node = link2.target;
-//     const x4Center=AdjustCoordNodeToCenter(x4,style);
-
-//     const result = checkIntersection(x1Center.x, x1Center.y, x2Center.x, x2Center.y, x3Center.x, x3Center.y, x4Center.x, x4Center.y);
-//     if (result.type == "intersecting") {
-//         return true;
-//     } else {
-//         return false;
-//     }
-    
-// }
-
-// function commonNodeBetween2Links(link1: Link,link2: Link): boolean {
-//     if (link1.source==link2.source || link1.source==link2.target || link1.target==link2.source || link1.target==link2.target) {
-//         return true;
-//     }else {
-//         return false;
-//     }
-// } 
-
-// function sameAngleBetween2ConnectedLinks(link1: Link,link2: Link,style:GraphStyleProperties): boolean {
-
-//     // get nodes information
-//     let commonNode: Node;
-//     let node1: Node; // node from link 1 that is not in link 2
-//     let node2: Node; // node from link 2 that is not in link 1
-
-//     // if link 1 source is the common node :
-//     if (link1.source==link2.source || link1.source==link2.target){
-//         commonNode=link1.source;
-//         node1=link1.target;
-//     // if link 1 target is the common node :
-//     }else if (link1.target==link2.source || link1.target==link2.target){
-//         commonNode=link1.target;
-//         node1=link1.source;
-//     }
-//     // get node 2
-//     if (link2.source==commonNode){
-//         node2=link2.target;
-//     }else{
-//         node2=link2.source;
-//     }
-
-//     // adjust coord
-//     const commonNodeCenter=AdjustCoordNodeToCenter(commonNode,style);
-//     const node1Center=AdjustCoordNodeToCenter(node1,style);
-//     const node2Center=AdjustCoordNodeToCenter(node2,style);
-//     // get angle between the 2 edges
-//     const angle1=adjustAngle(Math.atan2(node1Center.y-commonNodeCenter.y,node1Center.x-commonNodeCenter.x));
-//     const angle2=adjustAngle(Math.atan2(node2Center.y-commonNodeCenter.y,node2Center.x-commonNodeCenter.x));    
-    
-//     // same angles ?
-//     return angle1==angle2;
-
-// }
-
-// function adjustAngle(angle: number): number {
-//     return (angle + 2 * Math.PI) % (2 * Math.PI);
-// }
-
-// function AdjustCoordNodeToCenter(node:Node,style:GraphStyleProperties):{x:number,y:number}{
-//     const size = getSizeNodePixel(node,style);
-//     return {x:node.x-size.width/2,y:node.y-size.height/2}
-// }
-
-// /**
-//  * Counts how many crossings are in a network
-//  * @param network the network
-//  * @returns the number of crossings
-//  */
-// export function countIntersection(network: Network,style:GraphStyleProperties): number {
-//     let nb: number = 0;
-//     for (let i=0 ; i<network.links.length ; i++) {
-//         for (let j=i+1 ; j<network.links.length ; j++) {
-//             const link1=network.links[i];
-//             const link2=network.links[j];
-//             if (edgesIntersectionLink(link1, link2,style)){
-//                 nb++;
-//             }
-//         }
-//     }
-//     return nb;
-// }
-
-
-//______________________Intersection in another format of graph______________________
-
-////CLEAN CODE : CHANGE FORMAT TO NETWORK AND USE THE OTHER FUNCTIONS FOR CYCLE
-
-function AdjustCoordNodeToCenter2(node:Node,nodeCoord:{x:number,y:number},style:GraphStyleProperties):{x:number,y:number}{
-    const size = getSizeNodePixel(node,style);
-    return {x:nodeCoord.x-size.width/2,y:nodeCoord.y-size.height/2}
-}
-
-function edgesIntersection(node1Link1:{x:number,y:number},node2Link1:{x:number,y:number},node1Link2:{x:number,y:number},node2Link2:{x:number,y:number}): boolean {
-    // case node in common
-    if (commonNodeBetween2EdgesCoord(node1Link1,node2Link1,node1Link2,node2Link2)) {
-     return false;//intersection2ConnectedLinks(node1Link1,node2Link1,node1Link2,node2Link2);
-    }
-    const result = checkIntersection(node1Link1.x, node1Link1.y, node2Link1.x, node2Link1.y, node1Link2.x, node1Link2.y, node2Link2.x, node2Link2.y);
-    if (result.type == "intersecting") {
-        return true;
-    }else{
-        return false;
-    }
-}
-
-
-function commonNodeBetween2EdgesID(link1: {source:string,target:string},link2: {source:string,target:string}): boolean {
-    if (link1.source==link2.source || link1.source==link2.target || link1.target==link2.source || link1.target==link2.target) {
-        return true;
-    }else {
-        return false;
-    }
-}
-
-function commonNodeBetween2EdgesCoord(node1Link1:{x:number,y:number},node2Link1:{x:number,y:number},node1Link2:{x:number,y:number},node2Link2:{x:number,y:number}): boolean {
-    if (sameNode(node1Link1,node1Link2) || sameNode(node1Link1,node2Link2) || sameNode(node2Link1,node1Link2) || sameNode(node2Link1,node2Link2)) {
-        return true;
-    }else {
-        return false;
-    }
-}
-
-function sameNode(node1: {x:number,y:number},node2: {x:number,y:number}): boolean {
-    if (!node1 || !node2 ||  node1.x==null ||  node1.y==null  || node2.x==null || node2.y==null) {
-        return false;
-    }
-    return node1.x===node2.x && node1.y===node2.y;
-}
-
-// function intersection2ConnectedLinks(node1Link1:{x:number,y:number},node2Link1:{x:number,y:number},node1Link2:{x:number,y:number},node2Link2:{x:number,y:number}): boolean {
-
-//     // get nodes information
-//     let commonNode: {x:number,y:number};
-//     let node1: {x:number,y:number}; // node from link 1 that is not in link 2
-//     let node2: {x:number,y:number}; // node from link 2 that is not in link 1
-
-//     // if link 1 node 1 is the common node :
-//     if (sameNode(node1Link1,node1Link2) || sameNode(node1Link1,node2Link2)){
-//         commonNode=node1Link1;
-//         node1=node2Link1;
-//     // if link 1 node 2 is the common node :
-//     }else if (sameNode(node2Link1,node1Link2) || sameNode(node2Link1,node2Link2)){
-//         commonNode=node2Link1;
-//         node1=node1Link1;
-//     }
-//     // get node 2
-//     if (sameNode(node1Link2,commonNode)){
-//         node2=node2Link2;
-//     }else{
-//         node2=node1Link2;
-//     }
-
-//     // get angle between the 2 edges
-//     const angle1=adjustAngle(Math.atan2(node1.y-commonNode.y,node1.x-commonNode.x));
-//     const angle2=adjustAngle(Math.atan2(node2.y-commonNode.y,node2.x-commonNode.x));
-    
-//     // same angles ?
-//     return angle1==angle2;
-
-// }
-
-export function countIntersectionGraph(nodes: {[key:string]:{x:number,y:number}},links:{source:string,target:string}[],network:Network,style:GraphStyleProperties): number {
-    let nb: number = 0;
-    for (let i=0 ; i<links.length ; i++) {
-        for (let j=i+1 ; j<links.length ; j++) {
-            const link1=links[i];
-            const link2=links[j];
-            // check if intersection
-            let node1Link1=nodes[link1.source];
-            //node1Link1=AdjustCoordNodeToCenter2(network.nodes[link1.source],node1Link1,style);
-            let node2Link1=nodes[link1.target];
-            //node2Link1=AdjustCoordNodeToCenter2(network.nodes[link1.target],node2Link1,style);
-            let node1Link2=nodes[link2.source];
-            //node1Link2=AdjustCoordNodeToCenter2(network.nodes[link2.source],node1Link2,style);
-            let node2Link2=nodes[link2.target];
-            //node2Link2=AdjustCoordNodeToCenter2(network.nodes[link2.target],node2Link2,style);
-            if (edgesIntersection(node1Link1,node2Link1,node1Link2,node2Link2)){
-                nb++;
-            }
-        }
-    }
-    return nb;
-}
-
-export function isIntersectionGraph(nodes: {[key:string]:{x:number,y:number}},links:{source:string,target:string}[],network:Network,style:GraphStyleProperties): boolean {
-    for (let i=0 ; i<links.length ; i++) {
-        for (let j=i+1 ; j<links.length ; j++) {
-            const link1=links[i];
-            const link2=links[j];
-             // check if intersection
-             let node1Link1=nodes[link1.source];
-             //node1Link1=AdjustCoordNodeToCenter2(network.nodes[link1.source],node1Link1,style);
-             let node2Link1=nodes[link1.target];
-             //node2Link1=AdjustCoordNodeToCenter2(network.nodes[link1.target],node2Link1,style);
-             let node1Link2=nodes[link2.source];
-             //node1Link2=AdjustCoordNodeToCenter2(network.nodes[link2.source],node1Link2,style);
-             let node2Link2=nodes[link2.target];
-             //node2Link2=AdjustCoordNodeToCenter2(network.nodes[link2.target],node2Link2,style);
-            if (edgesIntersection(node1Link1,node2Link1,node1Link2,node2Link2)){
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-//______________________Nodes overlap for graph______________________
-
-export function countOverlapNodes(nodesPosition: {[key:string]:Coordinate},network:Network,networkStyle:GraphStyleProperties):number{
-    let nb=0;
-    const nodesID=Object.keys(nodesPosition);
-    for (let i=0 ; i<nodesID.length ; i++) {
-        for (let j=i+1 ; j<nodesID.length ; j++) {
-            // info about node1
-            const node1=network.nodes[nodesID[i]];
-            const posNode1=nodesPosition[nodesID[i]];
-            const sizeNode1=getSizeNodePixel(node1,networkStyle);
-            // info about node2
-            const node2=network.nodes[nodesID[j]];
-            const posNode2=nodesPosition[nodesID[j]];
-            const sizeNode2=getSizeNodePixel(node2,networkStyle);
-
-            if (nodeOverlap(posNode1,sizeNode1,posNode2,sizeNode2)){
-                nb+=1;
-            }
-
-        }
-    }
-    return nb;
-}
-
-export function isOverlapNodes(nodesPosition: {[key:string]:Coordinate},network:Network,networkStyle:GraphStyleProperties):boolean{
-    const nodesID=Object.keys(nodesPosition);
-    for (let i=0 ; i<nodesID.length ; i++) {
-        for (let j=i+1 ; j<nodesID.length ; j++) {
-            // info about node1
-            const node1=network.nodes[nodesID[i]];
-            const posNode1=nodesPosition[nodesID[i]];
-            const sizeNode1=getSizeNodePixel(node1,networkStyle);
-            // info about node2
-            const node2=network.nodes[nodesID[j]];
-            const posNode2=nodesPosition[nodesID[j]];
-            const sizeNode2=getSizeNodePixel(node2,networkStyle);
-
-            if (nodeOverlap(posNode1,sizeNode1,posNode2,sizeNode2)){
-                return true;
-            }
-
-        }
-    }
-    return false;
-}
-
-function nodeOverlap(coord1: Coordinate, size1: Size, coord2: Coordinate, size2: Size): boolean {
-    if ( !coord1 || !size1 || !coord2 || !size2 || size1.width == null || size1.height == null || size2.width == null
-        || size2.height == null || coord1.x == null || coord1.y == null || coord2.x == null || coord2.y == null ||
-        size1.width == undefined || size1.height == undefined || size2.width == undefined || size2.height == undefined ||
-        coord1.x == undefined || coord1.y == undefined || coord2.x == undefined || coord2.y == undefined) {
-        // Handle null or undefined inputs appropriately
-        return false;
-    }
-
-    // rectangle 1
-    const left1 = coord1.x - size1.width / 2;
-    const right1 = coord1.x + size1.width / 2;
-    const top1 = coord1.y - size1.height / 2;
-    const bottom1 = coord1.y + size1.height / 2;
-
-    // rectangle 2
-    const left2 = coord2.x - size2.width / 2;
-    const right2 = coord2.x + size2.width / 2;
-    const top2 = coord2.y - size2.height / 2;
-    const bottom2 = coord2.y + size2.height / 2;
-
-    // overlap?
-    const overlapX = left1 < right2 && right1 > left2;
-    const overlapY = top1 < bottom2 && bottom1 > top2;
-
-    return overlapX && overlapY;
-}
-//______________________Nodes overlap with edges for graph______________________
-
-export function countOverlapNodesEdges(nodesPosition: {[key:string]:{x:number,y:number}},links:{source:string,target:string}[],network:Network,networkStyle:GraphStyleProperties):number{
-    let nb=0;
-    const nodesID=Object.keys(nodesPosition);
-    for (let i=0 ; i<nodesID.length ; i++) {
-        // info about node
-        const node=network.nodes[nodesID[i]];
-        const posNode=nodesPosition[nodesID[i]];
-        const sizeNode=getSizeNodePixel(node,networkStyle);
-
-        for (let j=0 ; j<links.length ; j++) {        
-            // info about link
-            const link=links[j];
-            // if node is linked to the edge : continue
-            if(link.source==nodesID[i] || link.target==nodesID[i]){
-                continue;
-            }else{
-                let posLink1=nodesPosition[link.source];
-                let posLink2=nodesPosition[link.target];
-                //posLink1=AdjustCoordNodeToCenter2(network.nodes[link.source],posLink1,networkStyle);
-                //posLink2=AdjustCoordNodeToCenter2(network.nodes[link.target],posLink2,networkStyle);
-                if (nodeEdgeOverlap(posNode,sizeNode,posLink1,posLink2)){
-                    nb+=1;
-                }
-            }
-
-        }
-    }
-    return nb;
-}
-
-export function isOverlapNodesEdges(nodesPosition: {[key:string]:{x:number,y:number}},links:{source:string,target:string}[],network:Network,networkStyle:GraphStyleProperties):boolean{
-    const nodesID=Object.keys(nodesPosition);
-    for (let i=0 ; i<nodesID.length ; i++) {
-        // info about node
-        const node=network.nodes[nodesID[i]];
-        const posNode=nodesPosition[nodesID[i]];
-        const sizeNode=getSizeNodePixel(node,networkStyle);
-
-        for (let j=0 ; j<links.length ; j++) {        
-            // info about link
-            const link=links[j];
-            // if node is linked to the edge : continue
-            if(link.source==nodesID[i] || link.target==nodesID[i]){
-                continue;
-            }else{
-                let posLink1=nodesPosition[link.source];
-                let posLink2=nodesPosition[link.target];
-                //posLink1=AdjustCoordNodeToCenter2(network.nodes[link.source],posLink1,networkStyle);
-                //posLink2=AdjustCoordNodeToCenter2(network.nodes[link.target],posLink2,networkStyle);
-                if (nodeEdgeOverlap(posNode,sizeNode,posLink1,posLink2)){
-                    return true;
-                }
-            }
-
-        }
-    }
-    return false;
-}
-
-
-
-function nodeEdgeOverlap(centerCoordNode: Coordinate, sizeNode:Size, posLink1: Coordinate, posLink2: Coordinate): boolean {
-    
-    // Treat the node as a rectangle 
-    const rect = {
-        left: centerCoordNode.x - sizeNode.width / 2,
-        right: centerCoordNode.x + sizeNode.width / 2,
-        top: centerCoordNode.y - sizeNode.height / 2,
-        bottom: centerCoordNode.y + sizeNode.height / 2
-    };
-
-    // Check if any of the edge's endpoints is inside the rectangle
-    const isPointInsideRect = (point: { x: number, y: number }) => 
-        point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
-
-    if (isPointInsideRect(posLink1) || isPointInsideRect(posLink2)) {
-        return true; // One of the endpoints is inside the rectangle
-    }
-
-   // Check for overlap between the edge and the sides of the rectangle
-    // Convert the sides of the rectangle into line segments
-    const edges = [
-        { start: { x: rect.left, y: rect.top }, end: { x: rect.right, y: rect.top } }, // Top
-        { start: { x: rect.right, y: rect.top }, end: { x: rect.right, y: rect.bottom } }, // Right
-        { start: { x: rect.left, y: rect.bottom }, end: { x: rect.right, y: rect.bottom } }, // Bottom
-        { start: { x: rect.left, y: rect.top }, end: { x: rect.left, y: rect.bottom } } // Left
-    ];
-
-    // Use checkIntersection function to check if two line segments intersect
-    for (const edge of edges) {
-        const result = checkIntersection(edge.start.x,edge.start.y, edge.end.x,edge.end.y, posLink1.x, posLink1.y,posLink2.x,posLink2.y);
-        if (result.type == "intersecting") {
-            return true; // There is an overlap
-        }
-    }
-
-    return false; // No overlap detected
-}
\ No newline at end of file
diff --git a/src/composables/importNetwork.ts b/src/composables/importNetwork.ts
deleted file mode 100644
index ec7117c21656f862a1216c080419a47bf3c10444..0000000000000000000000000000000000000000
--- a/src/composables/importNetwork.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-// import type { Ref } from "vue";
-// import type { Network } from "@metabohub/viz-core/src/types/Network";
-// import type { GraphStyleProperties } from "@metabohub/viz-core/src//types/GraphStyleProperties";
-
-// //import { readJsonGraph } from "./readJson";
-// import { readJsonGraph } from "@metabohub/viz-core";
-// import { sbml2json } from "./SBMLtoJSON";
-
-
-
-// /**
-//  * Import network at JSONGraph format from an URL.
-//  * @param url URL to get network data
-//  * @param network Reference to network object
-//  * @param networkStyle Reference to networkStyle object
-//  * @param callbackFunction Function to call after network load (opt)
-//  */
-// export function importNetworkFromURL(url: string, network: Ref<Network>, networkStyle: Ref<GraphStyleProperties>, callbackFunction = () => {}): void {
-// 	setTimeout(async function() {
-// 		let data:string = await getContentFromURL(url);
-// 		// Check if the data is XML
-// 		if (data.startsWith('<?xml')) {
-// 			// Convert XML to JSON
-// 			data = JSON.stringify(await sbml2json(data));
-// 		}
-// 		const graphData = readJsonGraph(data);
-// 		networkStyle.value = graphData.networkStyle;
-// 		loadNetwork(graphData.network, network).then(() => {
-//       callbackFunction();
-// 		});
-// 	}, 1);
-// }
-
-
-// /**
-//  * Import network at JSONGraph format from a file.
-//  * @param file File to get network data
-//  * @param network Reference to network object
-//  * @param networkStyle Reference to networkStyle object
-//  * @param callbackFunction Function to call after network load (opt)
-//  */
-
-// export function importNetworkFromFile(file: File, network: Ref<Network>, networkStyle: Ref<GraphStyleProperties>, callbackFunction = () => {}): void {
-// 	const reader = new FileReader();
-// 	reader.onload = async function () {
-// 		let data = reader.result as string;
-// 		if (data.startsWith('<?xml')) {
-// 			// Convert XML to JSON
-// 			data = JSON.stringify(await sbml2json(data));
-// 		}
-// 		const networkData = readJsonGraph(data);
-// 		networkStyle.value = networkData.networkStyle;
-//     loadNetwork(networkData.network, network).then(() => {
-// 			callbackFunction();
-// 		});
-// 	}
-
-// 	reader.readAsText(file);
-// }
-
-
-// /**
-//  * Make async the step where the data are put in network reference. 
-//  * That permit to chain with another function like rescale.
-//  * @param data network data
-//  * @param network Reference to network object
-//  */
-// async function loadNetwork(data: Network, network: Ref<Network>): Promise<void> {
-// 	network.value = data;
-//   }
-
-
-// /**
-//  * Fetch url to return data
-//  * @param url URL to fetch 
-//  * @returns Return response
-//  */
-// export async function getContentFromURL(url: string): Promise<string> {
-// 	try {
-// 	  const response = await fetch(url);
-// 	  if (!response.ok) {
-// 		throw new Error('La requête a échoué avec le statut ' + response.status);
-// 	  }
-// 	  const content = await response.text();
-// 	  return content;
-// 	} catch (error) {
-// 	  console.error('Une erreur s\'est produite lors de la récupération du contenu du fichier :', error);
-// 	  throw error;
-// 	}
-//   }
-  
-  
-
diff --git a/src/index.ts b/src/index.ts
index 525ff2dd7d5f219d1d4c378eaf26a96fea999127..06991d009717644958ff49be84a0b5706835322c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,34 @@
-import {algorithmOnNetwork} from "./composables/LayoutMain";
+// Function import
+import {layoutOnNetwork} from "./composables/LayoutMain";
 
-export {algorithmOnNetwork};
\ No newline at end of file
+// Type import
+import {Network, GraphStyleProperties, Node,Link, NodeStyle} from "./types/TypeVizCore";
+
+// Variable import
+import { defaultParameters } from "./types/Parameters";
+
+// Enum import
+import { PathType,StartNodesType } from "./types/EnumArgs";
+
+
+//************************************************************/
+
+
+// Function export
+ export {layoutOnNetwork};
+
+// // Type export
+export type {
+    Network,
+    GraphStyleProperties,
+    Node,
+    Link,
+    NodeStyle,
+};
+
+// Variable import
+export {defaultParameters};
+
+// Enum export
+export{ PathType, StartNodesType};
+  
diff --git a/src/types/CoordinatesSize.ts b/src/types/CoordinatesSize.ts
index b7f674be5d089dcb36dbab268319b510cfe75202..36e3f9cd98d672cea764ebbded3eb105945769cc 100644
--- a/src/types/CoordinatesSize.ts
+++ b/src/types/CoordinatesSize.ts
@@ -5,6 +5,12 @@ export interface Coordinate {
 }
 
 
+export interface CoordinateNull {
+    x: number | null;
+    y: number | null;
+}
+
+
 export interface Size {
     width: number;
     height: number;
diff --git a/src/types/FormatJSONGraphXML.ts b/src/types/FormatJSONGraphXML.ts
deleted file mode 100644
index 7587a3226493d8ede3743d58bc84fcd5422c6981..0000000000000000000000000000000000000000
--- a/src/types/FormatJSONGraphXML.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-
-export interface JSONGraphFormat {
-    graph: {
-        id: any;
-        type: string;
-        metadata: {
-            style:GraphStyleProperties;
-        };
-        nodes: {
-            [x: string]: {
-                id: string;
-                metadata: {
-                    classes?: string[];
-                    position?: {
-                        x: number;
-                        y: number;
-                    
-                    };
-                };
-                label: string;
-            };
-        };
-        edges: {
-            id: string;
-            source: string;
-            target: string;
-            metadata: {
-                classes?: string[];
-            };
-        }[];
-    };
-}
-
-export interface XMLSpecies {
-    $: { 
-        compartment: string; 
-        id: string; 
-        name: string; 
-        species: string 
-    }
-}
-
-export interface XMLReactions {
-    $: {
-        id: string;
-        name: string;
-        reversible:string;
-    },
-    listOfReactants: {
-        speciesReference: any[]|{} 
-    },
-    listOfProducts: {
-        speciesReference: Partial<XMLSpecies>[]|Partial<XMLSpecies> 
-    }
-}
-
-
diff --git a/src/types/FormatJsonStyle.ts b/src/types/FormatJsonStyle.ts
deleted file mode 100644
index 9d54eb7d7e56d76ff968af38f4f422f94b98a86a..0000000000000000000000000000000000000000
--- a/src/types/FormatJsonStyle.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-
-export interface JsonStyle {
-    biosource?: {[key:string]:string|number},
-    generalStyle?:{[key:string]:string|number|boolean},
-    linkStyle?:{[key:string]:string|number|boolean},
-    metaboliteStyle?:{[key:string]:string|number|boolean},
-    reactionStyle?:{[key:string]:string|number|boolean},
-}
diff --git a/src/types/FormatJsonViz.ts b/src/types/FormatJsonViz.ts
index 4f95bc5d5be94a23f2307f865b3298a4bc524893..779ddc97bc43d79d90613824662d41d367166890 100644
--- a/src/types/FormatJsonViz.ts
+++ b/src/types/FormatJsonViz.ts
@@ -1,8 +1,10 @@
+import { EdgeViz } from "./TypeViz"
 
 /**
  * This file contains the types for the Json object return by the viz API
  */
 
+
 export interface JsonViz {
     directed?: boolean,
     edges:Array<EdgeViz>,
diff --git a/src/types/NetworkLayout.ts b/src/types/NetworkLayout.ts
index ec8bae275e44710670dabaedec18b27ffe325383..f9a6d4930b71a92510ee880c3aca697b37140f3f 100644
--- a/src/types/NetworkLayout.ts
+++ b/src/types/NetworkLayout.ts
@@ -1,6 +1,4 @@
-import { Link } from "@metabohub/viz-core/src/types/Link";
-import { Node } from "@metabohub/viz-core/src/types/Node";
-import type { Network } from "@metabohub/viz-core/src/types/Network";
+import { Link , Node, Network} from "../types/TypeVizCore";
 import { TypeSubgraph } from "./Subgraph";
 
 /**
@@ -26,6 +24,8 @@ export interface NodeLayout extends Node {
      [TypeSubgraph.SECONDARY_CHAIN]?:string[],
      [TypeSubgraph.CYCLE]?:string[],
      [TypeSubgraph.CYCLEGROUP]?:string,
+     fixedInCircle?:string,
+     isFixed?:boolean,
     }
 }
   
diff --git a/src/types/Parameters.ts b/src/types/Parameters.ts
index ee4c1f3c4f853cea8c7407687e7db0ec55cea16f..38eba04ce2006e8119c1d2fe99b55f0f5430e41f 100644
--- a/src/types/Parameters.ts
+++ b/src/types/Parameters.ts
@@ -1,4 +1,4 @@
-import { Network } from "@metabohub/viz-core/src/types/Network";
+import { Network } from "../types/TypeVizCore";
 import { PathType, StartNodesType } from "./EnumArgs";
 import { getPathSourcesToTargetNode } from "../composables/LayoutMainChain";
 
@@ -16,37 +16,34 @@ export interface Parameters {
     doReactionReversible: boolean; // do the step duplication and choice of reaction reversible ?
 
     doMainChain: boolean; // do the step main chain ?
-    getSubgraph : (network: Network, sources: Array<string>,merge?:boolean,pathType?:PathType) => {[key:string]:{nodes:Array<string>, height:number}}; // function to get subgraph (main chain)
+    readonly getSubgraph : (network: Network, sources: Array<string>,merge?:boolean,pathType?:PathType) => Promise<{[key:string]:{nodes:Array<string>, height:number}}>; // function to get subgraph (main chain)
     startNodeTypeMainChain: StartNodesType; // for the main chain step : which are the start nodes?
     pathType: PathType; // main chain step : longest path , all longest paths or all paths
-    merge: boolean; // merging main chain ? If not : nodes can be in several clusters
+    readonly merge: boolean; // merging main chain ? If not : nodes can be in several clusters
     doMiniBranch: boolean; // adding minibranch for main chains ?
-    groupOrCluster: "group" | "cluster"; //main chain as group or cluster in DOT
+    readonly groupOrCluster: "group" | "cluster"; //main chain as group or cluster in DOT
 
     doCycle: boolean; // do the step cycle ?
-    allowInternalCycles: boolean; // allow internal cycles for tangent one ?
+    readonly allowInternalCycles: boolean; // allow internal cycles for tangent one ?
 
-    addNodes: boolean; // adding node at the beginning of the DOT ?
-    ordering: boolean; // reorder edges in DOT ? (for cycle step)
+    readonly addNodes: boolean; // adding node at the beginning of the DOT ?
+    readonly ordering: boolean; // reorder edges in DOT ? (for cycle step)
 
-    dpi: number; // DPI for the image (viz parameter)
+    readonly dpi: number; // DPI for the image (viz parameter)
 
-    numberNodeOnEdge: number; // space between two rank, with size of a node as unit (mean of all size)
-    factorLengthSideCompounds: number; // % of the lenght of minimal edge to use as lenght of side compounds edges
+    readonly numberNodeOnEdge: number; // space between two rank, with size of a node as unit (mean of all size)
+    readonly factorLengthSideCompounds: number; // % of the lenght of minimal edge to use as lenght of side compounds edges
 
     shiftCoord?: boolean; // shift coordinates : center is at the previous coord (because of top left corner)
-
-    //userSources: string[];
-    //onlyUserSources: boolean;
 }
 
-export let defaultParameters: Parameters = {
+export const defaultParameters: Parameters = {
     doDuplicateSideCompounds: true, // user can change this parameter
     doPutAsideSideCompounds: true, // user can change this parameter
 
     doReactionReversible: true, // user can change this parameter
 
-    doMainChain: false, // user can change this parameter
+    doMainChain: true, // user can change this parameter
     getSubgraph : getPathSourcesToTargetNode,
     startNodeTypeMainChain: StartNodesType.RANK_SOURCE, // usefull to allow user to change this parameter with RANK_ONLY or SOURCE_ONLY ? (if source-only, put source_all for reaction rev and no first viz)
     pathType: PathType.ALL_LONGEST, // user can change this parameter
@@ -54,7 +51,7 @@ export let defaultParameters: Parameters = {
     doMiniBranch: true, // usefull choice ? run metrix to see if it's usefull
     groupOrCluster: "cluster",
 
-    doCycle: false, // user can change this parameter
+    doCycle: true, // user can change this parameter
     allowInternalCycles: false,
    
     addNodes: true,
@@ -65,8 +62,5 @@ export let defaultParameters: Parameters = {
     numberNodeOnEdge: 3, // user can change this parameter, but doesn't work for cycle edge length
     factorLengthSideCompounds: 1/2, // user can change this parameter
 
-    shiftCoord: true,
-
-    //userSources: [],
-    //onlyUserSources: false,
+    shiftCoord: false,
 };
\ No newline at end of file
diff --git a/src/types/Reaction.ts b/src/types/Reaction.ts
index cf90a30e4c2440973be11a73e422105364e7bf56..2688d6d174e957d4ecaefad0f71bb5a06915f90e 100644
--- a/src/types/Reaction.ts
+++ b/src/types/Reaction.ts
@@ -8,18 +8,11 @@ export enum MetaboliteType {
     PRODUCT='product',
 }
 
-// export enum MinMedianMax {
-//     MEDIAN='median',
-//     MIN='min',
-//     MAX='max'
-// }
-
 export interface Reaction {
     id:string
     sideCompoundsReactants: Array<string>
     sideCompoundsProducts:Array<string>
     metabolitesAngles:{[key:string]:{angle:number,type:MetaboliteType}}
-    //linkMedianMinMaxLength?:{median:number,min:number,max:number}
     intervalsAvailables?: ReactionInterval[]
     angleSpacingReactant?:number
     angleSpacingProduct?:number
diff --git a/src/types/Subgraph.ts b/src/types/Subgraph.ts
index aa278eb684e58a6f4a81c78d63959884e804bb13..f6538d8a814d0f86a4859e2f21a66964242e25f3 100644
--- a/src/types/Subgraph.ts
+++ b/src/types/Subgraph.ts
@@ -1,4 +1,4 @@
-import { Coordinate } from "./CoordinatesSize";
+import { Coordinate, CoordinateNull } from "./CoordinatesSize";
 import { Ordering } from "./EnumArgs";
 
 /**
@@ -32,10 +32,12 @@ export interface Subgraph {
     position?:Coordinate;
     originalPosition?:Coordinate; // if metanode : the metanode center not well positionned (precalulated position)
 
-    precalculatedNodesPosition?: {[key: string]: Coordinate}; // if metanode : the position of the nodes in the metanode
-    
-    metadata?: {[key: string]: string | number| boolean | {[key: string]: string | number} | Array<string>};
-}
+    precalculatedNodesPosition?: {[key: string]: CoordinateNull}; // if metanode : the position of the nodes in the metanode. Null indicates a need of placement by force layout
+
+    radiusCycle?:number; // if cycle : the radius of the circle  
+    centroidCycle?:Coordinate; // if cycle  : the center of the circle 
+
+    }
 
 
 
diff --git a/src/types/SubgraphNetwork.ts b/src/types/SubgraphNetwork.ts
index 59b848641a8a4dc54ef8665c842a0efddadd1098..9da2c08757ef7e8ac12645bce23e1a6427e766f1 100644
--- a/src/types/SubgraphNetwork.ts
+++ b/src/types/SubgraphNetwork.ts
@@ -1,9 +1,7 @@
-import { Network } from "@metabohub/viz-core/src/types/Network";
-import { Node } from "@metabohub/viz-core/src/types/Node";
 import { Subgraph, TypeSubgraph } from "./Subgraph";
-import { Ref } from "vue";
-import { GraphStyleProperties } from "@metabohub/viz-core/src/types/GraphStyleProperties";
-import { NetworkLayout } from "./NetworkLayout";
+import { GraphStyleProperties } from "../types/TypeVizCore";
+import { NetworkLayout, NodeLayout } from "./NetworkLayout";
+import { AttributesViz } from "./TypeViz";
 
 /**
  * This file contains the types for the SubgraphNetwork object : a network with nodes and links information, stats information (minimal length of edge for example), attributs for viz, subgraphs (main chains, secondary chains, cycles and cycle groups) and side compounds.
@@ -32,6 +30,6 @@ export interface SubgraphNetwork {
 	}
 
 	sideCompounds?:{
-		[key:string]:{reactants:Array<Node>,products:Array<Node>}
+		[key:string]:{reactants:Array<NodeLayout>,products:Array<NodeLayout>}
 	}
 }
\ No newline at end of file
diff --git a/src/types/TypeViz.ts b/src/types/TypeViz.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c9532e4eda3b85afd4a90f62a6e2311850ecd1f
--- /dev/null
+++ b/src/types/TypeViz.ts
@@ -0,0 +1,25 @@
+
+export interface AttributesViz {
+    [name: string]: string | number | boolean 
+  }
+  
+export interface NodeViz {
+name: string
+attributes?: AttributesViz
+}
+
+export interface EdgeViz {
+tail: string
+head: string
+attributes?: AttributesViz
+}
+
+export interface SubgraphViz {
+name?: string
+graphAttributes?: AttributesViz
+nodeAttributes?: AttributesViz
+edgeAttributes?: AttributesViz
+nodes?: NodeViz[]
+edges?: EdgeViz[] 
+subgraphs?: SubgraphViz[]
+}
diff --git a/src/types/TypeVizCore.ts b/src/types/TypeVizCore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b90dfe38c47f41289b7f38246ec2402d3d6a0741
--- /dev/null
+++ b/src/types/TypeVizCore.ts
@@ -0,0 +1,76 @@
+
+
+/*******************************************************************************************************************************************************/
+//_____________________________________________________________________ Style __________________________________________________________________________
+
+
+export interface GraphStyleProperties {
+	nodeStyles?: { [key: string]: NodeStyle };
+	linkStyles?: { [key: string]: LinkStyle };
+	curveLine?: boolean;
+	directed?: boolean;
+}
+
+export interface NodeStyle {
+    height?: number;
+    width?: number;
+    fill?: string;
+    strokeWidth?: number;
+    stroke?: string;
+    displayLabel?: boolean;
+    label?: string;
+    rx?: number;
+    ry?: number;
+    shape?: string;
+    opacity?: number;
+  }
+
+  
+export interface LinkStyle {
+    display ?: boolean;
+    stroke?: string;
+    strokeWidth?: number;
+    opacity?: number;
+  }
+  
+
+
+
+  /*******************************************************************************************************************************************************/
+//_____________________________________________________________________ Network __________________________________________________________________________
+
+
+export interface Network {
+	id: String;
+	label?: String;
+	nodes: {
+		[key: string]: Node
+	};
+	links: Array<Link>;
+	type?: String;
+	rescale?: Boolean;
+}
+
+export interface Node {
+    id: string;
+    label?: string;
+    x: number;
+    y: number;
+    classes?: Array<string>;
+    hidden?: boolean;
+    selected?: boolean;
+    metadata?: {[key: string]: string | number | {[key: string]: string | number} | Array<string> | boolean};
+  }
+  
+export interface Link {
+    id: string;
+    classes?: Array<string>;
+    label?: string;
+    type?: string;
+    source: Node;
+    target: Node;
+    relation?: string;
+    directed?: boolean;
+    metadata?: {[key: string]: string | number | {[key: string]: string | number} | Array<string | number>};
+  }
+
diff --git a/src/types/VizType.ts b/src/types/VizType.ts
deleted file mode 100644
index 7f13d302fbb7c053b6ed8482868648a116d0a0ad..0000000000000000000000000000000000000000
--- a/src/types/VizType.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-// Graph already accessible (see the format below, but implemented in library), but the other interface can't be exported so there are duplicated here
-// export interface Graph {
-//     name?: string
-//     strict?: boolean
-//     directed?: boolean
-//     graphAttributes?: Attributes
-//     nodeAttributes?: Attributes
-//     edgeAttributes?: Attributes
-//     nodes?: Node[]
-//     edges?: Edge[]
-//     subgraphs?: Subgraph[]
-//   }
-
-interface AttributesViz {
-    [name: string]: string | number | boolean 
-  }
-  
-interface NodeViz {
-name: string
-attributes?: AttributesViz
-}
-
-interface EdgeViz {
-tail: string
-head: string
-attributes?: AttributesViz
-}
-
-interface SubgraphViz {
-name?: string
-graphAttributes?: AttributesViz
-nodeAttributes?: AttributesViz
-edgeAttributes?: AttributesViz
-nodes?: NodeViz[]
-edges?: EdgeViz[] 
-subgraphs?: SubgraphViz[]
-}
diff --git a/tsconfig.json b/tsconfig.json
index 2a5c28aba6568896ac34d60a78eac3dc29e3ce09..c9a3040976ae2f868094fb17f746420aae3e0f37 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,6 @@
     /* Type Checking */
     "strict": true,
     "skipLibCheck": true,
-    "outDir": "dist"
+    "outDir": "dist",
   }
 }