From 2a2531e4145832ae08ea2116c7b4db995f5e5f65 Mon Sep 17 00:00:00 2001 From: "hela.ben-khalfallah" Date: Fri, 13 Sep 2024 21:06:49 +0200 Subject: [PATCH] [EVOL]: Graph-Level Metrics --- README.md | 28 + package.json | 18 +- pnpm-lock.yaml | 798 +++++++++++++++++- src/index.js | 33 +- .../duplication/CodeDuplicationAuditor.js | 16 +- .../modularity/CodeModularityAuditor.js | 223 +++++ src/kernel/modularity/CodeModularityUtils.js | 156 ++++ 7 files changed, 1255 insertions(+), 17 deletions(-) create mode 100644 src/kernel/modularity/CodeModularityAuditor.js create mode 100644 src/kernel/modularity/CodeModularityUtils.js diff --git a/README.md b/README.md index 91a965a..c453a17 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,29 @@ For more details on preventing XSS vulnerabilities, you can refer to the followi - [DOM-based XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html) - [HTML Sanitization](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#html-sanitization) +8. **Graph Metrics and Their Significance for Software Quality** +In addition to the Halstead metrics and Maintainability Index, **CodeHealthMeter** also leverages various graph metrics to analyze the structure and dependencies within your codebase. +These metrics provide valuable insights into the overall software quality and potential areas for improvement. + +a. **Graph-Level Metrics** + +* **Density:** Measures the interconnectedness of the modules within your project. A higher density indicates a more complex system with more dependencies between modules. While a certain level of density is expected in larger projects, excessively high density might suggest tight coupling and potential challenges in maintainability and evolution. `projectDensity` provides this metric for your project. +* **Modularity:** Quantifies the presence of distinct communities or clusters of modules within the system. Higher modularity suggests a better separation of concerns and potential for independent development and maintenance of modules. `projectLouvainDetails` provides details about the communities detected using the Louvain algorithm, including the modularity score. + +b. **Node-Level Metrics** + +* **Degree Centrality:** Measures the number of direct dependencies a module has. High degree centrality can indicate a module with many responsibilities or one that's heavily relied upon. `projectDegreeCentrality` provides this information for each module in your project. +* **In-Degree Centrality and Out-Degree Centrality:** Provide a more nuanced view by distinguishing between incoming (dependencies on the module) and outgoing (dependencies of the module) connections. `projectInDegreeCentrality` and `projectOutDegreeCentrality` offer these insights. + +c. **How These Metrics Help** + +By analyzing these graph metrics in conjunction with other code quality indicators, you can: + +* **Identify potential bottlenecks or critical modules:** These might require special attention in terms of testing, maintenance, and refactoring. High `degreeCentrality` or central positions within communities can indicate such modules. +* **Assess the overall complexity and interconnectedness** of your project using the `projectDensity` metric +* **Make informed decisions about refactoring or architectural changes** to enhance maintainability, testability, and scalability. +* **Gain a deeper understanding of the complex relationships and interactions** between different parts of your codebase. + --- This quantitative and mathematical approach provides a more precise and objective assessment of software quality compared to more subjective methods: @@ -160,6 +183,11 @@ While these methods can provide valuable insights, they lack the objectivity and ``` npm i -D code-health-meter ``` +In macos you should install `graphiz`: + + ``` + brew install graphviz || port install graphviz + ``` 2. Run the analysis on your project: ``` diff --git a/package.json b/package.json index 3f26831..7db6c6e 100644 --- a/package.json +++ b/package.json @@ -38,16 +38,30 @@ }, "dependencies": { "@typhonjs/babel-parser": "=0.2.0", + "canvas": "=2.11.2", + "dependency-cruiser": "=16.4.1", "fs-extra": "=8.1.0", "globals": "=15.3.0", "globby": "=10.0.1", + "graphology": "=0.25.4", + "graphology-canvas": "=0.4.2", + "graphology-communities-louvain": "=2.0.1", + "graphology-components": "=1.5.4", + "graphology-metrics": "=2.3.1", + "graphology-svg": "=0.1.3", "jscpd": "=4.0.4", - "jsinspect": "^0.12.7", + "jsinspect": "=0.12.7", "lodash": "=4.17.21", "madge": "=7.0.0", + "ngraph.coarsen": "=1.5.0", + "ngraph.cw": "=2.0.0", + "ngraph.graph": "=20.0.1", + "ngraph.louvain": "=2.0.0", + "ngraph.path": "=1.5.0", "typhonjs-escomplex": "=0.1.0", "unixify": "=1.0.0", "winston": "=3.13.0", - "winston-daily-rotate-file": "=5.0.0" + "winston-daily-rotate-file": "=5.0.0", + "xml2js": "=0.6.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea0dc1d..e60c0b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@typhonjs/babel-parser': specifier: '=0.2.0' version: 0.2.0 + canvas: + specifier: '=2.11.2' + version: 2.11.2 + dependency-cruiser: + specifier: '=16.4.1' + version: 16.4.1 fs-extra: specifier: '=8.1.0' version: 8.1.0 @@ -20,11 +26,29 @@ importers: globby: specifier: '=10.0.1' version: 10.0.1 + graphology: + specifier: '=0.25.4' + version: 0.25.4(graphology-types@0.24.7) + graphology-canvas: + specifier: '=0.4.2' + version: 0.4.2(canvas@2.11.2)(graphology-types@0.24.7) + graphology-communities-louvain: + specifier: '=2.0.1' + version: 2.0.1(graphology-types@0.24.7) + graphology-components: + specifier: '=1.5.4' + version: 1.5.4(graphology-types@0.24.7) + graphology-metrics: + specifier: '=2.3.1' + version: 2.3.1(graphology-types@0.24.7) + graphology-svg: + specifier: '=0.1.3' + version: 0.1.3(graphology-types@0.24.7) jscpd: specifier: '=4.0.4' version: 4.0.4 jsinspect: - specifier: ^0.12.7 + specifier: '=0.12.7' version: 0.12.7 lodash: specifier: '=4.17.21' @@ -32,6 +56,21 @@ importers: madge: specifier: '=7.0.0' version: 7.0.0(typescript@5.4.5) + ngraph.coarsen: + specifier: '=1.5.0' + version: 1.5.0 + ngraph.cw: + specifier: '=2.0.0' + version: 2.0.0 + ngraph.graph: + specifier: '=20.0.1' + version: 20.0.1 + ngraph.louvain: + specifier: '=2.0.0' + version: 2.0.0 + ngraph.path: + specifier: '=1.5.0' + version: 1.5.0 typhonjs-escomplex: specifier: '=0.1.0' version: 0.1.0 @@ -44,6 +83,9 @@ importers: winston-daily-rotate-file: specifier: '=5.0.0' version: 5.0.0(winston@3.13.0) + xml2js: + specifier: '=0.6.2' + version: 0.6.2 devDependencies: '@babel/core': specifier: '=7.18.6' @@ -725,6 +767,10 @@ packages: '@jscpd/tokenizer@4.0.1': resolution: {integrity: sha512-l/CPeEigadYcQUsUxf1wdCBfNjyAxYcQU04KciFNmSZAMY+ykJ8fZsiuyfjb+oOuDgsIPZZ9YvbvsCr6NBXueg==} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} @@ -775,11 +821,28 @@ packages: '@typhonjs/babel-parser@0.2.0': resolution: {integrity: sha512-YKqLZaQAVtOjMiqcJIqex1ezduMefBitoQZjsOqr4US+Yq+cOY/obyloOJ7Ee+XDPaaraVrxWkA3VZjOohtVjQ==} + '@yomguithereal/helpers@1.1.1': + resolution: {integrity: sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + acorn-jsx-walk@2.0.0: + resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-loose@8.4.0: + resolution: {integrity: sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} @@ -790,9 +853,21 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -811,6 +886,14 @@ packages: app-module-path@2.2.0: resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -903,6 +986,10 @@ packages: caniuse-lite@1.0.30001621: resolution: {integrity: sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==} + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -914,6 +1001,10 @@ packages: character-parser@2.2.0: resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -946,6 +1037,10 @@ packages: color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} @@ -960,6 +1055,10 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -977,6 +1076,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} @@ -1003,6 +1105,10 @@ packages: supports-color: optional: true + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -1017,11 +1123,23 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + dependency-cruiser@16.4.1: + resolution: {integrity: sha512-tmByrPhRMdjg/FsE3eT24QSXKhrZpo9APpDCg5Wgg0ReAhZughE3iKseD++UtSYsLQIjD1aYNSeNiAvqbcup9Q==} + engines: {node: ^18.17||>=20} + hasBin: true + dependency-tree@10.0.9: resolution: {integrity: sha512-dwc59FRIsht+HfnTVM0BCjJaEWxdq2YAvEDy4/Hn6CwS3CBWMtFnL3aZGAkQn3XCYxk/YcTDE4jX2Q7bFTwCjA==} engines: {node: '>=14'} hasBin: true + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + detective-amd@5.0.2: resolution: {integrity: sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==} engines: {node: '>=14'} @@ -1078,6 +1196,10 @@ packages: resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -1172,6 +1294,10 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + execa@4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} @@ -1189,6 +1315,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -1240,12 +1369,21 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1285,6 +1423,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1319,6 +1461,61 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphology-canvas@0.4.2: + resolution: {integrity: sha512-7YJCw/8mMXvFiLP1ujEgpFOM5lmj5/AQG+9H1S/ErFvAQ1eyoLsTK/OzyLJjXzhHsBEl0fF4rN//TDsvuvkfPg==} + peerDependencies: + canvas: '*' + graphology-types: '>=0.20.0' + peerDependenciesMeta: + canvas: + optional: true + + graphology-communities-louvain@2.0.1: + resolution: {integrity: sha512-JxEH8uxao6FcWp3UXNDJIRjU3pamzp9aqIWgpfAqWE66aPwHeBIB39YnqTgbe4baUJRdpbcp1u8jJiYvojHGIQ==} + peerDependencies: + graphology-types: '>=0.19.0' + + graphology-components@1.5.4: + resolution: {integrity: sha512-O37vC226wgnN0C6FUWHNe4fbTzaF51CcQjwX3naId/QTzH/PkUtXaanCShj9ws5Vju+z4u3zvSeEZE84Bo9jlA==} + peerDependencies: + graphology-types: '>=0.19.0' + + graphology-indices@0.17.0: + resolution: {integrity: sha512-A7RXuKQvdqSWOpn7ZVQo4S33O0vCfPBnUSf7FwE0zNCasqwZVUaCXePuWo5HBpWw68KJcwObZDHpFk6HKH6MYQ==} + peerDependencies: + graphology-types: '>=0.20.0' + + graphology-layout@0.6.0: + resolution: {integrity: sha512-QZIminJVOqOFHBOf6qEjcMp0m+BNP4/XqY1jrnJhH+fmUHFtNDdTPSXYhVa8Hr3AN5bOPP1Zte5oY1ZzOyzhvA==} + peerDependencies: + graphology-types: '>=0.19.0' + + graphology-metrics@2.3.1: + resolution: {integrity: sha512-131GRSKUR8DrGkLZSYKM3cwxEg+jqXvv1yLh/KgO0My7BOiuo80r0Qrsnv2N3ZjcOlh8namUS4sSk+cCVnTgKA==} + peerDependencies: + graphology-types: '>=0.20.0' + + graphology-shortest-path@2.1.0: + resolution: {integrity: sha512-KbT9CTkP/u72vGEJzyRr24xFC7usI9Es3LMmCPHGwQ1KTsoZjxwA9lMKxfU0syvT/w+7fZUdB/Hu2wWYcJBm6Q==} + peerDependencies: + graphology-types: '>=0.20.0' + + graphology-svg@0.1.3: + resolution: {integrity: sha512-bvsLnL9pIElvWgr/nPYKsqLWE1VPqG6wTFolmS9DxkAr9256hqzlb1ATfDGn52iyljJb+HTu9z6XMHrfRJ4Ggg==} + + graphology-types@0.24.7: + resolution: {integrity: sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==} + + graphology-utils@2.5.2: + resolution: {integrity: sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==} + peerDependencies: + graphology-types: '>=0.23.0' + + graphology@0.25.4: + resolution: {integrity: sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==} + peerDependencies: + graphology-types: '>=0.24.0' + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1342,10 +1539,17 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -1357,6 +1561,10 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -1375,6 +1583,14 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -1396,6 +1612,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -1412,6 +1632,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -1480,6 +1704,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1500,6 +1727,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} @@ -1541,9 +1772,17 @@ packages: typescript: optional: true + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + markdown-table@2.0.0: resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + memoize@10.0.0: + resolution: {integrity: sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1559,12 +1798,40 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mnemonist@0.39.8: + resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} + module-definition@5.0.1: resolution: {integrity: sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==} engines: {node: '>=14'} @@ -1584,6 +1851,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1592,6 +1862,39 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + ngraph.coarsen@1.5.0: + resolution: {integrity: sha512-ciGS6p02fbhvMeDLH4rKTzHe19NoPNahXGN49K33gVo6U0kAH6rr4ej1q+GN5OfVO0Uc6p0lX3bkvNoY5SMChw==} + + ngraph.cw@2.0.0: + resolution: {integrity: sha512-e5brWGahgBDsMX8GG7Zj6UcoTFFQICWdhttckOlH5shVux2J9AdUOEdCuXeZqRGZ0Goz5dI3R13uotZIr3wWBA==} + + ngraph.events@1.2.2: + resolution: {integrity: sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ==} + + ngraph.graph@20.0.1: + resolution: {integrity: sha512-VFsQ+EMkT+7lcJO1QP8Ik3w64WbHJl27Q53EO9hiFU9CRyxJ8HfcXtfWz/U8okuoYKDctbciL6pX3vG5dt1rYA==} + + ngraph.louvain@2.0.0: + resolution: {integrity: sha512-UQVjxD68NnEOKhXiCYmlfuVBlfG9M0a+rQpZXwbYQ5J/Gf5Hq4YfsKr/q21RyMhAgb3pLC/9evHPVfrZjYmcNw==} + + ngraph.path@1.5.0: + resolution: {integrity: sha512-2IdmqfBYq2zbGHtpmskdWF6x/nIWZkhfs1taMgg2waBJRn4xNqe7gBiRtD1YS5ZcKhp0trK+Gw94Rli2emMs1Q==} + + ngraph.random@0.0.1: + resolution: {integrity: sha512-QPKU7ChXF/VrvMQxVo9aWcvXCXp98VfL4nKUteTW/olDqeUqQ61t7m+jvFb8Dj7kKvlKlnsbDA1aWLJGmm17XA==} + + ngraph.subgraph@1.2.0: + resolution: {integrity: sha512-kbTisP2EKxNTA7YkthxbHk23Ucufi6301Ie2+CFpjUx5s5xhvblIEEQEqx3d0P4jobBSR1nLgp9/QlaQwU2//g==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} @@ -1603,6 +1906,11 @@ packages: resolution: {integrity: sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==} engines: {node: '>=14'} + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + normalize-path@2.1.1: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} @@ -1611,6 +1919,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1619,6 +1931,9 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1645,6 +1960,12 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + pandemonium@1.5.0: + resolution: {integrity: sha512-9PU9fy93rJhZHLMjX+4M1RwZPEYl6g7DdWKGmGNhkgBZR5+tOBVExNZc00kzdEGMxbaAvWdQy9MqGAScGwYlcA==} + + pandemonium@2.4.1: + resolution: {integrity: sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1679,6 +2000,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -1709,6 +2034,10 @@ packages: promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + pug-attrs@3.0.0: resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} @@ -1766,6 +2095,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} @@ -1782,6 +2115,10 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -1800,6 +2137,10 @@ packages: reprism@0.0.11: resolution: {integrity: sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + requirejs-config-file@4.0.0: resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} engines: {node: '>=10.13.0'} @@ -1829,12 +2170,20 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex@2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + safe-stable-stringify@2.4.3: resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} engines: {node: '>=10'} @@ -1844,6 +2193,9 @@ packages: engines: {node: '>=14'} hasBin: true + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1853,6 +2205,14 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -1868,9 +2228,18 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1958,6 +2327,13 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + teamcity-service-messages@0.1.14: + resolution: {integrity: sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==} + text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -1975,6 +2351,9 @@ packages: token-stream@1.0.0: resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -1983,6 +2362,10 @@ packages: resolution: {integrity: sha512-5YhbFoHmjxa7pgQLkB07MtGnGJ/yhvjmc9uhsnDBEICME6gkPf83SBwLDQqGDoCa3XzUMWLk1AU2Wn1u1naDtA==} engines: {node: '>=14.16'} + tsconfig-paths-webpack-plugin@4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -2077,14 +2460,28 @@ packages: resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} engines: {node: '>=6.0.0'} + watskeburt@4.1.0: + resolution: {integrity: sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==} + engines: {node: ^18||>=20} + hasBin: true + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + winston-daily-rotate-file@5.0.0: resolution: {integrity: sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==} engines: {node: '>=8'} @@ -2110,9 +2507,20 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2950,6 +3358,21 @@ snapshots: reprism: 0.0.11 spark-md5: 3.0.2 + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': dependencies: eslint-scope: 5.1.1 @@ -3007,14 +3430,40 @@ snapshots: '@babel/parser': 7.24.6 babel-runtime: 6.26.0 + '@yomguithereal/helpers@1.1.1': {} + + abbrev@1.1.1: {} + + acorn-jsx-walk@2.0.0: {} + acorn-jsx@5.3.2(acorn@8.11.3): dependencies: acorn: 8.11.3 + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn-loose@8.4.0: + dependencies: + acorn: 8.12.1 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 + acorn@7.4.1: {} acorn@8.11.3: {} + acorn@8.12.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3022,6 +3471,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-styles@3.2.1: @@ -3036,6 +3492,13 @@ snapshots: app-module-path@2.2.0: {} + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + argparse@2.0.1: {} array-union@2.1.0: {} @@ -3137,6 +3600,15 @@ snapshots: caniuse-lite@1.0.30001621: {} + canvas@2.11.2: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + nan: 2.20.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -3152,6 +3624,8 @@ snapshots: dependencies: is-regex: 1.1.4 + chownr@2.0.0: {} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -3183,6 +3657,8 @@ snapshots: color-name: 1.1.4 simple-swizzle: 0.2.2 + color-support@1.1.3: {} + color@3.2.1: dependencies: color-convert: 1.9.3 @@ -3197,6 +3673,8 @@ snapshots: commander@10.0.1: {} + commander@12.1.0: {} + commander@2.20.3: {} commander@5.1.0: {} @@ -3207,6 +3685,8 @@ snapshots: concat-map@0.0.1: {} + console-control-strings@1.1.0: {} + constantinople@4.0.1: dependencies: '@babel/parser': 7.24.6 @@ -3230,6 +3710,10 @@ snapshots: dependencies: ms: 2.1.2 + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -3244,6 +3728,33 @@ snapshots: es-errors: 1.3.0 gopd: 1.0.1 + delegates@1.0.0: {} + + dependency-cruiser@16.4.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + acorn-jsx-walk: 2.0.0 + acorn-loose: 8.4.0 + acorn-walk: 8.3.4 + ajv: 8.17.1 + commander: 12.1.0 + enhanced-resolve: 5.17.1 + ignore: 5.3.2 + interpret: 3.1.1 + is-installed-globally: 1.0.0 + json5: 2.2.3 + memoize: 10.0.0 + picocolors: 1.0.1 + picomatch: 4.0.2 + prompts: 2.4.2 + rechoir: 0.8.0 + safe-regex: 2.1.1 + semver: 7.6.3 + teamcity-service-messages: 0.1.14 + tsconfig-paths-webpack-plugin: 4.1.0 + watskeburt: 4.1.0 + dependency-tree@10.0.9: dependencies: commander: 10.0.1 @@ -3253,6 +3764,8 @@ snapshots: transitivePeerDependencies: - supports-color + detect-libc@2.0.3: {} + detective-amd@5.0.2: dependencies: ast-module-types: 5.0.0 @@ -3317,6 +3830,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.1 + enhanced-resolve@5.17.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 @@ -3433,6 +3951,8 @@ snapshots: eventemitter3@5.0.1: {} + events@3.3.0: {} + execa@4.1.0: dependencies: cross-spawn: 7.0.3 @@ -3459,6 +3979,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.0.1: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -3526,10 +4048,26 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + fs.realpath@1.0.0: {} function-bind@1.1.2: {} + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + gensync@1.0.0-beta.2: {} get-amd-module-type@5.0.1: @@ -3572,6 +4110,10 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + globals@11.12.0: {} globals@14.0.0: {} @@ -3610,6 +4152,74 @@ snapshots: graphemer@1.4.0: {} + graphology-canvas@0.4.2(canvas@2.11.2)(graphology-types@0.24.7): + dependencies: + graphology-layout: 0.6.0(graphology-types@0.24.7) + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + optionalDependencies: + canvas: 2.11.2 + + graphology-communities-louvain@2.0.1(graphology-types@0.24.7): + dependencies: + graphology-indices: 0.17.0(graphology-types@0.24.7) + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + mnemonist: 0.39.8 + pandemonium: 2.4.1 + + graphology-components@1.5.4(graphology-types@0.24.7): + dependencies: + graphology-indices: 0.17.0(graphology-types@0.24.7) + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + + graphology-indices@0.17.0(graphology-types@0.24.7): + dependencies: + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + mnemonist: 0.39.8 + + graphology-layout@0.6.0(graphology-types@0.24.7): + dependencies: + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + pandemonium: 1.5.0 + + graphology-metrics@2.3.1(graphology-types@0.24.7): + dependencies: + graphology-indices: 0.17.0(graphology-types@0.24.7) + graphology-shortest-path: 2.1.0(graphology-types@0.24.7) + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + mnemonist: 0.39.8 + + graphology-shortest-path@2.1.0(graphology-types@0.24.7): + dependencies: + '@yomguithereal/helpers': 1.1.1 + graphology-indices: 0.17.0(graphology-types@0.24.7) + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + mnemonist: 0.39.8 + + graphology-svg@0.1.3(graphology-types@0.24.7): + dependencies: + graphology-utils: 2.5.2(graphology-types@0.24.7) + transitivePeerDependencies: + - graphology-types + + graphology-types@0.24.7: {} + + graphology-utils@2.5.2(graphology-types@0.24.7): + dependencies: + graphology-types: 0.24.7 + + graphology@0.25.4(graphology-types@0.24.7): + dependencies: + events: 3.3.0 + graphology-types: 0.24.7 + obliterator: 2.0.4 + has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -3626,16 +4236,27 @@ snapshots: dependencies: has-symbols: 1.0.3 + has-unicode@2.0.1: {} + hasown@2.0.2: dependencies: function-bind: 1.1.2 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + human-signals@1.1.1: {} ieee754@1.2.1: {} ignore@5.3.1: {} + ignore@5.3.2: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -3652,6 +4273,10 @@ snapshots: ini@1.3.8: {} + ini@4.1.1: {} + + interpret@3.1.1: {} + is-arrayish@0.3.2: {} is-core-module@2.13.1: @@ -3671,6 +4296,11 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + is-interactive@1.0.0: {} is-number@7.0.0: {} @@ -3679,6 +4309,8 @@ snapshots: is-path-inside@3.0.3: {} + is-path-inside@4.0.0: {} + is-promise@2.2.2: {} is-regex@1.1.4: @@ -3744,6 +4376,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} @@ -3767,6 +4401,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kleur@3.0.3: {} + kuler@2.0.0: {} levn@0.4.1: @@ -3822,10 +4458,18 @@ snapshots: transitivePeerDependencies: - supports-color + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + markdown-table@2.0.0: dependencies: repeat-string: 1.6.1 + memoize@10.0.0: + dependencies: + mimic-function: 5.0.1 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3837,12 +4481,33 @@ snapshots: mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + + mimic-response@2.1.0: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 minimist@1.2.8: {} + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + mnemonist@0.39.8: + dependencies: + obliterator: 2.0.4 + module-definition@5.0.1: dependencies: ast-module-types: 5.0.0 @@ -3861,10 +4526,42 @@ snapshots: ms@2.1.3: {} + nan@2.20.0: {} + nanoid@3.3.7: {} natural-compare@1.4.0: {} + ngraph.coarsen@1.5.0: + dependencies: + ngraph.subgraph: 1.2.0 + + ngraph.cw@2.0.0: + dependencies: + ngraph.random: 0.0.1 + + ngraph.events@1.2.2: {} + + ngraph.graph@20.0.1: + dependencies: + ngraph.events: 1.2.2 + + ngraph.louvain@2.0.0: + dependencies: + ngraph.random: 0.0.1 + + ngraph.path@1.5.0: {} + + ngraph.random@0.0.1: {} + + ngraph.subgraph@1.2.0: + dependencies: + ngraph.graph: 20.0.1 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-releases@2.0.14: {} node-sarif-builder@2.0.3: @@ -3876,6 +4573,10 @@ snapshots: dependencies: '@babel/parser': 7.24.6 + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + normalize-path@2.1.1: dependencies: remove-trailing-separator: 1.1.0 @@ -3884,10 +4585,19 @@ snapshots: dependencies: path-key: 3.1.1 + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + object-assign@4.1.1: {} object-hash@3.0.0: {} + obliterator@2.0.4: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -3929,6 +4639,12 @@ snapshots: dependencies: p-limit: 3.1.0 + pandemonium@1.5.0: {} + + pandemonium@2.4.1: + dependencies: + mnemonist: 0.39.8 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -3949,6 +4665,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pluralize@8.0.0: {} postcss-values-parser@6.0.2(postcss@8.4.38): @@ -3991,6 +4709,11 @@ snapshots: dependencies: asap: 2.0.6 + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + pug-attrs@3.0.0: dependencies: constantinople: 4.0.1 @@ -4082,6 +4805,10 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + rechoir@0.8.0: + dependencies: + resolve: 1.22.8 + regenerate-unicode-properties@10.1.1: dependencies: regenerate: 1.4.2 @@ -4096,6 +4823,8 @@ snapshots: dependencies: '@babel/runtime': 7.24.6 + regexp-tree@0.1.27: {} + regexpu-core@5.3.2: dependencies: '@babel/regjsgen': 0.8.0 @@ -4115,6 +4844,8 @@ snapshots: reprism@0.0.11: {} + require-from-string@2.0.2: {} + requirejs-config-file@4.0.0: dependencies: esprima: 4.0.1 @@ -4139,22 +4870,36 @@ snapshots: reusify@1.0.4: {} + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 safe-buffer@5.2.1: {} + safe-regex@2.1.1: + dependencies: + regexp-tree: 0.1.27 + safe-stable-stringify@2.4.3: {} sass-lookup@5.0.1: dependencies: commander: 10.0.1 + sax@1.4.1: {} + semver@6.3.1: {} semver@7.6.2: {} + semver@7.6.3: {} + + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4172,10 +4917,20 @@ snapshots: signal-exit@3.0.7: {} + simple-concat@1.0.1: {} + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 + sisteransi@1.0.5: {} + slash@3.0.0: {} source-map-js@1.2.0: {} @@ -4243,6 +4998,17 @@ snapshots: tapable@2.2.1: {} + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + teamcity-service-messages@0.1.14: {} + text-hex@1.0.0: {} text-table@0.2.0: {} @@ -4255,10 +5021,18 @@ snapshots: token-stream@1.0.0: {} + tr46@0.0.3: {} + triple-beam@1.4.1: {} ts-graphviz@1.8.2: {} + tsconfig-paths-webpack-plugin@4.1.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.17.1 + tsconfig-paths: 4.2.0 + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -4351,14 +5125,27 @@ snapshots: walkdir@0.4.1: {} + watskeburt@4.1.0: {} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which@2.0.2: dependencies: isexe: 2.0.0 + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + winston-daily-rotate-file@5.0.0(winston@3.13.0): dependencies: file-stream-rotator: 0.6.1 @@ -4398,6 +5185,15 @@ snapshots: wrappy@1.0.2: {} + xml2js@0.6.2: + dependencies: + sax: 1.4.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + yallist@3.1.1: {} + yallist@4.0.0: {} + yocto-queue@0.1.0: {} diff --git a/src/index.js b/src/index.js index deaef1a..0076812 100755 --- a/src/index.js +++ b/src/index.js @@ -8,9 +8,11 @@ import CodeComplexityAuditor from './kernel/complexity/CodeComplexityAuditor.js' import CodeCouplingAuditor from './kernel/coupling/CodeCouplingAuditor.js'; import CodeDuplicationAuditor from './kernel/duplication/CodeDuplicationAuditor.js'; import CodeSecurityAuditor from './kernel/security/CodeSecurityAuditor.js'; +import CodeModularityAuditor from './kernel/modularity/CodeModularityAuditor.js'; import CodeComplexityUtils from './kernel/complexity/CodeComplexityUtils.js'; import CodeCouplingUtils from './kernel/coupling/CodeCouplingUtils.js'; import CodeSecurityUtils from './kernel/security/CodeSecurityUtils.js'; +import CodeModularityUtils from './kernel/modularity/CodeModularityUtils.js'; /** * Parses command line arguments. @@ -43,7 +45,7 @@ const { /** * Checks if the source directory and output directory are provided. */ -if(!srcDir || !outputDir){ +if (!srcDir || !outputDir) { AppLogger.info('srcDir is require and must be a string (npm run code-health-meter --srcDir "../../my-path" --outputDir "../../my-output-path" --format "json or html")'); process.exit(-1); } @@ -53,10 +55,10 @@ AppLogger.info('***** Code audit start *****'); /** * Cleaning workspace */ -if(fs.existsSync(outputDir)) { - try{ - execSync(`rm -rf ${outputDir}`); - } catch(error){ +if (fs.existsSync(outputDir)) { + try { + execSync(`rm -rf ${outputDir}`, { stdio: 'ignore' }); + } catch (error) { AppLogger.info(`Code auditor cleaning workspace error: ${error.message}`); process.exit(-1); } @@ -111,7 +113,7 @@ CodeCouplingUtils * Starts the code duplication audit. * @type {Object} */ -CodeDuplicationAuditor.startAudit( +await CodeDuplicationAuditor.startAudit( srcDir, `${outputDir}/code-duplication-audit`, format @@ -135,4 +137,23 @@ CodeSecurityUtils codeSecurityAnalysisResult, }); +/** + * Starts the code modularity audit. + * https://github.com/pahen/madge?tab=readme-ov-file#configuration + * @type {Object} + */ +const codeModularityAnalysisResult = await CodeModularityAuditor.startAudit(srcDir); + +/** + * Writes the audit result to files. + */ +CodeModularityUtils + .writeCodeModularityAuditToFile({ + codeModularityOptions: { + outputDir: `${outputDir}/code-modularity-audit`, + fileFormat: format, // html or json + }, + codeModularityAnalysisResult, + }); + AppLogger.info('***** Code audit finished successfully *****'); diff --git a/src/kernel/duplication/CodeDuplicationAuditor.js b/src/kernel/duplication/CodeDuplicationAuditor.js index dd4da72..c9ed204 100644 --- a/src/kernel/duplication/CodeDuplicationAuditor.js +++ b/src/kernel/duplication/CodeDuplicationAuditor.js @@ -60,22 +60,22 @@ const defaultOptions = { const startAudit = async (directory, outputDir, fileFormat) => { try { - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] directory: ${directory}`); - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] outputDir: ${outputDir}`); - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] fileFormat: ${fileFormat}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] directory: ${directory}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] outputDir: ${outputDir}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] fileFormat: ${fileFormat}`); // add jscpd if not installed - execSync('npm i -g jscpd@4.0.4'); + execSync('npm i -g jscpd@4.0.4', { stdio: 'ignore' }); // execute audit const codeDuplicationCommand = `jscpd --silent --mode "${defaultOptions.mode}" --threshold ${defaultOptions.threshold} --reporters "${fileFormat}" --output "${outputDir}" --format "${defaultOptions.format}" --ignore "${defaultOptions.ignore.join(',')}" ${directory}`; - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] jscpd script: ${codeDuplicationCommand}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] jscpd script: ${codeDuplicationCommand}`); // generate report try{ - execSync(codeDuplicationCommand); + execSync(codeDuplicationCommand, { stdio: 'ignore' }); }catch (error){ - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] execSync error: ${error.message}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] execSync error: ${error.message}`); } // modify generated html @@ -92,7 +92,7 @@ const startAudit = async (directory, outputDir, fileFormat) => { return true; } catch (error) { - AppLogger.info(`[CodeDuplicationAuditor - inspectDirectory] error: ${error.message}`); + AppLogger.info(`[CodeDuplicationAuditor - startAudit] error: ${error.message}`); return false; } }; diff --git a/src/kernel/modularity/CodeModularityAuditor.js b/src/kernel/modularity/CodeModularityAuditor.js new file mode 100644 index 0000000..8c638a4 --- /dev/null +++ b/src/kernel/modularity/CodeModularityAuditor.js @@ -0,0 +1,223 @@ +/** + * Module for performing code modularity audits using Madge and Graphology. + * @module CodeModularityAuditor + */ + +import AppLogger from '../../commons/AppLogger.js'; +import Madge from 'madge'; +import xml2js from 'xml2js'; +import Graph from 'graphology'; +import louvain from 'graphology-communities-louvain'; +import {density} from 'graphology-metrics/graph/density.js'; +import { + degreeCentrality, + inDegreeCentrality, + outDegreeCentrality +} from 'graphology-metrics/centrality/degree.js'; + +/** + * Default options for Madge analysis. + * @const {Object} defaultOptions + * @property {string[]} fileExtensions - Array of file extensions to include in the analysis. + * @property {RegExp[]} excludeRegExp - Array of regular expressions to exclude files or directories from the analysis. + */ +const defaultOptions = { + fileExtensions: ['ts', 'tsx', 'js', 'jsx'], + excludeRegExp: [ + '.*node_modules/.*', + '.*target/.*', + '.*dist/.*', + '.*__mocks__/.*', + '.*husky/.*', + '.*vscode/.*', + '.*idea/.*', + '.*gitlab/.*', + '.*github/.*', + '.*eslint.*', + '.*jest.*', + '.*test.*', + '.*babel.*', + '.*webpack.*', + '.*.config.*', + '.*.types.*', + '.*.svg', + '.*.d.ts.*', + ], +}; + +/** + * Options for creating the Graphology graph. + * @const {Object} GRAPH_OPTIONS + * @property {'directed'} type - Specifies a directed graph. + * @property {true} allowSelfLoops - Allows self-loops in the graph. + * @property {false} multi - Disallows multiple edges between the same pair of nodes. + */ +const GRAPH_OPTIONS = { + type: 'directed', + allowSelfLoops: true, + multi: false +}; + +/** + * Options for the Louvain community detection algorithm. + * @const {Object} LOUVAIN_ALGO_OPTIONS + * @property {number} resolution - Resolution parameter for the Louvain algorithm (default: 1). + */ +const LOUVAIN_ALGO_OPTIONS = { + resolution: 0.98, +}; + +/** + * XML to JavaScript parser instance. + * @const {xml2js.Parser} xml2jsParser + */ +const xml2jsParser = new xml2js.Parser({}); + +/** + * Retrieves project tree data (title, x, y coordinates) from an SVG buffer. + * @async + * @function retrieveProjectTreeData + * @param {Buffer} svgBuffer - The SVG content as a buffer. + * @returns {Promise | null>} - An array of objects containing title, x, and y coordinates or null on error. + */ +const retrieveProjectTreeData = async (svgBuffer) => { + try { + const svgContent = svgBuffer.toString(); + const result = await xml2jsParser.parseStringPromise(svgContent); + const svg = result.svg; + + const svgData = []; + const nodes = svg.g[0].g; + + for (const node of nodes) { + const title = node.title?.[0]; + if(!title?.length) { + continue; + } + + const textNode = node.text?.[0]?.$; + if(!textNode) { + continue; + } + + let x = 0; + let y = 0; + + if (textNode) { + x = parseFloat(textNode.x); + y = parseFloat(textNode.y); + } + + svgData.push({ title, x, y }); + } + + return svgData; + } catch (error) { + AppLogger.error('Error parsing SVG content:', error); + return null; + } +}; + +/** + * Normalizes the project tree structure. + * @function normalizeProjectTree + * @param {Object} tree - The project tree object. + * @returns {Object} - An object containing `nodes` (array of node names) and `edges` (array of [source, target] pairs). + */ +const normalizeProjectTree = (tree) => { + if(Object.keys(tree).length === 0) { + return { + nodes: [], + edges: [] + }; + } + + return Object.keys(tree).reduce((acc, node) => { + return ({ + ...acc, + nodes: [ + ...(acc.nodes || []), + node + ], + edges: [ + ...(acc.edges || []), + ...(tree[node]?.map((child) => [node, child]) || []) + ] + }); + }, {}); +}; + +/** + * Starts the code modularity audit process. + * @async + * @function startAudit + * @param {string} directory - The directory to analyze. + * @returns {Promise} - An object containing `projectTree`, `projectGraph`, and `projectLouvainDetails` or an empty object on error. + */ +const startAudit = async (directory) => { + try { + const projectAnalysisResult = await Madge(directory, defaultOptions); + if(!projectAnalysisResult) { + return ({}); + } + + const projectTree = projectAnalysisResult.obj(); + if(!projectTree + || !Object.keys(projectTree)?.length) { + return ({}); + } + + const projectTreeVisualization = await projectAnalysisResult.svg(); + if(!projectTreeVisualization) { + return ({}); + } + + const { nodes, edges } = normalizeProjectTree(projectTree) || {}; + const projectTreeData = await retrieveProjectTreeData(projectTreeVisualization); + + const projectGraph = new Graph(GRAPH_OPTIONS); + + nodes + .filter(item => item) + .reverse() + .forEach((node, index) => { + const nodeData = projectTreeData.find((item) => item.title === node); + if(nodeData) { + projectGraph.addNode(nodeData.title, {x: nodeData.x, y: nodeData.y}); + } + }); + + edges + .filter(item => item) + .reverse() + .forEach(([source, target]) => { + projectGraph.addEdge(source, target); + }); + + const louvainDetails = louvain.detailed(projectGraph, LOUVAIN_ALGO_OPTIONS); + + return({ + projectTree, + projectGraph, + projectLouvainDetails: louvainDetails, + projectDensity: density(projectGraph), + projectDegreeCentrality: degreeCentrality(projectGraph), + projectInDegreeCentrality: inDegreeCentrality(projectGraph), + projectOutDegreeCentrality: outDegreeCentrality(projectGraph), + }); + } catch (error) { + AppLogger.info(`[CodeModularityAuditor - startAudit] error: ${error.message}`); + return ({}); + } +}; + +/** + * The CodeModularityAuditor object containing the `startAudit` function. + * @const {Object} CodeModularityAuditor + * @property {function} startAudit - The function to initiate the code modularity audit. + */ +const CodeModularityAuditor = { + startAudit, +}; + +export default CodeModularityAuditor; \ No newline at end of file diff --git a/src/kernel/modularity/CodeModularityUtils.js b/src/kernel/modularity/CodeModularityUtils.js new file mode 100644 index 0000000..26bc5a1 --- /dev/null +++ b/src/kernel/modularity/CodeModularityUtils.js @@ -0,0 +1,156 @@ +/** + * Module providing utilities for code modularity audits. + * @module CodeModularityUtils + */ + +import AppLogger from '../../commons/AppLogger.js'; +import path from 'path'; +import fs from 'fs-extra'; + +/** + * Formats the code modularity audit reports based on the specified file format. + * @function formatCodeModularityAuditReports + * @param {Object} options - Options for formatting. + * @param {string} options.fileFormat - The desired output file format ('json' or 'html'). + * @param {Object} options.projectTree - The project tree data. + * @param {Graph} options.projectGraph - The Graphology graph representing the project. + * @param {Object} options.projectLouvainDetails - The detailed results of the Louvain community detection. + * @returns {string} - The formatted report content. + */ +const formatCodeModularityAuditReports = ({ + fileFormat, + projectTree, + projectGraph, + projectLouvainDetails, + projectDensity, + projectDegreeCentrality, + projectInDegreeCentrality, + projectOutDegreeCentrality, +}) => { + if(fileFormat === 'json'){ + return JSON.stringify({ + projectTree, + projectGraph, + projectLouvainDetails, + projectDensity, + projectDegreeCentrality, + projectInDegreeCentrality, + projectOutDegreeCentrality, + }, null, 2); + } + + if(fileFormat === 'html'){ + return ''; // Placeholder for future HTML formatting implementation + } + + return ''; // Default to empty string if format is not recognized +}; + +/** + * Writes the code modularity audit results to a file. + * @function writeCodeModularityAuditToFile + * @param {Object} options - Options for writing the audit results. + * @param {Object} options.codeModularityOptions - Code modularity audit options. + * @param {string} options.codeModularityOptions.outputDir - The output directory for the report. + * @param {string} options.codeModularityOptions.fileFormat - The desired output file format. + * @param {Object} options.codeModularityAnalysisResult - The results of the code modularity analysis. + * @param {Object} options.codeModularityAnalysisResult.projectTree - The project tree data. + * @param {Graph} options.codeModularityAnalysisResult.projectGraph - The Graphology graph. + * @param {Object} options.codeModularityAnalysisResult.projectLouvainDetails - Louvain details. + * @returns {boolean} - `true` if the write operation was successful, `false` otherwise. + */ +const writeCodeModularityAuditToFile = ({ + codeModularityOptions, + codeModularityAnalysisResult, +}) => { + try { + const { + outputDir, + fileFormat, + } = codeModularityOptions || {}; + + AppLogger.info(`[CodeModularityUtils - writeCodeModularityAuditToFile] outputDir: ${outputDir}`); + AppLogger.info(`[CodeModularityUtils - writeCodeModularityAuditToFile] fileFormat: ${fileFormat}`); + + if(!outputDir?.length){ + return false; + } + + if(!codeModularityAnalysisResult){ + return false; + } + + const { + projectTree, + projectGraph, + projectLouvainDetails, + projectDensity, + projectDegreeCentrality, + projectInDegreeCentrality, + projectOutDegreeCentrality, + } = codeModularityAnalysisResult; + + AppLogger.info('[CodeModularityUtils - startAudit] projectTree:', projectTree); + AppLogger.info('[CodeModularityUtils - startAudit] projectGraph:', projectGraph); + AppLogger.info('[CodeModularityUtils - startAudit] projectLouvainDetails:', projectLouvainDetails); + AppLogger.info('[CodeModularityUtils - startAudit] projectDensity:', projectDensity); + AppLogger.info('[CodeModularityUtils - startAudit] projectDegreeCentrality:', projectDegreeCentrality); + AppLogger.info('[CodeModularityUtils - startAudit] projectInDegreeCentrality:', projectInDegreeCentrality); + AppLogger.info('[CodeModularityUtils - startAudit] projectOutDegreeCentrality:', projectOutDegreeCentrality); + + if(!projectTree){ + return false; + } + + const codeModularityAuditOutputFileName = `CodeModularityReport.${fileFormat || 'json'}`; + AppLogger.info(`[CodeModularityUtils - writeCodeComplexityAuditToFile] codeModularityAuditOutputFileName: ${codeModularityAuditOutputFileName}`); + + const codeModularityAuditOutputFile = path.join(outputDir, codeModularityAuditOutputFileName); + AppLogger.info(`[CodeModularityUtils - writeCodeComplexityAuditToFile] codeModularityAuditOutputFile: ${codeModularityAuditOutputFile}`); + + const svgOutputFileName = 'CodeModularityReport.svg'; + AppLogger.info(`[CodeModularityUtils - writeCodeComplexityAuditToFile] svgOutputFileName: ${svgOutputFileName}`); + + const svgOutputFile = path.join(outputDir, svgOutputFileName); + AppLogger.info(`[CodeModularityUtils - writeCodeComplexityAuditToFile] svgOutputFile: ${svgOutputFile}`); + + if(fs.existsSync(codeModularityAuditOutputFile)){ + fs.rmSync(codeModularityAuditOutputFile); + } else { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const formattedModularityAuditReports = formatCodeModularityAuditReports({ + fileFormat, + projectTree, + projectGraph, + projectLouvainDetails, + projectDensity, + projectDegreeCentrality, + projectInDegreeCentrality, + projectOutDegreeCentrality, + }); + + if(!formattedModularityAuditReports?.length){ + return false; + } + + fs.writeFileSync(codeModularityAuditOutputFile, formattedModularityAuditReports); + + return true; + } catch (error) { + AppLogger.info(`[CodeModularityUtils - writeCodeModularityAuditToFile] error: ${error.message}`); + return false; + } +}; + +/** + * The CodeModularityUtils object containing utility functions for code modularity audits. + * @const {Object} CodeModularityUtils + * @property {function} writeCodeModularityAuditToFile - Writes the audit results to a file. + */ +const CodeModularityUtils = { + writeCodeModularityAuditToFile +}; + +export default CodeModularityUtils; \ No newline at end of file