From 7665e458edfe6c9f6e8f70fcac0c1cf794688359 Mon Sep 17 00:00:00 2001 From: Murat Sari Date: Thu, 21 Aug 2025 16:20:28 +0200 Subject: [PATCH] feat: trend analysis --- .detective/config.json | 32 +- .detective/hash | 2 +- .detective/log | 5789 ------------ .vscode/launch.json | 15 + apps/backend/package.json | 8 +- apps/backend/project.json | 8 +- apps/backend/src/express.ts | 274 + apps/backend/src/infrastructure/deps.ts | 9 +- apps/backend/src/infrastructure/git.ts | 19 +- apps/backend/src/infrastructure/paths.ts | 1 + apps/backend/src/main.ts | 100 +- apps/backend/src/mcp/README.md | 578 ++ apps/backend/src/mcp/http-router.ts | 60 + apps/backend/src/mcp/server.ts | 394 + apps/backend/src/options/options.ts | 2 + apps/backend/src/options/parse-options.ts | 2 + apps/backend/src/services/team-alignment.ts | 26 +- .../services/trend-analysis/api-formatter.ts | 32 + .../services/trend-analysis/cache-manager.ts | 88 + .../services/trend-analysis/data-formatter.ts | 118 + .../services/trend-analysis/git-service.ts | 155 + .../src/services/trend-analysis/index.ts | 13 + .../trend-analysis/metrics-calculator.ts | 90 + .../trend-analysis/progress-reporter.ts | 48 + .../trend-analysis/trend-analysis.types.ts | 126 + .../services/trend-analysis/trend-analyzer.ts | 568 ++ .../x-ray/base-metrics-analyzer.ts | 35 + .../x-ray/class-metrics-analyzer.ts | 151 + .../trend-analysis/x-ray/code-analyzer.ts | 76 + .../x-ray/data-structure-metrics-analyzer.ts | 65 + .../x-ray/method-metrics-analyzer.ts | 179 + .../metrics/class-complexity.metric.spec.ts | 90 + .../x-ray/metrics/class-complexity.metric.ts | 195 + .../metrics/class-dependencies.metric.spec.ts | 99 + .../metrics/class-dependencies.metric.ts | 142 + .../array-mixed-meanings.metric.spec.ts | 46 + .../array-mixed-meanings.metric.ts | 62 + .../complex-data-passing.metric.spec.ts | 63 + .../complex-data-passing.metric.ts | 86 + .../magic-numbers.metric.spec.ts | 39 + .../data-structure/magic-numbers.metric.ts | 41 + .../data-structure/null-checks.metric.spec.ts | 42 + .../data-structure/null-checks.metric.ts | 46 + .../public-fields.metric.spec.ts | 53 + .../data-structure/public-fields.metric.ts | 100 + .../x-ray/metrics/dependency.metric.spec.ts | 58 + .../x-ray/metrics/dependency.metric.ts | 117 + .../metrics/duplicate-code-analyzer.spec.ts | 66 + .../x-ray/metrics/duplicate-code-analyzer.ts | 199 + .../method-responsibility-metric.spec.ts | 57 + .../metrics/method-responsibility-metric.ts | 362 + .../x-ray/metrics/metric.base.ts | 27 + .../x-ray/metrics/metric.decorator.ts | 26 + .../x-ray/metrics/metric.interface.ts | 20 + .../metrics/nested-conditions-metric.spec.ts | 71 + .../x-ray/metrics/nested-conditions-metric.ts | 65 + .../complex-algorithms.metric.spec.ts | 63 + .../organization/complex-algorithms.metric.ts | 56 + .../organization/feature-envy.metric.spec.ts | 55 + .../organization/feature-envy.metric.ts | 79 + .../organization/middle-man.metric.spec.ts | 78 + .../metrics/organization/middle-man.metric.ts | 80 + .../temporal-coupling.metric.spec.ts | 66 + .../organization/temporal-coupling.metric.ts | 154 + .../metrics/reasons-to-change.metric.spec.ts | 91 + .../x-ray/metrics/reasons-to-change.metric.ts | 431 + .../x-ray/metrics/test-utils.ts | 91 + .../typescript/any-types.metric.spec.ts | 39 + .../metrics/typescript/any-types.metric.ts | 37 + .../typescript/complex-unions.metric.spec.ts | 39 + .../typescript/complex-unions.metric.ts | 36 + .../missing-type-guards.metric.spec.ts | 43 + .../typescript/missing-type-guards.metric.ts | 133 + .../typescript/type-assertions.metric.spec.ts | 40 + .../typescript/type-assertions.metric.ts | 37 + .../type-duplication.metric.spec.ts | 42 + .../typescript/type-duplication.metric.ts | 85 + .../weak-type-definitions.metric.spec.ts | 39 + .../weak-type-definitions.metric.ts | 44 + .../metrics/unused-members.metric.spec.ts | 122 + .../x-ray/metrics/unused-members.metric.ts | 159 + .../x-ray/organization-metrics-analyzer.ts | 59 + .../x-ray/typescript-metrics-analyzer.ts | 66 + .../trend-analysis/x-ray/ui-schema.types.ts | 85 + .../x-ray/x-ray-metrics.types.ts | 80 + .../trend-analysis/x-ray/x-ray.schema.ts | 164 + apps/backend/src/utils/complexity.ts | 31 +- apps/frontend/project.json | 5 +- apps/frontend/src/app/app.routes.ts | 10 + .../src/app/data/trend-analysis.store.ts | 266 + .../chord-coupling-renderer.component.html | 1 + .../chord-coupling-renderer.component.ts | 255 + .../features/coupling/coupling.component.html | 23 +- .../features/coupling/coupling.component.ts | 48 +- .../features/hotspot-city/city3d.component.ts | 462 + .../app/features/hotspot-city/city3d.types.ts | 38 + .../hotspot-city/hotspot-city.component.css | 87 + .../hotspot-city/hotspot-city.component.html | 74 + .../hotspot-city/hotspot-city.component.ts | 262 + .../hotspot-city/hotspot-city.store.ts | 176 + .../app/features/hotspot/hotspot-adapter.ts | 6 +- .../bus-factor-report.component.css | 131 + .../bus-factor-report.component.html | 85 + .../bus-factor-report.component.ts | 166 + .../team-alignment/bus-factor-report/index.ts | 1 + .../define-teams/define-teams.component.css | 140 +- .../define-teams/define-teams.component.html | 53 +- .../define-teams/define-teams.component.ts | 13 +- .../team-alignment.component.html | 16 +- .../team-alignment.component.ts | 30 +- .../trend-analysis/file-tree-node.model.ts | 36 + .../trend-analysis/file-tree.store.ts | 285 + .../hotspots/hotspots-dialog.component.ts | 181 + .../file-tree-node.component.ts | 101 + .../folder-file-item.component.ts | 77 + .../folder-tree-node.component.ts | 92 + .../recent-commits-table.component.ts | 149 + .../trend-chart.component.ts | 338 + .../trend-metrics-card.component.ts | 119 + .../trend-analysis.component.css | 256 + .../trend-analysis.component.html | 277 + .../trend-analysis.component.ts | 138 + .../features/trend-analysis/utils/index.ts | 20 + .../trend-analysis/utils/metrics-utils.ts | 78 + .../trend-analysis/utils/tree-accessors.ts | 19 + .../trend-analysis/utils/tree-utils.ts | 154 + .../trend-analysis/utils/trend-utils.ts | 98 + .../x-ray/x-ray-dialog.component.ts | 100 + .../trend-analysis/x-ray/x-ray.component.css | 151 + .../trend-analysis/x-ray/x-ray.component.html | 68 + .../trend-analysis/x-ray/x-ray.component.ts | 38 + .../trend-analysis/x-ray/x-ray.store.ts | 40 + .../trend-analysis/x-ray/x-ray.view-model.ts | 186 + .../src/app/model/trend-analysis-result.ts | 68 + .../src/app/shell/nav/nav.component.css | 48 - .../src/app/shell/nav/nav.component.html | 25 +- .../src/app/shell/nav/nav.component.ts | 11 +- apps/frontend/src/app/ui/file-icon.pipe.ts | 30 + apps/frontend/src/app/ui/file-name.pipe.ts | 11 + package-lock.json | 8116 ++++++++++++----- package.json | 15 +- 141 files changed, 19380 insertions(+), 8152 deletions(-) delete mode 100644 .detective/log create mode 100644 .vscode/launch.json create mode 100644 apps/backend/src/mcp/README.md create mode 100644 apps/backend/src/mcp/http-router.ts create mode 100644 apps/backend/src/mcp/server.ts create mode 100644 apps/backend/src/services/trend-analysis/api-formatter.ts create mode 100644 apps/backend/src/services/trend-analysis/cache-manager.ts create mode 100644 apps/backend/src/services/trend-analysis/data-formatter.ts create mode 100644 apps/backend/src/services/trend-analysis/git-service.ts create mode 100644 apps/backend/src/services/trend-analysis/index.ts create mode 100644 apps/backend/src/services/trend-analysis/metrics-calculator.ts create mode 100644 apps/backend/src/services/trend-analysis/progress-reporter.ts create mode 100644 apps/backend/src/services/trend-analysis/trend-analysis.types.ts create mode 100644 apps/backend/src/services/trend-analysis/trend-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/base-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/class-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/code-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/data-structure-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/method-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/metric.base.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/metric.decorator.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/metric.interface.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/test-utils.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.spec.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/organization-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/typescript-metrics-analyzer.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/ui-schema.types.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/x-ray-metrics.types.ts create mode 100644 apps/backend/src/services/trend-analysis/x-ray/x-ray.schema.ts create mode 100644 apps/frontend/src/app/data/trend-analysis.store.ts create mode 100644 apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.html create mode 100644 apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.ts create mode 100644 apps/frontend/src/app/features/hotspot-city/city3d.component.ts create mode 100644 apps/frontend/src/app/features/hotspot-city/city3d.types.ts create mode 100644 apps/frontend/src/app/features/hotspot-city/hotspot-city.component.css create mode 100644 apps/frontend/src/app/features/hotspot-city/hotspot-city.component.html create mode 100644 apps/frontend/src/app/features/hotspot-city/hotspot-city.component.ts create mode 100644 apps/frontend/src/app/features/hotspot-city/hotspot-city.store.ts create mode 100644 apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.css create mode 100644 apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.html create mode 100644 apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.ts create mode 100644 apps/frontend/src/app/features/team-alignment/bus-factor-report/index.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/file-tree-node.model.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/file-tree.store.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/hotspots/hotspots-dialog.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/file-tree-node.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-file-item.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-tree-node.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/recent-commits-table.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-chart.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-metrics-card.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis.component.css create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis.component.html create mode 100644 apps/frontend/src/app/features/trend-analysis/trend-analysis.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/utils/index.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/utils/metrics-utils.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/utils/tree-accessors.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/utils/tree-utils.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/utils/trend-utils.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray-dialog.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.css create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.html create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.store.ts create mode 100644 apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.view-model.ts create mode 100644 apps/frontend/src/app/model/trend-analysis-result.ts delete mode 100644 apps/frontend/src/app/shell/nav/nav.component.css create mode 100644 apps/frontend/src/app/ui/file-icon.pipe.ts create mode 100644 apps/frontend/src/app/ui/file-name.pipe.ts diff --git a/.detective/config.json b/.detective/config.json index 4c94d8e..8d24b17 100644 --- a/.detective/config.json +++ b/.detective/config.json @@ -1,40 +1,20 @@ { "scopes": [ + "apps/frontend", + "apps/backend/src/infrastructure", + "apps/backend/src/mcp", "apps/backend/src/model", "apps/backend/src/options", "apps/backend/src/services", - "apps/backend/src/utils", - "apps/backend/src/infrastructure", - "apps/frontend/src/app/features/coupling", - "apps/frontend/src/app/features/hotspot", - "apps/frontend/src/app/features/team-alignment", - "apps/frontend/src/app/shell/about", - "apps/frontend/src/app/shell/filter-tree", - "apps/frontend/src/app/shell/nav", - "apps/frontend/src/app/model", - "apps/frontend/src/app/ui/doughnut", - "apps/frontend/src/app/ui/graph", - "apps/frontend/src/app/ui/limits", - "apps/frontend/src/app/ui/loading", - "apps/frontend/src/app/ui/resizer", - "apps/frontend/src/app/ui/treemap" - ], - "groups": [ - "apps/backend/src", - "apps/backend", - "apps/frontend/src/app/features", - "apps/frontend/src/app/shell", - "apps/frontend/src/app/ui", - "apps/frontend/src/app", - "apps/frontend/src", - "apps/frontend", - "apps" + "apps/backend/src/utils" ], + "groups": ["apps/backend/src", "apps/backend", "apps"], "entries": [], "filter": { "files": [], "logs": [] }, + "aliases": {}, "teams": { "example-team-a": ["John Doe", "Jane Doe"], "example-team-b": ["Max Muster", "Susi Sorglos"] diff --git a/.detective/hash b/.detective/hash index 10770bb..2e203b5 100644 --- a/.detective/hash +++ b/.detective/hash @@ -1 +1 @@ -503c63bcf7fab325fc9687a40ad048b2b16ca636, v1.1.6 \ No newline at end of file +0a7d0219e81ae606e07a3ab3fedd5499f9db7f49, v1.3.0 \ No newline at end of file diff --git a/.detective/log b/.detective/log deleted file mode 100644 index 17f768c..0000000 --- a/.detective/log +++ /dev/null @@ -1,5789 +0,0 @@ -"Manfred Steyer ,Tue Oct 8 17:44:47 2024 +0200 6ed670acc998f83279713bc9c2a16ef7a79f66a1,feat: filter hotspots using slider and percent" -1 1 .detective/hash -33 3 .detective/log -70 11 apps/backend/src/services/hotspot.ts -8 10 apps/frontend/src/app/features/hotspot/hotspot.component.html -2 0 apps/frontend/src/app/features/hotspot/hotspot.component.ts -1 1 apps/frontend/src/app/features/hotspot/hotspot.store.ts - -"Manfred Steyer ,Sun Sep 29 12:41:39 2024 +0200 4ee102065cca1a50063a871b078ec87680116efb,chore(release): publish 1.1.6" -10 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 29 12:41:19 2024 +0200 0130a5533f0979c406e469c74305d8e214b4e0ee,fix: prevent word wrap in tree" -5 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css - -"Manfred Steyer ,Sun Sep 29 12:29:48 2024 +0200 0f519048ce8d0d999ea0a9fc7a1c8953513eccbf,chore(release): publish 1.1.5" -10 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 29 12:29:29 2024 +0200 527ed589150ac81ee5bc251526644cfdc6eeb11e,fix: don't wrap tree entries" -4 0 apps/frontend/src/app/shell/nav/nav.component.css - -"Manfred Steyer ,Sun Sep 29 12:12:29 2024 +0200 7a18a2d6fb97ea4d0d2bea59bd99b6b376862772,chore(release): publish 1.1.4" -10 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 29 12:12:15 2024 +0200 be5836b768b8fc6d307496ace2d037cc7063c56d,fix: always show resize cursor during resizing" -2 0 apps/frontend/src/app/ui/resizer/resizer.component.ts -4 0 apps/frontend/src/styles.scss - -"Manfred Steyer ,Sun Sep 29 00:19:03 2024 +0200 567e19278e6204fd4b29ca9490043fe1fc8d7431,chore(release): publish 1.1.3" -14 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sat Sep 28 23:22:37 2024 +0200 7ef551cfee65bf6a037923eb6310ddf72a2a1e52,feat: add resizer for tree" -1 1 .detective/hash -16 0 .detective/log -16 2 apps/frontend/src/app/shell/nav/nav.component.css -10 6 apps/frontend/src/app/shell/nav/nav.component.html -15 8 apps/frontend/src/app/shell/nav/nav.component.ts -10 0 apps/frontend/src/app/ui/resizer/resizer.component.css -1 0 apps/frontend/src/app/ui/resizer/resizer.component.html -22 0 apps/frontend/src/app/ui/resizer/resizer.component.spec.ts -42 0 apps/frontend/src/app/ui/resizer/resizer.component.ts - -"Manfred Steyer ,Sat Sep 28 19:32:42 2024 +0200 06c8dfeb5e988f17bcf3699e8d3c1748981c7231,refactor: streamline dataflow for hotspot analysis" -1 1 .detective/hash -12 0 .detective/log -2 6 apps/frontend/src/app/features/hotspot/hotspot.component.html -14 16 apps/frontend/src/app/features/hotspot/hotspot.component.ts -18 5 apps/frontend/src/app/features/hotspot/hotspot.store.ts -1 1 apps/frontend/src/app/ui/graph/graph.ts -14 1 apps/frontend/src/app/utils/{effects.ts => signal-helpers.ts} - -"Manfred Steyer ,Sat Sep 28 18:41:26 2024 +0200 fdc6882e65fb7a88afd40fb0850399808c6ed1d6,feat: add signal store to hotspot and team alignment analysis" -1 1 .detective/hash -3 2 .detective/log -1 1 .husky/pre-commit -7 3 apps/frontend/src/app/features/hotspot/hotspot.component.html -64 142 apps/frontend/src/app/features/hotspot/hotspot.component.ts -123 0 apps/frontend/src/app/features/hotspot/hotspot.store.ts -6 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -25 46 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -56 0 apps/frontend/src/app/features/team-alignment/team-alignment.store.ts -1 1 apps/frontend/src/app/ui/graph/graph.ts - -"Manfred Steyer ,Sat Sep 28 10:48:17 2024 +0200 854bde8ff89203de89c8a8f667162cfb93f6a2c4,chore: configure vscode to use single quotes" -1 1 .detective/hash -11 0 .detective/log -4 0 .vscode/settings.json - -"Manfred Steyer ,Sat Sep 28 10:44:18 2024 +0200 28b7c31784b0353ad33a72f2e5cd6c8e7bcf6947,feat: add store to coupling feature" -1 1 .detective/hash -6 0 .detective/log -11 2 apps/frontend/src/app/features/coupling/coupling.component.html -26 47 apps/frontend/src/app/features/coupling/coupling.component.ts -66 0 apps/frontend/src/app/features/coupling/coupling.store.ts - -"Manfred Steyer ,Fri Sep 27 22:48:19 2024 +0200 76656b36d327a6c9f45040294c99cc83a30d84b0,refactor(frontend): remove unnecessary rendering effect in hotspot component" -1 1 .detective/hash -9 0 .detective/log -5 1 apps/frontend/src/app/features/hotspot/hotspot.component.html -26 7 apps/frontend/src/app/features/hotspot/hotspot.component.ts - -"Manfred Steyer ,Fri Sep 27 22:20:25 2024 +0200 5547a7c4196a2ba80acb704250c0a76d9e1101c1,feat: add limits store to team alignment and hotspots" -1 1 .detective/hash -8 8 .detective/log -6 1 apps/frontend/src/app/features/hotspot/hotspot.component.html -8 1 apps/frontend/src/app/features/hotspot/hotspot.component.ts -6 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -7 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -1 1 apps/frontend/src/index.html - -"Manfred Steyer ,Fri Sep 27 21:59:41 2024 +0200 2f9b490469e722542cea8ac38eeabe02c5ea78e1,feat: add store for limits" -1 1 .detective/hash -22 0 .detective/log -15 0 apps/frontend/src/app/data/limits.store.ts -6 1 apps/frontend/src/app/features/coupling/coupling.component.html -16 8 apps/frontend/src/app/features/coupling/coupling.component.ts -4 0 apps/frontend/src/app/model/limits.ts -5 2 apps/frontend/src/app/ui/limits/limits.component.html -9 17 apps/frontend/src/app/ui/limits/limits.component.ts -32 0 package-lock.json -2 0 package.json - -"Manfred Steyer ,Fri Sep 27 21:19:56 2024 +0200 78933966b3593f52c81fe26a3472b0c7a46eee71,chore(frontend): remove unneeded linting directive" -0 1 apps/frontend/src/app/ui/graph/graph.ts - -"Manfred Steyer ,Fri Sep 27 14:48:06 2024 +0200 bc87b324a60db023efb3bbeb2d82d5e193e1db35,chore(release): publish 1.1.2" -5 14 .detective/config.json -1 1 .detective/hash -5 0 .detective/log -14 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Fri Sep 27 14:46:09 2024 +0200 955828b34c0cfce98bbd78073d6fdeee761b7191,feat(frontend): show actual number of changed lines in team alignment" -1 1 .detective/hash -110 0 .detective/log -3 2 apps/frontend/src/app/features/team-alignment/team-alignment-chart-adapter.ts - -"Manfred Steyer ,Fri Sep 27 14:28:59 2024 +0200 d5dbabd99f0fec965ff12158946bcc719fd3f632,fix(frontend): only show one tip tool text at once" -14 5 .detective/config.json -4 0 apps/frontend/src/app/ui/graph/graph.ts - -"Manfred Steyer ,Mon Sep 16 23:33:34 2024 +0200 72f00f123142e78af1c57c29d5c7b91f65095113,chore(release): publish 1.1.1" -10 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Mon Sep 16 21:43:04 2024 +0200 8502f9211d181e0ebcee20d98c6c7f8b1e0e8b68,chore: add template for pull requests" -19 0 .github/pull_request_template.md - -"Manfred Steyer ,Mon Sep 16 20:55:03 2024 +0200 4a3c3605aa11f6fc62a2df443cf4b1069676bb82,chore: add import lint plugin for sorting imports" -19 1 .eslintrc.json -1 1 .github/workflows/{pipeline.yaml => build.yaml} -1 1 README.md -11 9 apps/backend/src/express.ts -3 1 apps/backend/src/infrastructure/config.ts -6 3 apps/backend/src/infrastructure/deps.ts -2 1 apps/backend/src/infrastructure/git.ts -1 0 apps/backend/src/infrastructure/log.ts -1 0 apps/backend/src/infrastructure/tree-hash.ts -3 3 apps/backend/src/main.ts -2 1 apps/backend/src/options/validate-options.ts -1 0 apps/backend/src/services/change-coupling.spec.ts -2 1 apps/backend/src/services/coupling.spec.ts -5 4 apps/backend/src/services/coupling.ts -2 1 apps/backend/src/services/folders.spec.ts -1 1 apps/backend/src/services/folders.ts -1 0 apps/backend/src/services/hotspot.spec.ts -5 6 apps/backend/src/services/hotspot.ts -1 1 apps/backend/src/services/log-cache.ts -1 0 apps/backend/src/services/team-alignment.spec.ts -2 1 apps/backend/src/utils/complexity.ts -3 2 apps/backend/src/utils/git-parser.spec.ts -5 3 apps/backend/src/utils/git-parser.ts -0 31 apps/frontend/src/app/app.component.spec.ts -2 1 apps/frontend/src/app/app.component.ts -3 4 apps/frontend/src/app/app.config.ts -4 3 apps/frontend/src/app/app.routes.ts -2 1 apps/frontend/src/app/data/cache.service.ts -1 0 apps/frontend/src/app/data/config.service.ts -1 0 apps/frontend/src/app/data/coupling.service.ts -1 0 apps/frontend/src/app/data/folder.service.ts -1 0 apps/frontend/src/app/data/hotspot.service.ts -1 0 apps/frontend/src/app/data/module.service.ts -1 0 apps/frontend/src/app/data/status.service.ts -3 1 apps/frontend/src/app/data/status.store.ts -2 1 apps/frontend/src/app/data/team-alignment.service.ts -0 21 apps/frontend/src/app/features/coupling/coupling.component.spec.ts -12 11 apps/frontend/src/app/features/coupling/coupling.component.ts -2 1 apps/frontend/src/app/features/coupling/graph.adapter.ts -0 22 apps/frontend/src/app/features/hotspot/hotspot.component.spec.ts -25 24 apps/frontend/src/app/features/hotspot/hotspot.component.ts -1 1 apps/frontend/src/app/features/team-alignment/team-alignment-chart-adapter.ts -0 22 apps/frontend/src/app/features/team-alignment/team-alignment.component.spec.ts -7 5 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -0 21 apps/frontend/src/app/shell/about/about.component.spec.ts -1 1 apps/frontend/src/app/shell/about/about.component.ts -2 1 apps/frontend/src/app/shell/cache.guard.ts -0 22 apps/frontend/src/app/shell/filter-tree/filter-tree.component.spec.ts -7 6 apps/frontend/src/app/shell/filter-tree/filter-tree.component.ts -0 25 apps/frontend/src/app/shell/nav/nav.component.spec.ts -7 6 apps/frontend/src/app/shell/nav/nav.component.ts -0 21 apps/frontend/src/app/ui/doughnut/doughnut.component.spec.ts -1 1 apps/frontend/src/app/ui/doughnut/doughnut.component.ts -0 21 apps/frontend/src/app/ui/graph/graph.component.spec.ts -2 1 apps/frontend/src/app/ui/graph/graph.component.ts -1 2 apps/frontend/src/app/ui/graph/graph.ts -0 22 apps/frontend/src/app/ui/limits/limits.component.spec.ts -4 3 apps/frontend/src/app/ui/limits/limits.component.ts -0 22 apps/frontend/src/app/ui/loading/loading.component.spec.ts -2 1 apps/frontend/src/main.ts -1212 40 package-lock.json -1 0 package.json - -"Manfred Steyer ,Mon Sep 16 20:50:12 2024 +0200 671c2e7d8297917cb96461c9cd43e007aa1755f4,Merge branch 'main' of https://github.com/angular-architects/forensic" -"Manfred Steyer ,Mon Sep 16 20:49:55 2024 +0200 19fbe527a119b1cd21c83b1cf3583cfcdd50fd7f,chore: add github action" -34 0 .github/workflows/pipeline.yaml -21 0 LICENSE -1 1 apps/frontend/src/app/app.routes.ts -3 0 package.json - -"Manfred Steyer ,Mon Sep 16 19:51:23 2024 +0200 1bae09cd79a031c850f7fff621b47f7884038a9e,Merge pull request #22 from wamasimba/feature/git-log-aliases" -"Manfred Steyer ,Mon Sep 16 19:46:45 2024 +0200 9d2d38bb8ae990f2bce243387732e0452b7827bb,Merge branch 'main' of https://github.com/angular-architects/forensic" -"Manfred Steyer ,Mon Sep 16 19:46:18 2024 +0200 8d4ab63c945cf76d2dc1e7f6c0a43f8a68335911,chore: add issue template for github" -19 0 .detective/config.json -1 0 .detective/hash -5559 0 .detective/log -15 0 .github/ISSUE_TEMPLATE/bug_report.md -1 6 apps/backend/project.json -1 1 apps/frontend/src/app/app.routes.ts -1 1 apps/frontend/src/app/data/hotspot.service.ts -1 1 apps/frontend/src/app/features/hotspot/hotspot.component.ts -0 0 apps/frontend/src/app/{features/hotspot => model}/hotspot-result.ts -0 0 apps/frontend/src/app/{utils => shell}/cache.guard.ts -0 0 deps.txt - -"Manfred Steyer ,Mon Sep 16 19:12:11 2024 +0200 60f7fd584bd654d3cc8dc7229c14d82d41f0aa68,Merge pull request #20 from rainerhahnekamp/docs/json5" -"John van Leeuwen ,Sun Sep 15 15:58:29 2024 +0200 5f033a7b59238ceba1f906d11d8c2fa097918b96,feat: support aliases for git log usernames" -15 0 README.md -1 0 apps/backend/src/infrastructure/config.ts -2 0 apps/backend/src/model/config.ts -2 0 apps/backend/src/services/team-alignment.ts - -"John van Leeuwen ,Sat Sep 14 10:10:04 2024 +0200 04ddb0761f3cf8e7abcdaad32c4ac1ff77d45abb,chore: check git log line for at least two tabs before treating as file line" -1 1 apps/backend/src/utils/git-parser.ts - -"Rainer Hahnekamp ,Thu Sep 12 18:18:49 2024 +0200 16f52f428c5a9b591c5cb97b1ba941c661068217,docs: use json5 instead json" -3 3 README.md - -"Manfred Steyer ,Wed Sep 11 20:12:05 2024 +0200 8dc594f9d9b7e7fa74ae6e81c1a818055616d39f,chore(release): publish 1.1.0" -19 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Wed Sep 11 20:10:41 2024 +0200 c76dcde8ec9f9967447c496038c7042986935d36,feat: link readme for defining teams" -8 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -2 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -7 0 apps/frontend/src/styles.scss - -"Manfred Steyer ,Wed Sep 11 19:30:01 2024 +0200 83d45dc02b1d57ae7c5a96e79a35e739288c259e,feat: use version together with git hash as cache key" -42 1 README.md -2 0 apps/backend/src/infrastructure/version.ts -2 1 apps/backend/src/main.ts -7 2 apps/backend/src/services/log-cache.ts -0 0 deps.txt - -"Manfred Steyer ,Wed Sep 11 19:00:15 2024 +0200 b55048907a2883dc60d7d51194956dc2052e2dd3,chore: improve pre-commit hook" -2 1 .husky/pre-commit - -"Manfred Steyer ,Wed Sep 11 17:24:13 2024 +0200 7ee2005c0de7e7078ddfe76e2c3d1af263fcdaed,fix: normalize path for hotspot analysis" -1 1 apps/backend/src/infrastructure/config.ts -3 3 apps/backend/src/model/config.ts -5 3 apps/backend/src/services/hotspot.ts -3 8 apps/backend/src/utils/git-parser.spec.ts -6 4 apps/backend/src/utils/git-parser.ts - -"Manfred Steyer ,Wed Sep 11 17:01:17 2024 +0200 26b8c6610c73ad5abc47e00d6ee5c0cdda7f1eb9,feat: add file filter" -4 1 apps/backend/src/infrastructure/config.ts -10 2 apps/backend/src/model/config.ts -0 1 apps/backend/src/services/hotspot.spec.ts -2 5 apps/backend/src/services/hotspot.ts -114 61 apps/backend/src/utils/git-parser.spec.ts -12 4 apps/backend/src/utils/git-parser.ts -19 0 package-lock.json -2 0 package.json - -"Manfred Steyer ,Wed Sep 11 14:57:44 2024 +0200 dba3f4605fe8c61e7a98fc4c619068a143356db9,feat: add sum of coupling" -1 0 apps/backend/src/services/change-coupling.spec.ts -18 1 apps/backend/src/services/change-coupling.ts -1 0 apps/backend/src/services/hotspot.spec.ts -4 1 apps/backend/src/services/hotspot.ts -2 0 apps/frontend/src/app/features/coupling/coupling.component.ts -5 2 apps/frontend/src/app/features/coupling/graph.adapter.ts -2 0 apps/frontend/src/app/model/coupling-result.ts -0 1 apps/frontend/src/app/ui/graph/graph.component.ts - -"Manfred Steyer ,Wed Sep 11 11:01:11 2024 +0200 111d02a6e7bd40f3c1bcd8781393ebd9ad681ba6,feat: allow to filter git log" -1 0 apps/backend/src/infrastructure/config.ts -5 1 apps/backend/src/infrastructure/git.ts -2 0 apps/backend/src/model/config.ts -7 2 apps/backend/src/services/change-coupling.ts -15 5 apps/backend/src/services/hotspot.ts -7 2 apps/backend/src/services/team-alignment.ts -144 19 apps/backend/src/utils/git-parser.spec.ts -33 4 apps/backend/src/utils/git-parser.ts - -"Manfred Steyer ,Mon Sep 9 22:58:43 2024 +0200 9a25dcb8201c2dde68dbca1f57fdb3da4d211b6d,fix: remove unneeded console logs" -1 1 CHANGELOG.md -0 1 apps/backend/src/infrastructure/deps.ts -0 5 apps/backend/src/services/log-cache.ts - -"Manfred Steyer ,Mon Sep 9 22:52:58 2024 +0200 844021c28237424e70643a3032e3ddc1697e240e,chore: update to sheriff-core 0.17.1" -1 0 apps/frontend/src/app/ui/graph/graph.component.ts -4 4 package-lock.json -1 1 package.json - -"Manfred Steyer ,Mon Sep 9 22:49:07 2024 +0200 32f5b9218f0235906ae6c1e1aab6d091492066e1,chore(release): publish 1.0.3" -5 1 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 8 23:54:38 2024 +0200 a0a7b98a3e6ea5e52bdf3a9d19d5e9ed68d72ce4,chore: add nx release and changelog" -2 11 CHANGELOG.md -2 3 apps/backend/src/utils/normalize-folder.spec.ts -3 0 nx.json -2 1 package.json - -"Manfred Steyer ,Sun Sep 8 23:50:37 2024 +0200 02c318cb4fa6a87d4c8f0619d033f890253f5d1a,chore(release): publish 1.0.5-0" -62 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 8 23:46:58 2024 +0200 79953a544d52af95c6d9720799d8ee6567c3ca36,chore(release): publish v1.0.0" -5 1 CHANGELOG.md - -"Manfred Steyer ,Sun Sep 8 23:44:14 2024 +0200 308c935211fc584c7aeedd8708991a6619f5dd0a,chore(release): publish 1.0.3-0" -3 0 CHANGELOG.md -1 1 apps/backend/package.json - -"Manfred Steyer ,Sun Sep 8 22:01:57 2024 +0200 e07d8e73ab55e99259da821a08a6f8e500f1b2e3,test: add test for normalize-folder" -48 1 apps/backend/src/services/team-alignment.spec.ts -28 0 apps/backend/src/utils/normalize-folder.spec.ts - -"Manfred Steyer ,Sun Sep 8 21:41:29 2024 +0200 a16123afe9f4bde4222815d2e28c1f1afb7d31d1,test: add tests for renamed filenames" -0 26 apps/backend/src/services/folders.ts -355 223 apps/backend/src/utils/git-parser.spec.ts -1 7 apps/backend/src/utils/git-parser.ts - -"Manfred Steyer ,Sun Sep 8 20:26:31 2024 +0200 233b4861fc725b9e8c783599c73e4eb08495174b,test: add test for folder service" -1 0 apps/backend/src/infrastructure/deps.ts -6 8 apps/backend/src/services/coupling.spec.ts -103 0 apps/backend/src/services/folders.spec.ts - -"Manfred Steyer ,Sun Sep 8 20:11:48 2024 +0200 9bfb2f377d354efd6987c243a615690e0b5d0683,test: add test for coupling service" -68 0 apps/backend/src/services/coupling.spec.ts -2 1 apps/backend/src/services/coupling.ts - -"Manfred Steyer ,Sun Sep 8 19:54:39 2024 +0200 e40e49a5bae2170ebc070e51181165f7e27a705b,test: add test for git parser" -32 34 apps/backend/src/services/team-alignment.spec.ts -17 0 apps/backend/src/utils/date-utils.ts -290 0 apps/backend/src/utils/git-parser.spec.ts -15 13 apps/backend/src/utils/git-parser.ts - -"Manfred Steyer ,Sun Sep 8 19:04:57 2024 +0200 ba3b77e12d33abe6f96cb8533adcf787a35b9733,test: add test for team-alignment" -115 0 apps/backend/src/services/team-alignment.spec.ts - -"Manfred Steyer ,Sat Sep 7 23:36:13 2024 +0200 49875f49aba504604f70c61fc2711c5aea3cf618,test: add test for hotspot service" -166 0 apps/backend/src/services/hotspot.spec.ts -5 2 apps/backend/src/services/hotspot.ts - -"Manfred Steyer ,Sat Sep 7 17:18:48 2024 +0200 ff2e42e6674d22ecb7b18ded0cba65560b545f8b,chore: add husky for linting, prettier, and commitlint" -2 1 .eslintrc.json -1 0 .husky/commit-msg -2 0 .husky/pre-commit -7 7 README.md -1 1 apps/frontend/src/app/app.routes.ts -2 8 apps/frontend/src/app/features/coupling/coupling.component.ts -17 6 apps/frontend/src/app/features/hotspot/hotspot.component.ts -1 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -12 15 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -12 11 apps/frontend/src/app/shell/about/about.component.css -23 12 apps/frontend/src/app/shell/about/about.component.html -1 1 apps/frontend/src/app/ui/doughnut/doughnut.component.html -23 3 apps/frontend/src/app/ui/doughnut/doughnut.component.ts -8 1 apps/frontend/src/app/ui/graph/graph.component.ts -7 7 apps/frontend/src/styles.scss -3 0 commitlint.config.js -1171 0 package-lock.json -16 11 package.json - -"Manfred Steyer ,Sat Sep 7 15:04:39 2024 +0200 e5444246aa02429b44b677ff60b2fadd3fc8b380,refactor: extract dumb doughnut component" -6 34 apps/frontend/src/app/features/team-alignment/{team-alignment-chart.ts => team-alignment-chart-adapter.ts} -3 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -15 42 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -3 3 apps/frontend/src/app/shell/about/about.component.html -0 0 apps/frontend/src/app/ui/doughnut/doughnut.component.css -3 0 apps/frontend/src/app/ui/doughnut/doughnut.component.html -21 0 apps/frontend/src/app/ui/doughnut/doughnut.component.spec.ts -39 0 apps/frontend/src/app/ui/doughnut/doughnut.component.ts -1 1 apps/frontend/src/app/ui/graph/graph.component.ts - -"Manfred Steyer ,Sat Sep 7 12:11:45 2024 +0200 09ef0d1deaba19df54453d3262d4d52c97ea4857,refactor: move hotspot service to data layer" -2 2 apps/frontend/src/app/{features/hotspot => data}/hotspot.service.ts -1 1 apps/frontend/src/app/features/hotspot/hotspot.component.ts - -"Manfred Steyer ,Sat Sep 7 12:10:23 2024 +0200 1093c805716748bde46f187f34e161f3e8bb24fd,refactor: extract dumb graph component" -2 2 apps/frontend/src/app/features/coupling/coupling.component.html -13 24 apps/frontend/src/app/features/coupling/coupling.component.ts -1 1 apps/frontend/src/app/features/coupling/graph.adapter.ts -28 0 apps/frontend/src/app/ui/graph/graph.component.css -2 0 apps/frontend/src/app/ui/graph/graph.component.html -21 0 apps/frontend/src/app/ui/graph/graph.component.spec.ts -21 0 apps/frontend/src/app/ui/graph/graph.component.ts -0 0 apps/frontend/src/app/{features/coupling => ui/graph}/graph.ts - -"Manfred Steyer ,Fri Sep 6 21:19:28 2024 +0200 0654cf3aa498fc0ba5a747e3b0f0b66ab57d1e89,refactor: rename graph to coupling component" -3 3 apps/frontend/src/app/app.routes.ts -0 0 apps/frontend/src/app/features/coupling/{graph.component.css => coupling.component.css} -0 0 apps/frontend/src/app/features/coupling/{graph.component.html => coupling.component.html} -5 6 apps/frontend/src/app/features/coupling/{graph.component.spec.ts => coupling.component.spec.ts} -3 3 apps/frontend/src/app/features/coupling/{graph.component.ts => coupling.component.ts} -2 2 apps/frontend/src/app/shell/nav/nav.component.ts - -"Manfred Steyer ,Fri Sep 6 21:09:49 2024 +0200 aa77e90042747b877ed6aa3ca0706f89ec3e6cc5,refactor: remove unneeded ifs" -15 11 apps/frontend/src/app/features/coupling/graph.component.ts -2 6 apps/frontend/src/app/features/hotspot/hotspot.component.ts -3 4 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts - -"Manfred Steyer ,Fri Sep 6 19:30:26 2024 +0200 141f057101c0396a755c152c065e31c1fbdcd995,test: fix test name for change coupling service test" -2 3 apps/backend/src/services/change-coupling.spec.ts - -"Manfred Steyer ,Fri Sep 6 19:22:23 2024 +0200 ac081d692348d64a1128af215a8baa5491d6aad8,refactor: streamline rendering effect for team alignment" -5 0 .eslintrc.json -20 17 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts - -"Manfred Steyer ,Fri Sep 6 18:18:05 2024 +0200 a016b720caba1d79c24c36d695fc7562de43f327,test: adding test for change-coupling in backend" -7 0 apps/backend/src/model/config.ts -60 0 apps/backend/src/services/change-coupling.spec.ts -1 1 apps/backend/src/services/change-coupling.ts - -"Manfred Steyer ,Fri Sep 6 15:53:29 2024 +0200 3ff791a10a5e3687e5c28a3da3fc2f1fe30582bc,refactor: streamline rxjs usage" -1 1 apps/frontend/src/app/features/hotspot/hotspot.component.html -46 33 apps/frontend/src/app/features/hotspot/hotspot.component.ts -13 8 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts - -"Manfred Steyer ,Thu Sep 5 00:45:16 2024 +0200 c2051bbcdd2c24712c19762de3789b002181b6b0,chore: allow no-unused-vars for variables starting with _" -1 1 apps/frontend/src/app/shell/filter-tree/filter-tree.component.ts - -"Manfred Steyer ,Tue Sep 3 16:27:11 2024 +0200 256eb479df0108e2fb3dd575f8f2159fb89794fd,feat: improvde default entry points" -1 1 apps/backend/project.json -3 1 apps/backend/src/infrastructure/deps.ts - -"Manfred Steyer ,Tue Sep 3 16:20:15 2024 +0200 8da257396166e9118849c1c40160e8fb3fa89c52,fix: fix tree appearance" -1 1 apps/backend/package.json -0 40 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css -17 28 apps/frontend/src/app/shell/filter-tree/filter-tree.component.html -2 2 apps/frontend/src/app/shell/filter-tree/filter-tree.component.ts - -"Manfred Steyer ,Tue Sep 3 16:10:03 2024 +0200 026a16fd558602ac32fe93e8a10740d7e6307116,fix: add missing files" -13 0 .editorconfig -1 0 .eslintignore -42 0 .eslintrc.json -44 0 .gitignore -6 0 .prettierignore -3 0 .prettierrc -1 1 apps/backend/project.json - -"Manfred Steyer ,Tue Sep 3 02:04:43 2024 +0200 8c6ed746a175d732164b3adbd369bcf0029c6b83,feat: add help icons with tooltip texts to the features" -1 1 README.md -8 1 apps/frontend/src/app/features/coupling/graph.component.html -9 0 apps/frontend/src/app/features/coupling/graph.component.ts -8 0 apps/frontend/src/app/features/hotspot/hotspot.component.html -5 3 apps/frontend/src/app/features/hotspot/hotspot.component.ts -8 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -9 1 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -19 0 apps/frontend/src/styles.scss - -"Manfred Steyer ,Tue Sep 3 01:26:43 2024 +0200 da812593173e15732b9359f1a58708892f214a35,refactor: adjust tree css" -18 5 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css - -"Manfred Steyer ,Tue Sep 3 01:04:49 2024 +0200 a1a3acbf2335f01e500674e3e24e8ebea0b3c082,docs: add readme" -111 0 README.md -6 2 apps/backend/package.json -5 0 apps/frontend/src/app/app.routes.ts -26 0 apps/frontend/src/app/shell/about/about.component.css -38 0 apps/frontend/src/app/shell/about/about.component.html -21 0 apps/frontend/src/app/shell/about/about.component.spec.ts -11 0 apps/frontend/src/app/shell/about/about.component.ts -4 4 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css -0 4 apps/frontend/src/app/shell/filter-tree/filter-tree.component.html -4 0 apps/frontend/src/app/shell/nav/nav.component.css -11 0 apps/frontend/src/app/shell/nav/nav.component.html -- - docs/change-coupling.png -- - docs/domains-detail.png -- - docs/hotspots.png -- - docs/team-alignment.png -3 0 nx.json - -"Manfred Steyer ,Mon Sep 2 23:49:42 2024 +0200 42bc2df12649b86b65fdc53b11896d0746f16818,refactor: delete orignal project folders" -0 8 _backend/.detective/config.json -0 355 _backend/.detective/deps.json -0 6 _backend/.prettierrc -- - _backend/detective-0.0.1.tgz -0 3 _backend/export.txt -0 1 _backend/forensic.conf.json -0 1781 _backend/package-lock.json -0 29 _backend/package.json -0 3632 _backend/public/chunk-2FMXBR5T.js -0 10784 _backend/public/chunk-HIPB54JD.js -0 11131 _backend/public/chunk-HQFIBEV7.js -0 3632 _backend/public/chunk-OVZZS7I2.js -0 10644 _backend/public/chunk-QWS3MURL.js -0 3632 _backend/public/chunk-UOJ4IBH7.js -- - _backend/public/favicon.ico -0 1541 _backend/public/index.html -0 77834 _backend/public/main-4V2HQDRI.js -0 458 _backend/public/main-AIZTFC63.css -0 62671 _backend/public/main-C2TU7PNS.js -0 62674 _backend/public/main-DAYCMAFW.js -0 62705 _backend/public/main-K3WBRXWH.js -0 73390 _backend/public/main-MTHSAXW3.js -0 62706 _backend/public/main-UJURQ3JB.js -0 62674 _backend/public/main-UO5QPOB3.js -0 1 _backend/public/ping.txt -0 2030 _backend/public/polyfills-SCHOHYNV.js -0 2900 _backend/public/styles-KWQ3FGSD.css -0 2442 _backend/public/styles-OPAWKEW6.css -0 2913 _backend/public/styles-SVPGZYNK.css -0 4 _backend/publish.sh -0 17 _backend/readme.md -0 182 _backend/src/express.ts -0 42 _backend/src/index.ts -0 39 _backend/src/infrastructure/config.ts -0 60 _backend/src/infrastructure/deps.ts -0 88 _backend/src/infrastructure/git.ts -0 17 _backend/src/infrastructure/log.ts -0 3 _backend/src/infrastructure/paths.ts -0 17 _backend/src/infrastructure/tree-hash.ts -0 6 _backend/src/model/config.ts -0 7 _backend/src/model/deps.ts -0 9 _backend/src/model/limits.ts -0 17 _backend/src/options/options.ts -0 38 _backend/src/options/parse-options.ts -0 18 _backend/src/options/validate-options.ts -0 63 _backend/src/services/change-coupling.ts -0 93 _backend/src/services/coupling.ts -0 81 _backend/src/services/folders.ts -0 156 _backend/src/services/hotspot.ts -0 27 _backend/src/services/log-cache.ts -0 27 _backend/src/services/module-info.ts -0 109 _backend/src/services/team-alignment.ts -0 43 _backend/src/utils/complexity.ts -0 6 _backend/src/utils/count-lines.ts -0 181 _backend/src/utils/git-parser.ts -0 3 _backend/src/utils/matrix.ts -0 13 _backend/src/utils/normalize-folder.ts -0 30 _backend/src/utils/open.ts -0 3 _backend/src/utils/to-percent.ts -0 13 _backend/tsconfig.json -0 16 _fe/.editorconfig -0 42 _fe/.gitignore -0 6 _fe/.prettierrc -0 4 _fe/.vscode/extensions.json -0 20 _fe/.vscode/launch.json -0 42 _fe/.vscode/tasks.json -0 27 _fe/README.md -0 94 _fe/angular.json -0 3 _fe/build.sh -0 43 _fe/eslint.config.js -0 146 _fe/m3-theme.scss -0 17222 _fe/package-lock.json -0 58 _fe/package.json -0 7 _fe/proxy.conf.json -- - _fe/public/favicon.ico -0 0 _fe/src/app/app.component.css -0 1 _fe/src/app/app.component.html -0 31 _fe/src/app/app.component.spec.ts -0 19 _fe/src/app/app.component.ts -0 24 _fe/src/app/app.config.ts -0 42 _fe/src/app/app.routes.ts -0 19 _fe/src/app/data/cache.service.ts -0 17 _fe/src/app/data/config.service.ts -0 26 _fe/src/app/data/coupling.service.ts -0 13 _fe/src/app/data/folder.service.ts -0 13 _fe/src/app/data/module.service.ts -0 14 _fe/src/app/data/status.service.ts -0 17 _fe/src/app/data/status.store.ts -0 17 _fe/src/app/data/team-alignment.service.ts -0 150 _fe/src/app/features/coupling/graph.adapter.ts -0 60 _fe/src/app/features/coupling/graph.component.css -0 21 _fe/src/app/features/coupling/graph.component.html -0 22 _fe/src/app/features/coupling/graph.component.spec.ts -0 104 _fe/src/app/features/coupling/graph.component.ts -0 246 _fe/src/app/features/coupling/graph.ts -0 43 _fe/src/app/features/hotspot/hotspot-result.ts -0 57 _fe/src/app/features/hotspot/hotspot.component.css -0 105 _fe/src/app/features/hotspot/hotspot.component.html -0 22 _fe/src/app/features/hotspot/hotspot.component.spec.ts -0 195 _fe/src/app/features/hotspot/hotspot.component.ts -0 32 _fe/src/app/features/hotspot/hotspot.service.ts -0 97 _fe/src/app/features/team-alignment/team-alignment-chart.ts -0 0 _fe/src/app/features/team-alignment/team-alignment.component.css -0 20 _fe/src/app/features/team-alignment/team-alignment.component.html -0 22 _fe/src/app/features/team-alignment/team-alignment.component.spec.ts -0 77 _fe/src/app/features/team-alignment/team-alignment.component.ts -0 3 _fe/src/app/model/cache-status.ts -0 8 _fe/src/app/model/config.ts -0 7 _fe/src/app/model/coupling-result.ts -0 5 _fe/src/app/model/folder.ts -0 5 _fe/src/app/model/graph-type.ts -0 9 _fe/src/app/model/limits.ts -0 3 _fe/src/app/model/module-info.ts -0 7 _fe/src/app/model/status.ts -0 8 _fe/src/app/model/team-alignment-result.ts -0 27 _fe/src/app/shell/filter-tree/filter-tree.component.css -0 46 _fe/src/app/shell/filter-tree/filter-tree.component.html -0 22 _fe/src/app/shell/filter-tree/filter-tree.component.spec.ts -0 135 _fe/src/app/shell/filter-tree/filter-tree.component.ts -0 26 _fe/src/app/shell/nav/nav.component.css -0 52 _fe/src/app/shell/nav/nav.component.html -0 25 _fe/src/app/shell/nav/nav.component.spec.ts -0 41 _fe/src/app/shell/nav/nav.component.ts -0 3 _fe/src/app/types.d.ts -0 19 _fe/src/app/ui/limits/limits.component.css -0 34 _fe/src/app/ui/limits/limits.component.html -0 22 _fe/src/app/ui/limits/limits.component.spec.ts -0 82 _fe/src/app/ui/limits/limits.component.ts -0 3 _fe/src/app/ui/loading/loading.component.css -0 12 _fe/src/app/ui/loading/loading.component.html -0 22 _fe/src/app/ui/loading/loading.component.spec.ts -0 11 _fe/src/app/ui/loading/loading.component.ts -0 30 _fe/src/app/utils/cache.guard.ts -0 9 _fe/src/app/utils/debounce.ts -0 24 _fe/src/app/utils/effects.ts -0 34 _fe/src/app/utils/error-handler.ts -0 7 _fe/src/app/utils/event.service.ts -0 8 _fe/src/app/utils/segments.ts -0 21 _fe/src/index.html -0 7 _fe/src/main.ts -0 85 _fe/src/styles.scss -0 11 _fe/tsconfig.app.json -0 28 _fe/tsconfig.json -0 10 _fe/tsconfig.spec.json -- - backend/.detective/.DS_Store -0 8 backend/.detective/config.json -0 355 backend/.detective/deps.json -0 6 backend/.prettierrc -- - backend/detective-0.0.1.tgz -0 336 backend/dist/express.js -0 36 backend/dist/index.js -0 45 backend/dist/infrastructure/config.js -0 77 backend/dist/infrastructure/deps.js -0 84 backend/dist/infrastructure/git.js -0 2 backend/dist/model/config.js -0 2 backend/dist/model/deps.js -0 7 backend/dist/model/limits.js -0 11 backend/dist/options/options.js -0 54 backend/dist/options/parse-options.js -0 24 backend/dist/options/validate-options.js -0 204 backend/dist/services/change-coupling.js -0 77 backend/dist/services/coupling.js -0 95 backend/dist/services/folders.js -0 329 backend/dist/services/hotspot.js -0 22 backend/dist/services/module-info.js -0 255 backend/dist/services/team-alignment.js -0 85 backend/dist/utils/complexity.js -0 289 backend/dist/utils/git-parser.js -0 8 backend/dist/utils/matrix.js -0 16 backend/dist/utils/normalize-folder.js -0 34 backend/dist/utils/open.js -0 7 backend/dist/utils/round.js -0 6 backend/dist/utils/to-percent.js -0 3 backend/export.txt -0 1 backend/forensic.conf.json -0 1 backend/node_modules/.bin/acorn -0 1 backend/node_modules/.bin/mime -0 1 backend/node_modules/.bin/mkdirp -0 1 backend/node_modules/.bin/prettier -0 1 backend/node_modules/.bin/resolve -0 1 backend/node_modules/.bin/rimraf -0 1 backend/node_modules/.bin/sheriff -0 1 backend/node_modules/.bin/tree-kill -0 1 backend/node_modules/.bin/ts-node -0 1 backend/node_modules/.bin/ts-node-cwd -0 1 backend/node_modules/.bin/ts-node-dev -0 1 backend/node_modules/.bin/ts-node-esm -0 1 backend/node_modules/.bin/ts-node-script -0 1 backend/node_modules/.bin/ts-node-transpile-only -0 1 backend/node_modules/.bin/ts-script -0 1 backend/node_modules/.bin/tsc -0 1 backend/node_modules/.bin/tsnd -0 1 backend/node_modules/.bin/tsserver -0 1761 backend/node_modules/.package-lock.json -0 21 backend/node_modules/@cspotcode/source-map-support/LICENSE.md -0 289 backend/node_modules/@cspotcode/source-map-support/README.md -0 114 backend/node_modules/@cspotcode/source-map-support/browser-source-map-support.js -0 50 backend/node_modules/@cspotcode/source-map-support/package.json -0 7 backend/node_modules/@cspotcode/source-map-support/register-hook-require.d.ts -0 3 backend/node_modules/@cspotcode/source-map-support/register-hook-require.js -0 7 backend/node_modules/@cspotcode/source-map-support/register.d.ts -0 1 backend/node_modules/@cspotcode/source-map-support/register.js -0 76 backend/node_modules/@cspotcode/source-map-support/source-map-support.d.ts -0 938 backend/node_modules/@cspotcode/source-map-support/source-map-support.js -0 19 backend/node_modules/@jridgewell/resolve-uri/LICENSE -0 40 backend/node_modules/@jridgewell/resolve-uri/README.md -0 232 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs -0 1 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs.map -0 240 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js -0 1 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js.map -0 4 backend/node_modules/@jridgewell/resolve-uri/dist/types/resolve-uri.d.ts -0 69 backend/node_modules/@jridgewell/resolve-uri/package.json -0 21 backend/node_modules/@jridgewell/sourcemap-codec/LICENSE -0 264 backend/node_modules/@jridgewell/sourcemap-codec/README.md -0 424 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs -0 1 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map -0 439 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js -0 1 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map -0 49 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/scopes.d.ts -0 8 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/sourcemap-codec.d.ts -0 15 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/strings.d.ts -0 6 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/vlq.d.ts -0 75 backend/node_modules/@jridgewell/sourcemap-codec/package.json -0 19 backend/node_modules/@jridgewell/trace-mapping/LICENSE -0 193 backend/node_modules/@jridgewell/trace-mapping/README.md -0 514 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs -0 1 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs.map -0 528 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js -0 1 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js.map -0 8 backend/node_modules/@jridgewell/trace-mapping/dist/types/any-map.d.ts -0 32 backend/node_modules/@jridgewell/trace-mapping/dist/types/binary-search.d.ts -0 7 backend/node_modules/@jridgewell/trace-mapping/dist/types/by-source.d.ts -0 1 backend/node_modules/@jridgewell/trace-mapping/dist/types/resolve.d.ts -0 2 backend/node_modules/@jridgewell/trace-mapping/dist/types/sort.d.ts -0 16 backend/node_modules/@jridgewell/trace-mapping/dist/types/sourcemap-segment.d.ts -0 4 backend/node_modules/@jridgewell/trace-mapping/dist/types/strip-filename.d.ts -0 70 backend/node_modules/@jridgewell/trace-mapping/dist/types/trace-mapping.d.ts -0 85 backend/node_modules/@jridgewell/trace-mapping/dist/types/types.d.ts -0 70 backend/node_modules/@jridgewell/trace-mapping/package.json -0 21 backend/node_modules/@nodelib/fs.scandir/LICENSE -0 171 backend/node_modules/@nodelib/fs.scandir/README.md -0 20 backend/node_modules/@nodelib/fs.scandir/out/adapters/fs.d.ts -0 19 backend/node_modules/@nodelib/fs.scandir/out/adapters/fs.js -0 4 backend/node_modules/@nodelib/fs.scandir/out/constants.d.ts -0 17 backend/node_modules/@nodelib/fs.scandir/out/constants.js -0 12 backend/node_modules/@nodelib/fs.scandir/out/index.d.ts -0 26 backend/node_modules/@nodelib/fs.scandir/out/index.js -0 7 backend/node_modules/@nodelib/fs.scandir/out/providers/async.d.ts -0 104 backend/node_modules/@nodelib/fs.scandir/out/providers/async.js -0 1 backend/node_modules/@nodelib/fs.scandir/out/providers/common.d.ts -0 13 backend/node_modules/@nodelib/fs.scandir/out/providers/common.js -0 5 backend/node_modules/@nodelib/fs.scandir/out/providers/sync.d.ts -0 54 backend/node_modules/@nodelib/fs.scandir/out/providers/sync.js -0 20 backend/node_modules/@nodelib/fs.scandir/out/settings.d.ts -0 24 backend/node_modules/@nodelib/fs.scandir/out/settings.js -0 20 backend/node_modules/@nodelib/fs.scandir/out/types/index.d.ts -0 2 backend/node_modules/@nodelib/fs.scandir/out/types/index.js -0 2 backend/node_modules/@nodelib/fs.scandir/out/utils/fs.d.ts -0 19 backend/node_modules/@nodelib/fs.scandir/out/utils/fs.js -0 2 backend/node_modules/@nodelib/fs.scandir/out/utils/index.d.ts -0 5 backend/node_modules/@nodelib/fs.scandir/out/utils/index.js -0 44 backend/node_modules/@nodelib/fs.scandir/package.json -0 21 backend/node_modules/@nodelib/fs.stat/LICENSE -0 126 backend/node_modules/@nodelib/fs.stat/README.md -0 13 backend/node_modules/@nodelib/fs.stat/out/adapters/fs.d.ts -0 17 backend/node_modules/@nodelib/fs.stat/out/adapters/fs.js -0 12 backend/node_modules/@nodelib/fs.stat/out/index.d.ts -0 26 backend/node_modules/@nodelib/fs.stat/out/index.js -0 4 backend/node_modules/@nodelib/fs.stat/out/providers/async.d.ts -0 36 backend/node_modules/@nodelib/fs.stat/out/providers/async.js -0 3 backend/node_modules/@nodelib/fs.stat/out/providers/sync.d.ts -0 23 backend/node_modules/@nodelib/fs.stat/out/providers/sync.js -0 16 backend/node_modules/@nodelib/fs.stat/out/settings.d.ts -0 16 backend/node_modules/@nodelib/fs.stat/out/settings.js -0 4 backend/node_modules/@nodelib/fs.stat/out/types/index.d.ts -0 2 backend/node_modules/@nodelib/fs.stat/out/types/index.js -0 37 backend/node_modules/@nodelib/fs.stat/package.json -0 21 backend/node_modules/@nodelib/fs.walk/LICENSE -0 215 backend/node_modules/@nodelib/fs.walk/README.md -0 14 backend/node_modules/@nodelib/fs.walk/out/index.d.ts -0 34 backend/node_modules/@nodelib/fs.walk/out/index.js -0 12 backend/node_modules/@nodelib/fs.walk/out/providers/async.d.ts -0 30 backend/node_modules/@nodelib/fs.walk/out/providers/async.js -0 4 backend/node_modules/@nodelib/fs.walk/out/providers/index.d.ts -0 9 backend/node_modules/@nodelib/fs.walk/out/providers/index.js -0 12 backend/node_modules/@nodelib/fs.walk/out/providers/stream.d.ts -0 34 backend/node_modules/@nodelib/fs.walk/out/providers/stream.js -0 10 backend/node_modules/@nodelib/fs.walk/out/providers/sync.d.ts -0 14 backend/node_modules/@nodelib/fs.walk/out/providers/sync.js -0 30 backend/node_modules/@nodelib/fs.walk/out/readers/async.d.ts -0 97 backend/node_modules/@nodelib/fs.walk/out/readers/async.js -0 7 backend/node_modules/@nodelib/fs.walk/out/readers/common.d.ts -0 31 backend/node_modules/@nodelib/fs.walk/out/readers/common.js -0 6 backend/node_modules/@nodelib/fs.walk/out/readers/reader.d.ts -0 11 backend/node_modules/@nodelib/fs.walk/out/readers/reader.js -0 15 backend/node_modules/@nodelib/fs.walk/out/readers/sync.d.ts -0 59 backend/node_modules/@nodelib/fs.walk/out/readers/sync.js -0 30 backend/node_modules/@nodelib/fs.walk/out/settings.d.ts -0 26 backend/node_modules/@nodelib/fs.walk/out/settings.js -0 8 backend/node_modules/@nodelib/fs.walk/out/types/index.d.ts -0 2 backend/node_modules/@nodelib/fs.walk/out/types/index.js -0 44 backend/node_modules/@nodelib/fs.walk/package.json -0 9 backend/node_modules/@softarc/sheriff-core/README.md -0 23 backend/node_modules/@softarc/sheriff-core/package.json -0 8 backend/node_modules/@softarc/sheriff-core/src/bin/main.d.ts -0 12 backend/node_modules/@softarc/sheriff-core/src/bin/main.js -0 1 backend/node_modules/@softarc/sheriff-core/src/bin/main.js.map -0 8 backend/node_modules/@softarc/sheriff-core/src/index.d.ts -0 18 backend/node_modules/@softarc/sheriff-core/src/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/index.js.map -0 61 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.d.ts -0 51 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.d.ts -0 9 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.d.ts -0 7 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.js.map -0 9 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.d.ts -0 39 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.js.map -0 10 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.d.ts -0 45 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.d.ts -0 11 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.d.ts -0 34 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.d.ts -0 8 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.js.map -0 26 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.d.ts -0 30 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.js.map -0 7 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.d.ts -0 11 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.d.ts -0 17 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.d.ts -0 43 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.d.ts -0 28 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.js.map -0 7 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.d.ts -0 30 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.d.ts -0 64 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.d.ts -0 46 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.js.map -0 4 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.d.ts -0 43 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.d.ts -0 61 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.d.ts -0 69 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.js.map -0 7 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.d.ts -0 15 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.d.ts -0 14 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.js.map -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.d.ts -0 17 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.d.ts -0 46 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.js.map -0 4 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.js.map -0 20 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.js.map -0 119 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.js.map -0 26 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.d.ts -0 53 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.d.ts -0 43 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.d.ts -0 57 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.d.ts -0 18 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.js.map -0 16 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.d.ts -0 50 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.js.map -0 21 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.d.ts -0 74 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.js.map -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.d.ts -0 27 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.js.map -0 14 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.d.ts -0 80 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.js.map -0 30 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.d.ts -0 178 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.js.map -0 6 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.d.ts -0 19 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.js.map -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.js.map -0 28 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.d.ts -0 90 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.js.map -0 21 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.d.ts -0 103 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.d.ts -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.js.map -0 31 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.d.ts -0 37 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.js.map -0 6 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.d.ts -0 18 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.js.map -0 34 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.d.ts -0 228 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.d.ts -0 6 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.js.map -0 7 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.d.ts -0 48 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.d.ts -0 18 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.d.ts -0 14 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.d.ts -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.js.map -0 50 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.d.ts -0 40 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.d.ts -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.d.ts -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.js.map -0 11 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.d.ts -0 31 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.d.ts -0 40 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.js.map -0 25 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.d.ts -0 52 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.js.map -0 4 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.d.ts -0 15 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.d.ts -0 20 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.d.ts -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.d.ts -0 33 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.js.map -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.d.ts -0 27 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.d.ts -0 18 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.d.ts -0 155 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.d.ts -0 15 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.d.ts -0 15 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.d.ts -0 16 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.js.map -0 5 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.d.ts -0 17 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.d.ts -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.js.map -0 13 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.d.ts -0 44 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.js.map -0 24 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.d.ts -0 22 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.d.ts -0 77 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.d.ts -0 28 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.js.map -0 4 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.d.ts -0 17 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.js.map -0 3 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.d.ts -0 21 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.d.ts -0 11 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.d.ts -0 9 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.d.ts -0 8 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.js.map -0 2 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.d.ts -0 9 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.d.ts -0 14 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.js -0 1 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.js.map -0 1 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.d.ts -0 44 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.js -0 1 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.js.map -0 21 backend/node_modules/@tsconfig/node10/LICENSE -0 38 backend/node_modules/@tsconfig/node10/README.md -0 15 backend/node_modules/@tsconfig/node10/package.json -0 14 backend/node_modules/@tsconfig/node10/tsconfig.json -0 21 backend/node_modules/@tsconfig/node12/LICENSE -0 40 backend/node_modules/@tsconfig/node12/README.md -0 1 backend/node_modules/@tsconfig/node12/package.json -0 16 backend/node_modules/@tsconfig/node12/tsconfig.json -0 21 backend/node_modules/@tsconfig/node14/LICENSE -0 40 backend/node_modules/@tsconfig/node14/README.md -0 1 backend/node_modules/@tsconfig/node14/package.json -0 16 backend/node_modules/@tsconfig/node14/tsconfig.json -0 21 backend/node_modules/@tsconfig/node16/LICENSE -0 40 backend/node_modules/@tsconfig/node16/README.md -0 15 backend/node_modules/@tsconfig/node16/package.json -0 16 backend/node_modules/@tsconfig/node16/tsconfig.json -0 21 backend/node_modules/@types/body-parser/LICENSE -0 15 backend/node_modules/@types/body-parser/README.md -0 95 backend/node_modules/@types/body-parser/index.d.ts -0 58 backend/node_modules/@types/body-parser/package.json -0 21 backend/node_modules/@types/connect/LICENSE -0 15 backend/node_modules/@types/connect/README.md -0 91 backend/node_modules/@types/connect/index.d.ts -0 32 backend/node_modules/@types/connect/package.json -0 21 backend/node_modules/@types/express-serve-static-core/LICENSE -0 15 backend/node_modules/@types/express-serve-static-core/README.md -0 1295 backend/node_modules/@types/express-serve-static-core/index.d.ts -0 50 backend/node_modules/@types/express-serve-static-core/package.json -0 21 backend/node_modules/@types/express/LICENSE -0 15 backend/node_modules/@types/express/README.md -0 128 backend/node_modules/@types/express/index.d.ts -0 45 backend/node_modules/@types/express/package.json -0 21 backend/node_modules/@types/http-errors/LICENSE -0 15 backend/node_modules/@types/http-errors/README.md -0 77 backend/node_modules/@types/http-errors/index.d.ts -0 30 backend/node_modules/@types/http-errors/package.json -0 21 backend/node_modules/@types/mime/LICENSE -0 10 backend/node_modules/@types/mime/Mime.d.ts -0 15 backend/node_modules/@types/mime/README.md -0 31 backend/node_modules/@types/mime/index.d.ts -0 7 backend/node_modules/@types/mime/lite.d.ts -0 30 backend/node_modules/@types/mime/package.json -0 21 backend/node_modules/@types/node/LICENSE -0 15 backend/node_modules/@types/node/README.md -0 1040 backend/node_modules/@types/node/assert.d.ts -0 8 backend/node_modules/@types/node/assert/strict.d.ts -0 541 backend/node_modules/@types/node/async_hooks.d.ts -0 2293 backend/node_modules/@types/node/buffer.d.ts -0 1544 backend/node_modules/@types/node/child_process.d.ts -0 578 backend/node_modules/@types/node/cluster.d.ts -0 452 backend/node_modules/@types/node/console.d.ts -0 19 backend/node_modules/@types/node/constants.d.ts -0 4451 backend/node_modules/@types/node/crypto.d.ts -0 596 backend/node_modules/@types/node/dgram.d.ts -0 554 backend/node_modules/@types/node/diagnostics_channel.d.ts -0 865 backend/node_modules/@types/node/dns.d.ts -0 476 backend/node_modules/@types/node/dns/promises.d.ts -0 124 backend/node_modules/@types/node/dom-events.d.ts -0 170 backend/node_modules/@types/node/domain.d.ts -0 931 backend/node_modules/@types/node/events.d.ts -0 4390 backend/node_modules/@types/node/fs.d.ts -0 1264 backend/node_modules/@types/node/fs/promises.d.ts -0 433 backend/node_modules/@types/node/globals.d.ts -0 1 backend/node_modules/@types/node/globals.global.d.ts -0 1908 backend/node_modules/@types/node/http.d.ts -0 2418 backend/node_modules/@types/node/http2.d.ts -0 550 backend/node_modules/@types/node/https.d.ts -0 89 backend/node_modules/@types/node/index.d.ts -0 2746 backend/node_modules/@types/node/inspector.d.ts -0 301 backend/node_modules/@types/node/module.d.ts -0 999 backend/node_modules/@types/node/net.d.ts -0 495 backend/node_modules/@types/node/os.d.ts -0 217 backend/node_modules/@types/node/package.json -0 191 backend/node_modules/@types/node/path.d.ts -0 941 backend/node_modules/@types/node/perf_hooks.d.ts -0 1873 backend/node_modules/@types/node/process.d.ts -0 117 backend/node_modules/@types/node/punycode.d.ts -0 153 backend/node_modules/@types/node/querystring.d.ts -0 540 backend/node_modules/@types/node/readline.d.ts -0 150 backend/node_modules/@types/node/readline/promises.d.ts -0 430 backend/node_modules/@types/node/repl.d.ts -0 153 backend/node_modules/@types/node/sea.d.ts -0 1707 backend/node_modules/@types/node/stream.d.ts -0 12 backend/node_modules/@types/node/stream/consumers.d.ts -0 83 backend/node_modules/@types/node/stream/promises.d.ts -0 367 backend/node_modules/@types/node/stream/web.d.ts -0 67 backend/node_modules/@types/node/string_decoder.d.ts -0 1990 backend/node_modules/@types/node/test.d.ts -0 240 backend/node_modules/@types/node/timers.d.ts -0 97 backend/node_modules/@types/node/timers/promises.d.ts -0 1217 backend/node_modules/@types/node/tls.d.ts -0 197 backend/node_modules/@types/node/trace_events.d.ts -0 208 backend/node_modules/@types/node/tty.d.ts -0 969 backend/node_modules/@types/node/url.d.ts -0 2292 backend/node_modules/@types/node/util.d.ts -0 808 backend/node_modules/@types/node/v8.d.ts -0 922 backend/node_modules/@types/node/vm.d.ts -0 181 backend/node_modules/@types/node/wasi.d.ts -0 694 backend/node_modules/@types/node/worker_threads.d.ts -0 539 backend/node_modules/@types/node/zlib.d.ts -0 21 backend/node_modules/@types/qs/LICENSE -0 15 backend/node_modules/@types/qs/README.md -0 79 backend/node_modules/@types/qs/index.d.ts -0 65 backend/node_modules/@types/qs/package.json -0 21 backend/node_modules/@types/range-parser/LICENSE -0 53 backend/node_modules/@types/range-parser/README.md -0 34 backend/node_modules/@types/range-parser/index.d.ts -0 25 backend/node_modules/@types/range-parser/package.json -0 21 backend/node_modules/@types/send/LICENSE -0 15 backend/node_modules/@types/send/README.md -0 225 backend/node_modules/@types/send/index.d.ts -0 33 backend/node_modules/@types/send/package.json -0 21 backend/node_modules/@types/serve-static/LICENSE -0 15 backend/node_modules/@types/serve-static/README.md -0 107 backend/node_modules/@types/serve-static/index.d.ts -0 39 backend/node_modules/@types/serve-static/package.json -0 17 backend/node_modules/@types/strip-bom/README.md -0 7 backend/node_modules/@types/strip-bom/index.d.ts -0 17 backend/node_modules/@types/strip-bom/package.json -0 22 backend/node_modules/@types/strip-bom/types-metadata.json -0 21 backend/node_modules/@types/strip-json-comments/LICENSE -0 16 backend/node_modules/@types/strip-json-comments/README.md -0 13 backend/node_modules/@types/strip-json-comments/index.d.ts -0 21 backend/node_modules/@types/strip-json-comments/package.json -0 243 backend/node_modules/accepts/HISTORY.md -0 23 backend/node_modules/accepts/LICENSE -0 140 backend/node_modules/accepts/README.md -0 238 backend/node_modules/accepts/index.js -0 47 backend/node_modules/accepts/package.json -0 193 backend/node_modules/acorn-walk/CHANGELOG.md -0 21 backend/node_modules/acorn-walk/LICENSE -0 124 backend/node_modules/acorn-walk/README.md -0 177 backend/node_modules/acorn-walk/dist/walk.d.mts -0 177 backend/node_modules/acorn-walk/dist/walk.d.ts -0 461 backend/node_modules/acorn-walk/dist/walk.js -0 443 backend/node_modules/acorn-walk/dist/walk.mjs -0 50 backend/node_modules/acorn-walk/package.json -0 910 backend/node_modules/acorn/CHANGELOG.md -0 21 backend/node_modules/acorn/LICENSE -0 282 backend/node_modules/acorn/README.md -0 4 backend/node_modules/acorn/bin/acorn -0 856 backend/node_modules/acorn/dist/acorn.d.mts -0 856 backend/node_modules/acorn/dist/acorn.d.ts -0 6065 backend/node_modules/acorn/dist/acorn.js -0 6036 backend/node_modules/acorn/dist/acorn.mjs -0 90 backend/node_modules/acorn/dist/bin.js -0 50 backend/node_modules/acorn/package.json -0 15 backend/node_modules/anymatch/LICENSE -0 87 backend/node_modules/anymatch/README.md -0 20 backend/node_modules/anymatch/index.d.ts -0 104 backend/node_modules/anymatch/index.js -0 48 backend/node_modules/anymatch/package.json -0 21 backend/node_modules/arg/LICENSE.md -0 280 backend/node_modules/arg/README.md -0 31 backend/node_modules/arg/index.d.ts -0 144 backend/node_modules/arg/index.js -0 30 backend/node_modules/arg/package.json -0 21 backend/node_modules/array-flatten/LICENSE -0 43 backend/node_modules/array-flatten/README.md -0 64 backend/node_modules/array-flatten/array-flatten.js -0 39 backend/node_modules/array-flatten/package.json -0 2 backend/node_modules/balanced-match/.github/FUNDING.yml -0 21 backend/node_modules/balanced-match/LICENSE.md -0 97 backend/node_modules/balanced-match/README.md -0 62 backend/node_modules/balanced-match/index.js -0 48 backend/node_modules/balanced-match/package.json -0 263 backend/node_modules/binary-extensions/binary-extensions.json -0 3 backend/node_modules/binary-extensions/binary-extensions.json.d.ts -0 14 backend/node_modules/binary-extensions/index.d.ts -0 1 backend/node_modules/binary-extensions/index.js -0 10 backend/node_modules/binary-extensions/license -0 40 backend/node_modules/binary-extensions/package.json -0 25 backend/node_modules/binary-extensions/readme.md -0 665 backend/node_modules/body-parser/HISTORY.md -0 23 backend/node_modules/body-parser/LICENSE -0 465 backend/node_modules/body-parser/README.md -0 25 backend/node_modules/body-parser/SECURITY.md -0 156 backend/node_modules/body-parser/index.js -0 205 backend/node_modules/body-parser/lib/read.js -0 247 backend/node_modules/body-parser/lib/types/json.js -0 101 backend/node_modules/body-parser/lib/types/raw.js -0 121 backend/node_modules/body-parser/lib/types/text.js -0 284 backend/node_modules/body-parser/lib/types/urlencoded.js -0 56 backend/node_modules/body-parser/package.json -0 21 backend/node_modules/brace-expansion/LICENSE -0 129 backend/node_modules/brace-expansion/README.md -0 201 backend/node_modules/brace-expansion/index.js -0 47 backend/node_modules/brace-expansion/package.json -0 21 backend/node_modules/braces/LICENSE -0 586 backend/node_modules/braces/README.md -0 170 backend/node_modules/braces/index.js -0 60 backend/node_modules/braces/lib/compile.js -0 57 backend/node_modules/braces/lib/constants.js -0 113 backend/node_modules/braces/lib/expand.js -0 331 backend/node_modules/braces/lib/parse.js -0 32 backend/node_modules/braces/lib/stringify.js -0 122 backend/node_modules/braces/lib/utils.js -0 77 backend/node_modules/braces/package.json -0 21 backend/node_modules/buffer-from/LICENSE -0 72 backend/node_modules/buffer-from/index.js -0 19 backend/node_modules/buffer-from/package.json -0 69 backend/node_modules/buffer-from/readme.md -0 97 backend/node_modules/bytes/History.md -0 23 backend/node_modules/bytes/LICENSE -0 152 backend/node_modules/bytes/Readme.md -0 170 backend/node_modules/bytes/index.js -0 42 backend/node_modules/bytes/package.json -0 1 backend/node_modules/call-bind/.eslintignore -0 16 backend/node_modules/call-bind/.eslintrc -0 12 backend/node_modules/call-bind/.github/FUNDING.yml -0 9 backend/node_modules/call-bind/.nycrc -0 93 backend/node_modules/call-bind/CHANGELOG.md -0 21 backend/node_modules/call-bind/LICENSE -0 64 backend/node_modules/call-bind/README.md -0 15 backend/node_modules/call-bind/callBound.js -0 35 backend/node_modules/call-bind/index.js -0 95 backend/node_modules/call-bind/package.json -0 54 backend/node_modules/call-bind/test/callBound.js -0 80 backend/node_modules/call-bind/test/index.js -0 21 backend/node_modules/chokidar/LICENSE -0 308 backend/node_modules/chokidar/README.md -0 973 backend/node_modules/chokidar/index.js -0 66 backend/node_modules/chokidar/lib/constants.js -0 526 backend/node_modules/chokidar/lib/fsevents-handler.js -0 654 backend/node_modules/chokidar/lib/nodefs-handler.js -0 70 backend/node_modules/chokidar/package.json -0 192 backend/node_modules/chokidar/types/index.d.ts -0 4 backend/node_modules/concat-map/.travis.yml -0 18 backend/node_modules/concat-map/LICENSE -0 62 backend/node_modules/concat-map/README.markdown -0 6 backend/node_modules/concat-map/example/map.js -0 13 backend/node_modules/concat-map/index.js -0 43 backend/node_modules/concat-map/package.json -0 39 backend/node_modules/concat-map/test/map.js -0 60 backend/node_modules/content-disposition/HISTORY.md -0 22 backend/node_modules/content-disposition/LICENSE -0 142 backend/node_modules/content-disposition/README.md -0 458 backend/node_modules/content-disposition/index.js -0 44 backend/node_modules/content-disposition/package.json -0 29 backend/node_modules/content-type/HISTORY.md -0 22 backend/node_modules/content-type/LICENSE -0 94 backend/node_modules/content-type/README.md -0 225 backend/node_modules/content-type/index.js -0 42 backend/node_modules/content-type/package.json -0 4 backend/node_modules/cookie-signature/.npmignore -0 38 backend/node_modules/cookie-signature/History.md -0 42 backend/node_modules/cookie-signature/Readme.md -0 51 backend/node_modules/cookie-signature/index.js -0 18 backend/node_modules/cookie-signature/package.json -0 147 backend/node_modules/cookie/HISTORY.md -0 24 backend/node_modules/cookie/LICENSE -0 317 backend/node_modules/cookie/README.md -0 25 backend/node_modules/cookie/SECURITY.md -0 274 backend/node_modules/cookie/index.js -0 44 backend/node_modules/cookie/package.json -0 33 backend/node_modules/create-require/CHANGELOG.md -0 25 backend/node_modules/create-require/LICENSE -0 46 backend/node_modules/create-require/README.md -0 3 backend/node_modules/create-require/create-require.d.ts -0 49 backend/node_modules/create-require/create-require.js -0 39 backend/node_modules/create-require/package.json -0 1 backend/node_modules/debug/.coveralls.yml -0 11 backend/node_modules/debug/.eslintrc -0 9 backend/node_modules/debug/.npmignore -0 14 backend/node_modules/debug/.travis.yml -0 362 backend/node_modules/debug/CHANGELOG.md -0 19 backend/node_modules/debug/LICENSE -0 50 backend/node_modules/debug/Makefile -0 312 backend/node_modules/debug/README.md -0 19 backend/node_modules/debug/component.json -0 70 backend/node_modules/debug/karma.conf.js -0 1 backend/node_modules/debug/node.js -0 49 backend/node_modules/debug/package.json -0 185 backend/node_modules/debug/src/browser.js -0 202 backend/node_modules/debug/src/debug.js -0 10 backend/node_modules/debug/src/index.js -0 15 backend/node_modules/debug/src/inspector-log.js -0 248 backend/node_modules/debug/src/node.js -0 24 backend/node_modules/define-data-property/.eslintrc -0 12 backend/node_modules/define-data-property/.github/FUNDING.yml -0 13 backend/node_modules/define-data-property/.nycrc -0 70 backend/node_modules/define-data-property/CHANGELOG.md -0 21 backend/node_modules/define-data-property/LICENSE -0 67 backend/node_modules/define-data-property/README.md -0 12 backend/node_modules/define-data-property/index.d.ts -0 56 backend/node_modules/define-data-property/index.js -0 106 backend/node_modules/define-data-property/package.json -0 392 backend/node_modules/define-data-property/test/index.js -0 59 backend/node_modules/define-data-property/tsconfig.json -0 103 backend/node_modules/depd/History.md -0 22 backend/node_modules/depd/LICENSE -0 280 backend/node_modules/depd/Readme.md -0 538 backend/node_modules/depd/index.js -0 77 backend/node_modules/depd/lib/browser/index.js -0 45 backend/node_modules/depd/package.json -0 23 backend/node_modules/destroy/LICENSE -0 63 backend/node_modules/destroy/README.md -0 209 backend/node_modules/destroy/index.js -0 48 backend/node_modules/destroy/package.json -0 39 backend/node_modules/diff/CONTRIBUTING.md -0 31 backend/node_modules/diff/LICENSE -0 207 backend/node_modules/diff/README.md -0 1585 backend/node_modules/diff/dist/diff.js -0 38 backend/node_modules/diff/dist/diff.min.js -0 32 backend/node_modules/diff/lib/convert/dmp.js -0 42 backend/node_modules/diff/lib/convert/xml.js -0 45 backend/node_modules/diff/lib/diff/array.js -0 304 backend/node_modules/diff/lib/diff/base.js -0 37 backend/node_modules/diff/lib/diff/character.js -0 41 backend/node_modules/diff/lib/diff/css.js -0 163 backend/node_modules/diff/lib/diff/json.js -0 89 backend/node_modules/diff/lib/diff/line.js -0 41 backend/node_modules/diff/lib/diff/sentence.js -0 107 backend/node_modules/diff/lib/diff/word.js -0 1519 backend/node_modules/diff/lib/index.es6.js -0 216 backend/node_modules/diff/lib/index.js -0 243 backend/node_modules/diff/lib/patch/apply.js -0 247 backend/node_modules/diff/lib/patch/create.js -0 609 backend/node_modules/diff/lib/patch/merge.js -0 156 backend/node_modules/diff/lib/patch/parse.js -0 32 backend/node_modules/diff/lib/util/array.js -0 57 backend/node_modules/diff/lib/util/distance-iterator.js -0 24 backend/node_modules/diff/lib/util/params.js -0 73 backend/node_modules/diff/package.json -0 261 backend/node_modules/diff/release-notes.md -0 3 backend/node_modules/diff/runtime.js -0 10 backend/node_modules/dynamic-dedupe/.jshintrc -0 15 backend/node_modules/dynamic-dedupe/.npmignore -0 23 backend/node_modules/dynamic-dedupe/LICENSE -0 110 backend/node_modules/dynamic-dedupe/README.md -0 10 backend/node_modules/dynamic-dedupe/example/deduped.js -0 7 backend/node_modules/dynamic-dedupe/example/not-deduped.js -0 4 backend/node_modules/dynamic-dedupe/example/pack1/common/dep-uno/foo.js -0 4 backend/node_modules/dynamic-dedupe/example/pack2/common/dep-uno/foo.js -0 80 backend/node_modules/dynamic-dedupe/index.js -0 44 backend/node_modules/dynamic-dedupe/package.json -0 101 backend/node_modules/dynamic-dedupe/test/dedupe.js -0 1 backend/node_modules/dynamic-dedupe/test/fixtures/count.js -0 4 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-dos/foo.js -0 4 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-uno/bar.js -0 4 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-uno/foo.js -0 4 backend/node_modules/dynamic-dedupe/test/fixtures/pack2/common/dep-uno/bar.js -0 4 backend/node_modules/dynamic-dedupe/test/fixtures/pack2/common/dep-uno/foo.js -0 22 backend/node_modules/ee-first/LICENSE -0 80 backend/node_modules/ee-first/README.md -0 95 backend/node_modules/ee-first/index.js -0 29 backend/node_modules/ee-first/package.json -0 14 backend/node_modules/encodeurl/HISTORY.md -0 22 backend/node_modules/encodeurl/LICENSE -0 128 backend/node_modules/encodeurl/README.md -0 60 backend/node_modules/encodeurl/index.js -0 40 backend/node_modules/encodeurl/package.json -0 13 backend/node_modules/es-define-property/.eslintrc -0 12 backend/node_modules/es-define-property/.github/FUNDING.yml -0 9 backend/node_modules/es-define-property/.nycrc -0 15 backend/node_modules/es-define-property/CHANGELOG.md -0 21 backend/node_modules/es-define-property/LICENSE -0 49 backend/node_modules/es-define-property/README.md -0 3 backend/node_modules/es-define-property/index.d.ts -0 16 backend/node_modules/es-define-property/index.js -0 81 backend/node_modules/es-define-property/package.json -0 55 backend/node_modules/es-define-property/test/index.js -0 50 backend/node_modules/es-define-property/tsconfig.json -0 5 backend/node_modules/es-errors/.eslintrc -0 12 backend/node_modules/es-errors/.github/FUNDING.yml -0 40 backend/node_modules/es-errors/CHANGELOG.md -0 21 backend/node_modules/es-errors/LICENSE -0 55 backend/node_modules/es-errors/README.md -0 3 backend/node_modules/es-errors/eval.d.ts -0 4 backend/node_modules/es-errors/eval.js -0 3 backend/node_modules/es-errors/index.d.ts -0 4 backend/node_modules/es-errors/index.js -0 80 backend/node_modules/es-errors/package.json -0 3 backend/node_modules/es-errors/range.d.ts -0 4 backend/node_modules/es-errors/range.js -0 3 backend/node_modules/es-errors/ref.d.ts -0 4 backend/node_modules/es-errors/ref.js -0 3 backend/node_modules/es-errors/syntax.d.ts -0 4 backend/node_modules/es-errors/syntax.js -0 19 backend/node_modules/es-errors/test/index.js -0 49 backend/node_modules/es-errors/tsconfig.json -0 3 backend/node_modules/es-errors/type.d.ts -0 4 backend/node_modules/es-errors/type.js -0 3 backend/node_modules/es-errors/uri.d.ts -0 4 backend/node_modules/es-errors/uri.js -0 24 backend/node_modules/escape-html/LICENSE -0 43 backend/node_modules/escape-html/Readme.md -0 78 backend/node_modules/escape-html/index.js -0 24 backend/node_modules/escape-html/package.json -0 83 backend/node_modules/etag/HISTORY.md -0 22 backend/node_modules/etag/LICENSE -0 159 backend/node_modules/etag/README.md -0 131 backend/node_modules/etag/index.js -0 47 backend/node_modules/etag/package.json -0 3615 backend/node_modules/express/History.md -0 24 backend/node_modules/express/LICENSE -0 166 backend/node_modules/express/Readme.md -0 11 backend/node_modules/express/index.js -0 661 backend/node_modules/express/lib/application.js -0 116 backend/node_modules/express/lib/express.js -0 43 backend/node_modules/express/lib/middleware/init.js -0 47 backend/node_modules/express/lib/middleware/query.js -0 525 backend/node_modules/express/lib/request.js -0 1178 backend/node_modules/express/lib/response.js -0 673 backend/node_modules/express/lib/router/index.js -0 181 backend/node_modules/express/lib/router/layer.js -0 230 backend/node_modules/express/lib/router/route.js -0 303 backend/node_modules/express/lib/utils.js -0 182 backend/node_modules/express/lib/view.js -0 98 backend/node_modules/express/package.json -0 21 backend/node_modules/fast-glob/LICENSE -0 830 backend/node_modules/fast-glob/README.md -0 40 backend/node_modules/fast-glob/out/index.d.ts -0 102 backend/node_modules/fast-glob/out/index.js -0 22 backend/node_modules/fast-glob/out/managers/tasks.d.ts -0 110 backend/node_modules/fast-glob/out/managers/tasks.js -0 9 backend/node_modules/fast-glob/out/providers/async.d.ts -0 23 backend/node_modules/fast-glob/out/providers/async.js -0 16 backend/node_modules/fast-glob/out/providers/filters/deep.d.ts -0 62 backend/node_modules/fast-glob/out/providers/filters/deep.js -0 16 backend/node_modules/fast-glob/out/providers/filters/entry.d.ts -0 63 backend/node_modules/fast-glob/out/providers/filters/entry.js -0 8 backend/node_modules/fast-glob/out/providers/filters/error.d.ts -0 15 backend/node_modules/fast-glob/out/providers/filters/error.js -0 33 backend/node_modules/fast-glob/out/providers/matchers/matcher.d.ts -0 45 backend/node_modules/fast-glob/out/providers/matchers/matcher.js -0 4 backend/node_modules/fast-glob/out/providers/matchers/partial.d.ts -0 38 backend/node_modules/fast-glob/out/providers/matchers/partial.js -0 19 backend/node_modules/fast-glob/out/providers/provider.d.ts -0 48 backend/node_modules/fast-glob/out/providers/provider.js -0 11 backend/node_modules/fast-glob/out/providers/stream.d.ts -0 31 backend/node_modules/fast-glob/out/providers/stream.js -0 9 backend/node_modules/fast-glob/out/providers/sync.d.ts -0 23 backend/node_modules/fast-glob/out/providers/sync.js -0 8 backend/node_modules/fast-glob/out/providers/transformers/entry.d.ts -0 26 backend/node_modules/fast-glob/out/providers/transformers/entry.js -0 10 backend/node_modules/fast-glob/out/readers/async.d.ts -0 35 backend/node_modules/fast-glob/out/readers/async.js -0 15 backend/node_modules/fast-glob/out/readers/reader.d.ts -0 33 backend/node_modules/fast-glob/out/readers/reader.js -0 14 backend/node_modules/fast-glob/out/readers/stream.d.ts -0 55 backend/node_modules/fast-glob/out/readers/stream.js -0 12 backend/node_modules/fast-glob/out/readers/sync.d.ts -0 43 backend/node_modules/fast-glob/out/readers/sync.js -0 164 backend/node_modules/fast-glob/out/settings.d.ts -0 59 backend/node_modules/fast-glob/out/settings.js -0 31 backend/node_modules/fast-glob/out/types/index.d.ts -0 2 backend/node_modules/fast-glob/out/types/index.js -0 2 backend/node_modules/fast-glob/out/utils/array.d.ts -0 22 backend/node_modules/fast-glob/out/utils/array.js -0 2 backend/node_modules/fast-glob/out/utils/errno.d.ts -0 7 backend/node_modules/fast-glob/out/utils/errno.js -0 4 backend/node_modules/fast-glob/out/utils/fs.d.ts -0 19 backend/node_modules/fast-glob/out/utils/fs.js -0 8 backend/node_modules/fast-glob/out/utils/index.d.ts -0 17 backend/node_modules/fast-glob/out/utils/index.js -0 13 backend/node_modules/fast-glob/out/utils/path.d.ts -0 68 backend/node_modules/fast-glob/out/utils/path.js -0 47 backend/node_modules/fast-glob/out/utils/pattern.d.ts -0 188 backend/node_modules/fast-glob/out/utils/pattern.js -0 4 backend/node_modules/fast-glob/out/utils/stream.d.ts -0 17 backend/node_modules/fast-glob/out/utils/stream.js -0 2 backend/node_modules/fast-glob/out/utils/string.d.ts -0 11 backend/node_modules/fast-glob/out/utils/string.js -0 81 backend/node_modules/fast-glob/package.json -0 11 backend/node_modules/fastq/.github/dependabot.yml -0 75 backend/node_modules/fastq/.github/workflows/ci.yml -0 13 backend/node_modules/fastq/LICENSE -0 306 backend/node_modules/fastq/README.md -0 66 backend/node_modules/fastq/bench.js -0 14 backend/node_modules/fastq/example.js -0 11 backend/node_modules/fastq/example.mjs -0 38 backend/node_modules/fastq/index.d.ts -0 53 backend/node_modules/fastq/package.json -0 311 backend/node_modules/fastq/queue.js -0 83 backend/node_modules/fastq/test/example.ts -0 248 backend/node_modules/fastq/test/promise.js -0 642 backend/node_modules/fastq/test/test.js -0 11 backend/node_modules/fastq/test/tsconfig.json -0 21 backend/node_modules/fill-range/LICENSE -0 237 backend/node_modules/fill-range/README.md -0 248 backend/node_modules/fill-range/index.js -0 74 backend/node_modules/fill-range/package.json -0 195 backend/node_modules/finalhandler/HISTORY.md -0 22 backend/node_modules/finalhandler/LICENSE -0 147 backend/node_modules/finalhandler/README.md -0 25 backend/node_modules/finalhandler/SECURITY.md -0 336 backend/node_modules/finalhandler/index.js -0 46 backend/node_modules/finalhandler/package.json -0 21 backend/node_modules/forwarded/HISTORY.md -0 22 backend/node_modules/forwarded/LICENSE -0 57 backend/node_modules/forwarded/README.md -0 90 backend/node_modules/forwarded/index.js -0 45 backend/node_modules/forwarded/package.json -0 70 backend/node_modules/fresh/HISTORY.md -0 23 backend/node_modules/fresh/LICENSE -0 119 backend/node_modules/fresh/README.md -0 137 backend/node_modules/fresh/index.js -0 46 backend/node_modules/fresh/package.json -0 43 backend/node_modules/fs.realpath/LICENSE -0 33 backend/node_modules/fs.realpath/README.md -0 66 backend/node_modules/fs.realpath/index.js -0 303 backend/node_modules/fs.realpath/old.js -0 26 backend/node_modules/fs.realpath/package.json -0 22 backend/node_modules/fsevents/LICENSE -0 89 backend/node_modules/fsevents/README.md -0 46 backend/node_modules/fsevents/fsevents.d.ts -0 83 backend/node_modules/fsevents/fsevents.js -- - backend/node_modules/fsevents/fsevents.node -0 62 backend/node_modules/fsevents/package.json -0 21 backend/node_modules/function-bind/.eslintrc -0 12 backend/node_modules/function-bind/.github/FUNDING.yml -0 3 backend/node_modules/function-bind/.github/SECURITY.md -0 13 backend/node_modules/function-bind/.nycrc -0 136 backend/node_modules/function-bind/CHANGELOG.md -0 20 backend/node_modules/function-bind/LICENSE -0 46 backend/node_modules/function-bind/README.md -0 84 backend/node_modules/function-bind/implementation.js -0 5 backend/node_modules/function-bind/index.js -0 87 backend/node_modules/function-bind/package.json -0 9 backend/node_modules/function-bind/test/.eslintrc -0 252 backend/node_modules/function-bind/test/index.js -0 38 backend/node_modules/get-intrinsic/.eslintrc -0 12 backend/node_modules/get-intrinsic/.github/FUNDING.yml -0 9 backend/node_modules/get-intrinsic/.nycrc -0 143 backend/node_modules/get-intrinsic/CHANGELOG.md -0 21 backend/node_modules/get-intrinsic/LICENSE -0 71 backend/node_modules/get-intrinsic/README.md -0 359 backend/node_modules/get-intrinsic/index.js -0 93 backend/node_modules/get-intrinsic/package.json -0 274 backend/node_modules/get-intrinsic/test/GetIntrinsic.js -0 110 backend/node_modules/glob-parent/CHANGELOG.md -0 15 backend/node_modules/glob-parent/LICENSE -0 137 backend/node_modules/glob-parent/README.md -0 42 backend/node_modules/glob-parent/index.js -0 48 backend/node_modules/glob-parent/package.json -0 21 backend/node_modules/glob/LICENSE -0 378 backend/node_modules/glob/README.md -0 238 backend/node_modules/glob/common.js -0 790 backend/node_modules/glob/glob.js -0 55 backend/node_modules/glob/package.json -0 486 backend/node_modules/glob/sync.js -0 16 backend/node_modules/gopd/.eslintrc -0 12 backend/node_modules/gopd/.github/FUNDING.yml -0 25 backend/node_modules/gopd/CHANGELOG.md -0 21 backend/node_modules/gopd/LICENSE -0 40 backend/node_modules/gopd/README.md -0 16 backend/node_modules/gopd/index.js -0 71 backend/node_modules/gopd/package.json -0 35 backend/node_modules/gopd/test/index.js -0 13 backend/node_modules/has-property-descriptors/.eslintrc -0 12 backend/node_modules/has-property-descriptors/.github/FUNDING.yml -0 9 backend/node_modules/has-property-descriptors/.nycrc -0 35 backend/node_modules/has-property-descriptors/CHANGELOG.md -0 21 backend/node_modules/has-property-descriptors/LICENSE -0 43 backend/node_modules/has-property-descriptors/README.md -0 22 backend/node_modules/has-property-descriptors/index.js -0 77 backend/node_modules/has-property-descriptors/package.json -0 57 backend/node_modules/has-property-descriptors/test/index.js -0 5 backend/node_modules/has-proto/.eslintrc -0 12 backend/node_modules/has-proto/.github/FUNDING.yml -0 38 backend/node_modules/has-proto/CHANGELOG.md -0 21 backend/node_modules/has-proto/LICENSE -0 38 backend/node_modules/has-proto/README.md -0 3 backend/node_modules/has-proto/index.d.ts -0 15 backend/node_modules/has-proto/index.js -0 78 backend/node_modules/has-proto/package.json -0 19 backend/node_modules/has-proto/test/index.js -0 49 backend/node_modules/has-proto/tsconfig.json -0 11 backend/node_modules/has-symbols/.eslintrc -0 12 backend/node_modules/has-symbols/.github/FUNDING.yml -0 9 backend/node_modules/has-symbols/.nycrc -0 75 backend/node_modules/has-symbols/CHANGELOG.md -0 21 backend/node_modules/has-symbols/LICENSE -0 46 backend/node_modules/has-symbols/README.md -0 13 backend/node_modules/has-symbols/index.js -0 101 backend/node_modules/has-symbols/package.json -0 42 backend/node_modules/has-symbols/shams.js -0 22 backend/node_modules/has-symbols/test/index.js -0 28 backend/node_modules/has-symbols/test/shams/core-js.js -0 28 backend/node_modules/has-symbols/test/shams/get-own-property-symbols.js -0 56 backend/node_modules/has-symbols/test/tests.js -0 5 backend/node_modules/hasown/.eslintrc -0 12 backend/node_modules/hasown/.github/FUNDING.yml -0 13 backend/node_modules/hasown/.nycrc -0 40 backend/node_modules/hasown/CHANGELOG.md -0 21 backend/node_modules/hasown/LICENSE -0 40 backend/node_modules/hasown/README.md -0 3 backend/node_modules/hasown/index.d.ts -0 8 backend/node_modules/hasown/index.js -0 92 backend/node_modules/hasown/package.json -0 6 backend/node_modules/hasown/tsconfig.json -0 180 backend/node_modules/http-errors/HISTORY.md -0 23 backend/node_modules/http-errors/LICENSE -0 169 backend/node_modules/http-errors/README.md -0 289 backend/node_modules/http-errors/index.js -0 50 backend/node_modules/http-errors/package.json -0 162 backend/node_modules/iconv-lite/Changelog.md -0 21 backend/node_modules/iconv-lite/LICENSE -0 156 backend/node_modules/iconv-lite/README.md -0 555 backend/node_modules/iconv-lite/encodings/dbcs-codec.js -0 176 backend/node_modules/iconv-lite/encodings/dbcs-data.js -0 22 backend/node_modules/iconv-lite/encodings/index.js -0 188 backend/node_modules/iconv-lite/encodings/internal.js -0 72 backend/node_modules/iconv-lite/encodings/sbcs-codec.js -0 451 backend/node_modules/iconv-lite/encodings/sbcs-data-generated.js -0 174 backend/node_modules/iconv-lite/encodings/sbcs-data.js -0 122 backend/node_modules/iconv-lite/encodings/tables/big5-added.json -0 264 backend/node_modules/iconv-lite/encodings/tables/cp936.json -0 273 backend/node_modules/iconv-lite/encodings/tables/cp949.json -0 177 backend/node_modules/iconv-lite/encodings/tables/cp950.json -0 182 backend/node_modules/iconv-lite/encodings/tables/eucjp.json -0 1 backend/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json -0 55 backend/node_modules/iconv-lite/encodings/tables/gbk-added.json -0 125 backend/node_modules/iconv-lite/encodings/tables/shiftjis.json -0 177 backend/node_modules/iconv-lite/encodings/utf16.js -0 290 backend/node_modules/iconv-lite/encodings/utf7.js -0 52 backend/node_modules/iconv-lite/lib/bom-handling.js -0 217 backend/node_modules/iconv-lite/lib/extend-node.js -0 24 backend/node_modules/iconv-lite/lib/index.d.ts -0 153 backend/node_modules/iconv-lite/lib/index.js -0 121 backend/node_modules/iconv-lite/lib/streams.js -0 46 backend/node_modules/iconv-lite/package.json -0 15 backend/node_modules/inflight/LICENSE -0 37 backend/node_modules/inflight/README.md -0 54 backend/node_modules/inflight/inflight.js -0 29 backend/node_modules/inflight/package.json -0 16 backend/node_modules/inherits/LICENSE -0 42 backend/node_modules/inherits/README.md -0 9 backend/node_modules/inherits/inherits.js -0 27 backend/node_modules/inherits/inherits_browser.js -0 29 backend/node_modules/inherits/package.json -0 19 backend/node_modules/ipaddr.js/LICENSE -0 233 backend/node_modules/ipaddr.js/README.md -0 1 backend/node_modules/ipaddr.js/ipaddr.min.js -0 673 backend/node_modules/ipaddr.js/lib/ipaddr.js -0 68 backend/node_modules/ipaddr.js/lib/ipaddr.js.d.ts -0 35 backend/node_modules/ipaddr.js/package.json -0 17 backend/node_modules/is-binary-path/index.d.ts -0 7 backend/node_modules/is-binary-path/index.js -0 9 backend/node_modules/is-binary-path/license -0 40 backend/node_modules/is-binary-path/package.json -0 34 backend/node_modules/is-binary-path/readme.md -0 18 backend/node_modules/is-core-module/.eslintrc -0 9 backend/node_modules/is-core-module/.nycrc -0 195 backend/node_modules/is-core-module/CHANGELOG.md -0 20 backend/node_modules/is-core-module/LICENSE -0 40 backend/node_modules/is-core-module/README.md -0 161 backend/node_modules/is-core-module/core.json -0 69 backend/node_modules/is-core-module/index.js -0 76 backend/node_modules/is-core-module/package.json -0 138 backend/node_modules/is-core-module/test/index.js -0 21 backend/node_modules/is-extglob/LICENSE -0 107 backend/node_modules/is-extglob/README.md -0 20 backend/node_modules/is-extglob/index.js -0 69 backend/node_modules/is-extglob/package.json -0 21 backend/node_modules/is-glob/LICENSE -0 206 backend/node_modules/is-glob/README.md -0 150 backend/node_modules/is-glob/index.js -0 81 backend/node_modules/is-glob/package.json -0 21 backend/node_modules/is-number/LICENSE -0 187 backend/node_modules/is-number/README.md -0 18 backend/node_modules/is-number/index.js -0 82 backend/node_modules/is-number/package.json -0 5 backend/node_modules/make-error/LICENSE -0 112 backend/node_modules/make-error/README.md -0 1 backend/node_modules/make-error/dist/make-error.js -0 47 backend/node_modules/make-error/index.d.ts -0 151 backend/node_modules/make-error/index.js -0 62 backend/node_modules/make-error/package.json -0 22 backend/node_modules/media-typer/HISTORY.md -0 22 backend/node_modules/media-typer/LICENSE -0 81 backend/node_modules/media-typer/README.md -0 270 backend/node_modules/media-typer/index.js -0 26 backend/node_modules/media-typer/package.json -0 21 backend/node_modules/merge-descriptors/HISTORY.md -0 23 backend/node_modules/merge-descriptors/LICENSE -0 48 backend/node_modules/merge-descriptors/README.md -0 60 backend/node_modules/merge-descriptors/index.js -0 32 backend/node_modules/merge-descriptors/package.json -0 21 backend/node_modules/merge2/LICENSE -0 144 backend/node_modules/merge2/README.md -0 144 backend/node_modules/merge2/index.js -0 43 backend/node_modules/merge2/package.json -0 29 backend/node_modules/methods/HISTORY.md -0 24 backend/node_modules/methods/LICENSE -0 51 backend/node_modules/methods/README.md -0 69 backend/node_modules/methods/index.js -0 36 backend/node_modules/methods/package.json -0 21 backend/node_modules/micromatch/LICENSE -0 1024 backend/node_modules/micromatch/README.md -0 474 backend/node_modules/micromatch/index.js -0 119 backend/node_modules/micromatch/package.json -0 507 backend/node_modules/mime-db/HISTORY.md -0 23 backend/node_modules/mime-db/LICENSE -0 100 backend/node_modules/mime-db/README.md -0 8519 backend/node_modules/mime-db/db.json -0 12 backend/node_modules/mime-db/index.js -0 60 backend/node_modules/mime-db/package.json -0 397 backend/node_modules/mime-types/HISTORY.md -0 23 backend/node_modules/mime-types/LICENSE -0 113 backend/node_modules/mime-types/README.md -0 188 backend/node_modules/mime-types/index.js -0 44 backend/node_modules/mime-types/package.json -0 0 backend/node_modules/mime/.npmignore -0 164 backend/node_modules/mime/CHANGELOG.md -0 21 backend/node_modules/mime/LICENSE -0 90 backend/node_modules/mime/README.md -0 8 backend/node_modules/mime/cli.js -0 108 backend/node_modules/mime/mime.js -0 44 backend/node_modules/mime/package.json -0 53 backend/node_modules/mime/src/build.js -0 60 backend/node_modules/mime/src/test.js -0 1 backend/node_modules/mime/types.json -0 15 backend/node_modules/minimatch/LICENSE -0 230 backend/node_modules/minimatch/README.md -0 947 backend/node_modules/minimatch/minimatch.js -0 33 backend/node_modules/minimatch/package.json -0 29 backend/node_modules/minimist/.eslintrc -0 12 backend/node_modules/minimist/.github/FUNDING.yml -0 14 backend/node_modules/minimist/.nycrc -0 298 backend/node_modules/minimist/CHANGELOG.md -0 18 backend/node_modules/minimist/LICENSE -0 121 backend/node_modules/minimist/README.md -0 4 backend/node_modules/minimist/example/parse.js -0 263 backend/node_modules/minimist/index.js -0 75 backend/node_modules/minimist/package.json -0 34 backend/node_modules/minimist/test/all_bool.js -0 177 backend/node_modules/minimist/test/bool.js -0 43 backend/node_modules/minimist/test/dash.js -0 37 backend/node_modules/minimist/test/default_bool.js -0 24 backend/node_modules/minimist/test/dotted.js -0 32 backend/node_modules/minimist/test/kv_short.js -0 33 backend/node_modules/minimist/test/long.js -0 38 backend/node_modules/minimist/test/num.js -0 209 backend/node_modules/minimist/test/parse.js -0 11 backend/node_modules/minimist/test/parse_modified.js -0 64 backend/node_modules/minimist/test/proto.js -0 69 backend/node_modules/minimist/test/short.js -0 17 backend/node_modules/minimist/test/stop_early.js -0 104 backend/node_modules/minimist/test/unknown.js -0 10 backend/node_modules/minimist/test/whitespace.js -0 15 backend/node_modules/mkdirp/CHANGELOG.md -0 21 backend/node_modules/mkdirp/LICENSE -0 68 backend/node_modules/mkdirp/bin/cmd.js -0 31 backend/node_modules/mkdirp/index.js -0 29 backend/node_modules/mkdirp/lib/find-made.js -0 64 backend/node_modules/mkdirp/lib/mkdirp-manual.js -0 39 backend/node_modules/mkdirp/lib/mkdirp-native.js -0 23 backend/node_modules/mkdirp/lib/opts-arg.js -0 29 backend/node_modules/mkdirp/lib/path-arg.js -0 10 backend/node_modules/mkdirp/lib/use-native.js -0 44 backend/node_modules/mkdirp/package.json -0 266 backend/node_modules/mkdirp/readme.markdown -0 152 backend/node_modules/ms/index.js -0 21 backend/node_modules/ms/license.md -0 37 backend/node_modules/ms/package.json -0 51 backend/node_modules/ms/readme.md -0 108 backend/node_modules/negotiator/HISTORY.md -0 24 backend/node_modules/negotiator/LICENSE -0 203 backend/node_modules/negotiator/README.md -0 82 backend/node_modules/negotiator/index.js -0 169 backend/node_modules/negotiator/lib/charset.js -0 184 backend/node_modules/negotiator/lib/encoding.js -0 179 backend/node_modules/negotiator/lib/language.js -0 294 backend/node_modules/negotiator/lib/mediaType.js -0 42 backend/node_modules/negotiator/package.json -0 21 backend/node_modules/normalize-path/LICENSE -0 127 backend/node_modules/normalize-path/README.md -0 35 backend/node_modules/normalize-path/index.js -0 77 backend/node_modules/normalize-path/package.json -0 53 backend/node_modules/object-inspect/.eslintrc -0 12 backend/node_modules/object-inspect/.github/FUNDING.yml -0 13 backend/node_modules/object-inspect/.nycrc -0 404 backend/node_modules/object-inspect/CHANGELOG.md -0 21 backend/node_modules/object-inspect/LICENSE -0 23 backend/node_modules/object-inspect/example/all.js -0 6 backend/node_modules/object-inspect/example/circular.js -0 5 backend/node_modules/object-inspect/example/fn.js -0 10 backend/node_modules/object-inspect/example/inspect.js -0 527 backend/node_modules/object-inspect/index.js -0 20 backend/node_modules/object-inspect/package-support.json -0 104 backend/node_modules/object-inspect/package.json -0 84 backend/node_modules/object-inspect/readme.markdown -0 26 backend/node_modules/object-inspect/test-core-js.js -0 58 backend/node_modules/object-inspect/test/bigint.js -0 15 backend/node_modules/object-inspect/test/browser/dom.js -0 16 backend/node_modules/object-inspect/test/circular.js -0 12 backend/node_modules/object-inspect/test/deep.js -0 53 backend/node_modules/object-inspect/test/element.js -0 48 backend/node_modules/object-inspect/test/err.js -0 29 backend/node_modules/object-inspect/test/fakes.js -0 76 backend/node_modules/object-inspect/test/fn.js -0 17 backend/node_modules/object-inspect/test/global.js -0 15 backend/node_modules/object-inspect/test/has.js -0 15 backend/node_modules/object-inspect/test/holes.js -0 271 backend/node_modules/object-inspect/test/indent-option.js -0 139 backend/node_modules/object-inspect/test/inspect.js -0 12 backend/node_modules/object-inspect/test/lowbyte.js -0 58 backend/node_modules/object-inspect/test/number.js -0 17 backend/node_modules/object-inspect/test/quoteStyle.js -0 40 backend/node_modules/object-inspect/test/toStringTag.js -0 12 backend/node_modules/object-inspect/test/undef.js -0 211 backend/node_modules/object-inspect/test/values.js -0 1 backend/node_modules/object-inspect/util.inspect.js -0 98 backend/node_modules/on-finished/HISTORY.md -0 23 backend/node_modules/on-finished/LICENSE -0 162 backend/node_modules/on-finished/README.md -0 234 backend/node_modules/on-finished/index.js -0 39 backend/node_modules/on-finished/package.json -0 15 backend/node_modules/once/LICENSE -0 79 backend/node_modules/once/README.md -0 42 backend/node_modules/once/once.js -0 33 backend/node_modules/once/package.json -0 58 backend/node_modules/parseurl/HISTORY.md -0 24 backend/node_modules/parseurl/LICENSE -0 133 backend/node_modules/parseurl/README.md -0 158 backend/node_modules/parseurl/index.js -0 40 backend/node_modules/parseurl/package.json -0 20 backend/node_modules/path-is-absolute/index.js -0 21 backend/node_modules/path-is-absolute/license -0 43 backend/node_modules/path-is-absolute/package.json -0 59 backend/node_modules/path-is-absolute/readme.md -0 21 backend/node_modules/path-parse/LICENSE -0 42 backend/node_modules/path-parse/README.md -0 75 backend/node_modules/path-parse/index.js -0 33 backend/node_modules/path-parse/package.json -0 36 backend/node_modules/path-to-regexp/History.md -0 21 backend/node_modules/path-to-regexp/LICENSE -0 35 backend/node_modules/path-to-regexp/Readme.md -0 129 backend/node_modules/path-to-regexp/index.js -0 30 backend/node_modules/path-to-regexp/package.json -0 136 backend/node_modules/picomatch/CHANGELOG.md -0 21 backend/node_modules/picomatch/LICENSE -0 708 backend/node_modules/picomatch/README.md -0 3 backend/node_modules/picomatch/index.js -0 179 backend/node_modules/picomatch/lib/constants.js -0 1091 backend/node_modules/picomatch/lib/parse.js -0 342 backend/node_modules/picomatch/lib/picomatch.js -0 391 backend/node_modules/picomatch/lib/scan.js -0 64 backend/node_modules/picomatch/lib/utils.js -0 81 backend/node_modules/picomatch/package.json -0 4379 backend/node_modules/prettier/LICENSE -0 109 backend/node_modules/prettier/README.md -0 64 backend/node_modules/prettier/bin/prettier.cjs -0 243 backend/node_modules/prettier/doc.d.ts -0 1319 backend/node_modules/prettier/doc.js -0 1291 backend/node_modules/prettier/doc.mjs -0 648 backend/node_modules/prettier/index.cjs -0 941 backend/node_modules/prettier/index.d.ts -0 22795 backend/node_modules/prettier/index.mjs -0 3884 backend/node_modules/prettier/internal/cli.mjs -0 198 backend/node_modules/prettier/package.json -0 6 backend/node_modules/prettier/plugins/acorn.d.ts -0 15 backend/node_modules/prettier/plugins/acorn.js -0 15 backend/node_modules/prettier/plugins/acorn.mjs -0 8 backend/node_modules/prettier/plugins/angular.d.ts -0 1 backend/node_modules/prettier/plugins/angular.js -0 1 backend/node_modules/prettier/plugins/angular.mjs -0 18 backend/node_modules/prettier/plugins/babel.d.ts -0 15 backend/node_modules/prettier/plugins/babel.js -0 15 backend/node_modules/prettier/plugins/babel.mjs -0 1 backend/node_modules/prettier/plugins/estree.d.ts -0 36 backend/node_modules/prettier/plugins/estree.js -0 36 backend/node_modules/prettier/plugins/estree.mjs -0 5 backend/node_modules/prettier/plugins/flow.d.ts -0 19 backend/node_modules/prettier/plugins/flow.js -0 19 backend/node_modules/prettier/plugins/flow.mjs -0 5 backend/node_modules/prettier/plugins/glimmer.d.ts -0 30 backend/node_modules/prettier/plugins/glimmer.js -0 30 backend/node_modules/prettier/plugins/glimmer.mjs -0 5 backend/node_modules/prettier/plugins/graphql.d.ts -0 29 backend/node_modules/prettier/plugins/graphql.js -0 29 backend/node_modules/prettier/plugins/graphql.mjs -0 8 backend/node_modules/prettier/plugins/html.d.ts -0 22 backend/node_modules/prettier/plugins/html.js -0 22 backend/node_modules/prettier/plugins/html.mjs -0 7 backend/node_modules/prettier/plugins/markdown.d.ts -0 62 backend/node_modules/prettier/plugins/markdown.js -0 62 backend/node_modules/prettier/plugins/markdown.mjs -0 5 backend/node_modules/prettier/plugins/meriyah.d.ts -0 4 backend/node_modules/prettier/plugins/meriyah.js -0 4 backend/node_modules/prettier/plugins/meriyah.mjs -0 7 backend/node_modules/prettier/plugins/postcss.d.ts -0 52 backend/node_modules/prettier/plugins/postcss.js -0 52 backend/node_modules/prettier/plugins/postcss.mjs -0 5 backend/node_modules/prettier/plugins/typescript.d.ts -0 20 backend/node_modules/prettier/plugins/typescript.js -0 20 backend/node_modules/prettier/plugins/typescript.mjs -0 5 backend/node_modules/prettier/plugins/yaml.d.ts -0 161 backend/node_modules/prettier/plugins/yaml.js -0 161 backend/node_modules/prettier/plugins/yaml.mjs -0 33 backend/node_modules/prettier/standalone.d.ts -0 35 backend/node_modules/prettier/standalone.js -0 35 backend/node_modules/prettier/standalone.mjs -0 161 backend/node_modules/proxy-addr/HISTORY.md -0 22 backend/node_modules/proxy-addr/LICENSE -0 139 backend/node_modules/proxy-addr/README.md -0 327 backend/node_modules/proxy-addr/index.js -0 47 backend/node_modules/proxy-addr/package.json -0 43 backend/node_modules/qs/.editorconfig -0 38 backend/node_modules/qs/.eslintrc -0 12 backend/node_modules/qs/.github/FUNDING.yml -0 13 backend/node_modules/qs/.nycrc -0 546 backend/node_modules/qs/CHANGELOG.md -0 29 backend/node_modules/qs/LICENSE.md -0 625 backend/node_modules/qs/README.md -0 2054 backend/node_modules/qs/dist/qs.js -0 23 backend/node_modules/qs/lib/formats.js -0 11 backend/node_modules/qs/lib/index.js -0 263 backend/node_modules/qs/lib/parse.js -0 326 backend/node_modules/qs/lib/stringify.js -0 252 backend/node_modules/qs/lib/utils.js -0 77 backend/node_modules/qs/package.json -0 855 backend/node_modules/qs/test/parse.js -0 909 backend/node_modules/qs/test/stringify.js -0 136 backend/node_modules/qs/test/utils.js -0 20 backend/node_modules/queue-microtask/LICENSE -0 90 backend/node_modules/queue-microtask/README.md -0 2 backend/node_modules/queue-microtask/index.d.ts -0 9 backend/node_modules/queue-microtask/index.js -0 55 backend/node_modules/queue-microtask/package.json -0 56 backend/node_modules/range-parser/HISTORY.md -0 23 backend/node_modules/range-parser/LICENSE -0 84 backend/node_modules/range-parser/README.md -0 162 backend/node_modules/range-parser/index.js -0 44 backend/node_modules/range-parser/package.json -0 308 backend/node_modules/raw-body/HISTORY.md -0 22 backend/node_modules/raw-body/LICENSE -0 223 backend/node_modules/raw-body/README.md -0 24 backend/node_modules/raw-body/SECURITY.md -0 87 backend/node_modules/raw-body/index.d.ts -0 336 backend/node_modules/raw-body/index.js -0 49 backend/node_modules/raw-body/package.json -0 21 backend/node_modules/readdirp/LICENSE -0 122 backend/node_modules/readdirp/README.md -0 43 backend/node_modules/readdirp/index.d.ts -0 287 backend/node_modules/readdirp/index.js -0 122 backend/node_modules/readdirp/package.json -0 37 backend/node_modules/resolve/.editorconfig -0 65 backend/node_modules/resolve/.eslintrc -0 12 backend/node_modules/resolve/.github/FUNDING.yml -0 21 backend/node_modules/resolve/LICENSE -0 3 backend/node_modules/resolve/SECURITY.md -0 3 backend/node_modules/resolve/async.js -0 50 backend/node_modules/resolve/bin/resolve -0 5 backend/node_modules/resolve/example/async.js -0 3 backend/node_modules/resolve/example/sync.js -0 6 backend/node_modules/resolve/index.js -0 329 backend/node_modules/resolve/lib/async.js -0 8 backend/node_modules/resolve/lib/caller.js -0 12 backend/node_modules/resolve/lib/core.js -0 158 backend/node_modules/resolve/lib/core.json -0 24 backend/node_modules/resolve/lib/homedir.js -0 5 backend/node_modules/resolve/lib/is-core.js -0 42 backend/node_modules/resolve/lib/node-modules-paths.js -0 10 backend/node_modules/resolve/lib/normalize-options.js -0 208 backend/node_modules/resolve/lib/sync.js -0 72 backend/node_modules/resolve/package.json -0 301 backend/node_modules/resolve/readme.markdown -0 3 backend/node_modules/resolve/sync.js -0 88 backend/node_modules/resolve/test/core.js -0 29 backend/node_modules/resolve/test/dotdot.js -0 2 backend/node_modules/resolve/test/dotdot/abc/index.js -0 1 backend/node_modules/resolve/test/dotdot/index.js -0 29 backend/node_modules/resolve/test/faulty_basedir.js -0 34 backend/node_modules/resolve/test/filter.js -0 33 backend/node_modules/resolve/test/filter_sync.js -0 127 backend/node_modules/resolve/test/home_paths.js -0 114 backend/node_modules/resolve/test/home_paths_sync.js -0 315 backend/node_modules/resolve/test/mock.js -0 214 backend/node_modules/resolve/test/mock_sync.js -0 56 backend/node_modules/resolve/test/module_dir.js -0 1 backend/node_modules/resolve/test/module_dir/xmodules/aaa/index.js -0 1 backend/node_modules/resolve/test/module_dir/ymodules/aaa/index.js -0 1 backend/node_modules/resolve/test/module_dir/zmodules/bbb/main.js -0 3 backend/node_modules/resolve/test/module_dir/zmodules/bbb/package.json -0 143 backend/node_modules/resolve/test/node-modules-paths.js -0 70 backend/node_modules/resolve/test/node_path.js -0 1 backend/node_modules/resolve/test/node_path/x/aaa/index.js -0 1 backend/node_modules/resolve/test/node_path/x/ccc/index.js -0 1 backend/node_modules/resolve/test/node_path/y/bbb/index.js -0 1 backend/node_modules/resolve/test/node_path/y/ccc/index.js -0 9 backend/node_modules/resolve/test/nonstring.js -0 75 backend/node_modules/resolve/test/pathfilter.js -0 0 backend/node_modules/resolve/test/pathfilter/deep_ref/main.js -0 23 backend/node_modules/resolve/test/precedence.js -0 1 backend/node_modules/resolve/test/precedence/aaa.js -0 1 backend/node_modules/resolve/test/precedence/aaa/index.js -0 1 backend/node_modules/resolve/test/precedence/aaa/main.js -0 1 backend/node_modules/resolve/test/precedence/bbb.js -0 1 backend/node_modules/resolve/test/precedence/bbb/main.js -0 597 backend/node_modules/resolve/test/resolver.js -0 0 backend/node_modules/resolve/test/resolver/baz/doom.js -0 4 backend/node_modules/resolve/test/resolver/baz/package.json -0 1 backend/node_modules/resolve/test/resolver/baz/quux.js -0 0 backend/node_modules/resolve/test/resolver/browser_field/a.js -0 0 backend/node_modules/resolve/test/resolver/browser_field/b.js -0 5 backend/node_modules/resolve/test/resolver/browser_field/package.json -0 1 backend/node_modules/resolve/test/resolver/cup.coffee -0 1 backend/node_modules/resolve/test/resolver/dot_main/index.js -0 3 backend/node_modules/resolve/test/resolver/dot_main/package.json -0 1 backend/node_modules/resolve/test/resolver/dot_slash_main/index.js -0 3 backend/node_modules/resolve/test/resolver/dot_slash_main/package.json -0 0 backend/node_modules/resolve/test/resolver/false_main/index.js -0 4 backend/node_modules/resolve/test/resolver/false_main/package.json -0 1 backend/node_modules/resolve/test/resolver/foo.js -0 2 backend/node_modules/resolve/test/resolver/incorrect_main/index.js -0 3 backend/node_modules/resolve/test/resolver/incorrect_main/package.json -0 7 backend/node_modules/resolve/test/resolver/invalid_main/package.json -0 0 backend/node_modules/resolve/test/resolver/mug.coffee -0 0 backend/node_modules/resolve/test/resolver/mug.js -0 6 backend/node_modules/resolve/test/resolver/multirepo/lerna.json -0 20 backend/node_modules/resolve/test/resolver/multirepo/package.json -0 35 backend/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js -0 14 backend/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json -0 0 backend/node_modules/resolve/test/resolver/multirepo/packages/package-b/index.js -0 14 backend/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json -0 26 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js -0 15 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json -0 12 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js -0 0 backend/node_modules/resolve/test/resolver/other_path/lib/other-lib.js -0 0 backend/node_modules/resolve/test/resolver/other_path/root.js -0 1 backend/node_modules/resolve/test/resolver/quux/foo/index.js -0 1 backend/node_modules/resolve/test/resolver/same_names/foo.js -0 1 backend/node_modules/resolve/test/resolver/same_names/foo/index.js -0 0 backend/node_modules/resolve/test/resolver/symlinked/_/node_modules/foo.js -0 0 backend/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep -0 1 backend/node_modules/resolve/test/resolver/symlinked/package/bar.js -0 3 backend/node_modules/resolve/test/resolver/symlinked/package/package.json -0 5 backend/node_modules/resolve/test/resolver/without_basedir/main.js -0 730 backend/node_modules/resolve/test/resolver_sync.js -0 54 backend/node_modules/resolve/test/shadowed_core.js -0 0 backend/node_modules/resolve/test/shadowed_core/node_modules/util/index.js -0 13 backend/node_modules/resolve/test/subdirs.js -0 176 backend/node_modules/resolve/test/symlinks.js -0 1 backend/node_modules/reusify/.coveralls.yml -0 28 backend/node_modules/reusify/.travis.yml -0 22 backend/node_modules/reusify/LICENSE -0 145 backend/node_modules/reusify/README.md -0 30 backend/node_modules/reusify/benchmarks/createNoCodeFunction.js -0 13 backend/node_modules/reusify/benchmarks/fib.js -0 38 backend/node_modules/reusify/benchmarks/reuseNoCodeFunction.js -0 45 backend/node_modules/reusify/package.json -0 33 backend/node_modules/reusify/reusify.js -0 66 backend/node_modules/reusify/test.js -0 15 backend/node_modules/rimraf/LICENSE -0 101 backend/node_modules/rimraf/README.md -0 50 backend/node_modules/rimraf/bin.js -0 29 backend/node_modules/rimraf/package.json -0 372 backend/node_modules/rimraf/rimraf.js -0 20 backend/node_modules/run-parallel/LICENSE -0 85 backend/node_modules/run-parallel/README.md -0 51 backend/node_modules/run-parallel/index.js -0 58 backend/node_modules/run-parallel/package.json -0 21 backend/node_modules/safe-buffer/LICENSE -0 584 backend/node_modules/safe-buffer/README.md -0 187 backend/node_modules/safe-buffer/index.d.ts -0 65 backend/node_modules/safe-buffer/index.js -0 51 backend/node_modules/safe-buffer/package.json -0 21 backend/node_modules/safer-buffer/LICENSE -0 268 backend/node_modules/safer-buffer/Porting-Buffer.md -0 156 backend/node_modules/safer-buffer/Readme.md -0 58 backend/node_modules/safer-buffer/dangerous.js -0 34 backend/node_modules/safer-buffer/package.json -0 77 backend/node_modules/safer-buffer/safer.js -0 406 backend/node_modules/safer-buffer/tests.js -0 521 backend/node_modules/send/HISTORY.md -0 23 backend/node_modules/send/LICENSE -0 327 backend/node_modules/send/README.md -0 24 backend/node_modules/send/SECURITY.md -0 1143 backend/node_modules/send/index.js -0 162 backend/node_modules/send/node_modules/ms/index.js -0 21 backend/node_modules/send/node_modules/ms/license.md -0 38 backend/node_modules/send/node_modules/ms/package.json -0 59 backend/node_modules/send/node_modules/ms/readme.md -0 62 backend/node_modules/send/package.json -0 471 backend/node_modules/serve-static/HISTORY.md -0 25 backend/node_modules/serve-static/LICENSE -0 257 backend/node_modules/serve-static/README.md -0 210 backend/node_modules/serve-static/index.js -0 42 backend/node_modules/serve-static/package.json -0 27 backend/node_modules/set-function-length/.eslintrc -0 12 backend/node_modules/set-function-length/.github/FUNDING.yml -0 13 backend/node_modules/set-function-length/.nycrc -0 70 backend/node_modules/set-function-length/CHANGELOG.md -0 21 backend/node_modules/set-function-length/LICENSE -0 56 backend/node_modules/set-function-length/README.md -0 9 backend/node_modules/set-function-length/env.d.ts -0 25 backend/node_modules/set-function-length/env.js -0 7 backend/node_modules/set-function-length/index.d.ts -0 42 backend/node_modules/set-function-length/index.js -0 102 backend/node_modules/set-function-length/package.json -0 9 backend/node_modules/set-function-length/tsconfig.json -0 13 backend/node_modules/setprototypeof/LICENSE -0 31 backend/node_modules/setprototypeof/README.md -0 2 backend/node_modules/setprototypeof/index.d.ts -0 17 backend/node_modules/setprototypeof/index.js -0 38 backend/node_modules/setprototypeof/package.json -0 24 backend/node_modules/setprototypeof/test/index.js -0 9 backend/node_modules/side-channel/.editorconfig -0 11 backend/node_modules/side-channel/.eslintrc -0 12 backend/node_modules/side-channel/.github/FUNDING.yml -0 13 backend/node_modules/side-channel/.nycrc -0 95 backend/node_modules/side-channel/CHANGELOG.md -0 21 backend/node_modules/side-channel/LICENSE -0 2 backend/node_modules/side-channel/README.md -0 27 backend/node_modules/side-channel/index.d.ts -0 129 backend/node_modules/side-channel/index.js -0 84 backend/node_modules/side-channel/package.json -0 83 backend/node_modules/side-channel/test/index.js -0 50 backend/node_modules/side-channel/tsconfig.json -0 21 backend/node_modules/source-map-support/LICENSE.md -0 284 backend/node_modules/source-map-support/README.md -0 114 backend/node_modules/source-map-support/browser-source-map-support.js -0 31 backend/node_modules/source-map-support/package.json -0 1 backend/node_modules/source-map-support/register-hook-require.js -0 1 backend/node_modules/source-map-support/register.js -0 625 backend/node_modules/source-map-support/source-map-support.js -0 301 backend/node_modules/source-map/CHANGELOG.md -0 28 backend/node_modules/source-map/LICENSE -0 742 backend/node_modules/source-map/README.md -0 3234 backend/node_modules/source-map/dist/source-map.debug.js -0 3233 backend/node_modules/source-map/dist/source-map.js -0 2 backend/node_modules/source-map/dist/source-map.min.js -0 1 backend/node_modules/source-map/dist/source-map.min.js.map -0 121 backend/node_modules/source-map/lib/array-set.js -0 140 backend/node_modules/source-map/lib/base64-vlq.js -0 67 backend/node_modules/source-map/lib/base64.js -0 111 backend/node_modules/source-map/lib/binary-search.js -0 79 backend/node_modules/source-map/lib/mapping-list.js -0 114 backend/node_modules/source-map/lib/quick-sort.js -0 1145 backend/node_modules/source-map/lib/source-map-consumer.js -0 425 backend/node_modules/source-map/lib/source-map-generator.js -0 413 backend/node_modules/source-map/lib/source-node.js -0 488 backend/node_modules/source-map/lib/util.js -0 73 backend/node_modules/source-map/package.json -0 98 backend/node_modules/source-map/source-map.d.ts -0 8 backend/node_modules/source-map/source-map.js -0 82 backend/node_modules/statuses/HISTORY.md -0 23 backend/node_modules/statuses/LICENSE -0 136 backend/node_modules/statuses/README.md -0 65 backend/node_modules/statuses/codes.json -0 146 backend/node_modules/statuses/index.js -0 49 backend/node_modules/statuses/package.json -0 14 backend/node_modules/strip-bom/index.js -0 21 backend/node_modules/strip-bom/license -0 40 backend/node_modules/strip-bom/package.json -0 36 backend/node_modules/strip-bom/readme.md -0 70 backend/node_modules/strip-json-comments/index.js -0 21 backend/node_modules/strip-json-comments/license -0 42 backend/node_modules/strip-json-comments/package.json -0 64 backend/node_modules/strip-json-comments/readme.md -0 14 backend/node_modules/supports-preserve-symlinks-flag/.eslintrc -0 12 backend/node_modules/supports-preserve-symlinks-flag/.github/FUNDING.yml -0 9 backend/node_modules/supports-preserve-symlinks-flag/.nycrc -0 22 backend/node_modules/supports-preserve-symlinks-flag/CHANGELOG.md -0 21 backend/node_modules/supports-preserve-symlinks-flag/LICENSE -0 42 backend/node_modules/supports-preserve-symlinks-flag/README.md -0 3 backend/node_modules/supports-preserve-symlinks-flag/browser.js -0 9 backend/node_modules/supports-preserve-symlinks-flag/index.js -0 70 backend/node_modules/supports-preserve-symlinks-flag/package.json -0 29 backend/node_modules/supports-preserve-symlinks-flag/test/index.js -0 21 backend/node_modules/to-regex-range/LICENSE -0 305 backend/node_modules/to-regex-range/README.md -0 288 backend/node_modules/to-regex-range/index.js -0 88 backend/node_modules/to-regex-range/package.json -0 9 backend/node_modules/toidentifier/HISTORY.md -0 21 backend/node_modules/toidentifier/LICENSE -0 61 backend/node_modules/toidentifier/README.md -0 32 backend/node_modules/toidentifier/index.js -0 38 backend/node_modules/toidentifier/package.json -0 21 backend/node_modules/tree-kill/LICENSE -0 89 backend/node_modules/tree-kill/README.md -0 14 backend/node_modules/tree-kill/cli.js -0 13 backend/node_modules/tree-kill/index.d.ts -0 118 backend/node_modules/tree-kill/index.js -0 51 backend/node_modules/tree-kill/package.json -0 22 backend/node_modules/ts-node-dev/LICENSE -0 90 backend/node_modules/ts-node-dev/README.md -- - backend/node_modules/ts-node-dev/icons/node_error.png -- - backend/node_modules/ts-node-dev/icons/node_info.png -0 136 backend/node_modules/ts-node-dev/lib/bin.js -0 59 backend/node_modules/ts-node-dev/lib/cfg.js -0 16 backend/node_modules/ts-node-dev/lib/check-file-exists.js -0 140 backend/node_modules/ts-node-dev/lib/child-require-hook.js -0 290 backend/node_modules/ts-node-dev/lib/compiler.js -0 2 backend/node_modules/ts-node-dev/lib/dedupe.js -0 18 backend/node_modules/ts-node-dev/lib/get-compiled-path.js -0 49 backend/node_modules/ts-node-dev/lib/get-cwd.js -0 68 backend/node_modules/ts-node-dev/lib/hook.js -0 286 backend/node_modules/ts-node-dev/lib/index.js -0 35 backend/node_modules/ts-node-dev/lib/ipc.js -0 50 backend/node_modules/ts-node-dev/lib/log.js -0 33 backend/node_modules/ts-node-dev/lib/notify.js -0 33 backend/node_modules/ts-node-dev/lib/resolveMain.js -0 104 backend/node_modules/ts-node-dev/lib/wrap.js -0 101 backend/node_modules/ts-node-dev/package.json -0 21 backend/node_modules/ts-node/LICENSE -0 1442 backend/node_modules/ts-node/README.md -0 8 backend/node_modules/ts-node/child-loader.mjs -0 24 backend/node_modules/ts-node/dist-raw/NODE-LICENSE.md -0 36 backend/node_modules/ts-node/dist-raw/README.md -0 4 backend/node_modules/ts-node/dist-raw/node-internal-constants.js -0 82 backend/node_modules/ts-node/dist-raw/node-internal-errors.js -0 89 backend/node_modules/ts-node/dist-raw/node-internal-modules-cjs-helpers.js -0 593 backend/node_modules/ts-node/dist-raw/node-internal-modules-cjs-loader.js -0 106 backend/node_modules/ts-node/dist-raw/node-internal-modules-esm-get_format.js -0 962 backend/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js -0 44 backend/node_modules/ts-node/dist-raw/node-internal-modules-package_json_reader.js -0 254 backend/node_modules/ts-node/dist-raw/node-internal-repl-await.js -0 58 backend/node_modules/ts-node/dist-raw/node-internalBinding-fs.js -0 9 backend/node_modules/ts-node/dist-raw/node-nativemodule.js -0 103 backend/node_modules/ts-node/dist-raw/node-options.js -0 37 backend/node_modules/ts-node/dist-raw/node-primordials.js -0 9 backend/node_modules/ts-node/dist-raw/runmain-hack.js -0 2 backend/node_modules/ts-node/dist/bin-cwd.d.ts -0 6 backend/node_modules/ts-node/dist/bin-cwd.js -0 1 backend/node_modules/ts-node/dist/bin-cwd.js.map -0 2 backend/node_modules/ts-node/dist/bin-esm.d.ts -0 6 backend/node_modules/ts-node/dist/bin-esm.js -0 1 backend/node_modules/ts-node/dist/bin-esm.js.map -0 2 backend/node_modules/ts-node/dist/bin-script-deprecated.d.ts -0 7 backend/node_modules/ts-node/dist/bin-script-deprecated.js -0 1 backend/node_modules/ts-node/dist/bin-script-deprecated.js.map -0 2 backend/node_modules/ts-node/dist/bin-script.d.ts -0 6 backend/node_modules/ts-node/dist/bin-script.js -0 1 backend/node_modules/ts-node/dist/bin-script.js.map -0 2 backend/node_modules/ts-node/dist/bin-transpile.d.ts -0 6 backend/node_modules/ts-node/dist/bin-transpile.js -0 1 backend/node_modules/ts-node/dist/bin-transpile.js.map -0 11 backend/node_modules/ts-node/dist/bin.d.ts -0 581 backend/node_modules/ts-node/dist/bin.js -0 1 backend/node_modules/ts-node/dist/bin.js.map -0 1 backend/node_modules/ts-node/dist/child/argv-payload.d.ts -0 19 backend/node_modules/ts-node/dist/child/argv-payload.js -0 1 backend/node_modules/ts-node/dist/child/argv-payload.js.map -0 1 backend/node_modules/ts-node/dist/child/child-entrypoint.d.ts -0 24 backend/node_modules/ts-node/dist/child/child-entrypoint.js -0 1 backend/node_modules/ts-node/dist/child/child-entrypoint.js.map -0 1 backend/node_modules/ts-node/dist/child/child-loader.d.ts -0 32 backend/node_modules/ts-node/dist/child/child-loader.js -0 1 backend/node_modules/ts-node/dist/child/child-loader.js.map -0 7 backend/node_modules/ts-node/dist/child/child-require.d.ts -0 22 backend/node_modules/ts-node/dist/child/child-require.js -0 1 backend/node_modules/ts-node/dist/child/child-require.js.map -0 1 backend/node_modules/ts-node/dist/child/spawn-child.d.ts -0 49 backend/node_modules/ts-node/dist/child/spawn-child.js -0 1 backend/node_modules/ts-node/dist/child/spawn-child.js.map -0 1 backend/node_modules/ts-node/dist/cjs-resolve-hooks.d.ts -0 29 backend/node_modules/ts-node/dist/cjs-resolve-hooks.js -0 1 backend/node_modules/ts-node/dist/cjs-resolve-hooks.js.map -0 1 backend/node_modules/ts-node/dist/configuration.d.ts -0 308 backend/node_modules/ts-node/dist/configuration.js -0 1 backend/node_modules/ts-node/dist/configuration.js.map -0 53 backend/node_modules/ts-node/dist/esm.d.ts -0 228 backend/node_modules/ts-node/dist/esm.js -0 1 backend/node_modules/ts-node/dist/esm.js.map -0 1 backend/node_modules/ts-node/dist/file-extensions.d.ts -0 133 backend/node_modules/ts-node/dist/file-extensions.js -0 1 backend/node_modules/ts-node/dist/file-extensions.js.map -0 332 backend/node_modules/ts-node/dist/index.d.ts -0 953 backend/node_modules/ts-node/dist/index.js -0 1 backend/node_modules/ts-node/dist/index.js.map -0 1 backend/node_modules/ts-node/dist/module-type-classifier.d.ts -0 64 backend/node_modules/ts-node/dist/module-type-classifier.js -0 1 backend/node_modules/ts-node/dist/module-type-classifier.js.map -0 1 backend/node_modules/ts-node/dist/node-module-type-classifier.d.ts -0 39 backend/node_modules/ts-node/dist/node-module-type-classifier.js -0 1 backend/node_modules/ts-node/dist/node-module-type-classifier.js.map -0 78 backend/node_modules/ts-node/dist/repl.d.ts -0 561 backend/node_modules/ts-node/dist/repl.js -0 1 backend/node_modules/ts-node/dist/repl.js.map -0 1 backend/node_modules/ts-node/dist/resolver-functions.d.ts -0 143 backend/node_modules/ts-node/dist/resolver-functions.js -0 1 backend/node_modules/ts-node/dist/resolver-functions.js.map -0 11 backend/node_modules/ts-node/dist/transpilers/swc.d.ts -0 218 backend/node_modules/ts-node/dist/transpilers/swc.js -0 1 backend/node_modules/ts-node/dist/transpilers/swc.js.map -0 35 backend/node_modules/ts-node/dist/transpilers/types.d.ts -0 3 backend/node_modules/ts-node/dist/transpilers/types.js -0 1 backend/node_modules/ts-node/dist/transpilers/types.js.map -0 63 backend/node_modules/ts-node/dist/ts-compiler-types.d.ts -0 3 backend/node_modules/ts-node/dist/ts-compiler-types.js -0 1 backend/node_modules/ts-node/dist/ts-compiler-types.js.map -0 6 backend/node_modules/ts-node/dist/ts-internals.d.ts -0 321 backend/node_modules/ts-node/dist/ts-internals.js -0 1 backend/node_modules/ts-node/dist/ts-internals.js.map -0 1 backend/node_modules/ts-node/dist/ts-transpile-module.d.ts -0 100 backend/node_modules/ts-node/dist/ts-transpile-module.js -0 1 backend/node_modules/ts-node/dist/ts-transpile-module.js.map -0 13 backend/node_modules/ts-node/dist/tsconfig-schema.d.ts -0 3 backend/node_modules/ts-node/dist/tsconfig-schema.js -0 1 backend/node_modules/ts-node/dist/tsconfig-schema.js.map -0 1 backend/node_modules/ts-node/dist/tsconfigs.d.ts -0 36 backend/node_modules/ts-node/dist/tsconfigs.js -0 1 backend/node_modules/ts-node/dist/tsconfigs.js.map -0 4 backend/node_modules/ts-node/dist/util.d.ts -0 175 backend/node_modules/ts-node/dist/util.js -0 1 backend/node_modules/ts-node/dist/util.js.map -0 8 backend/node_modules/ts-node/esm.mjs -0 8 backend/node_modules/ts-node/esm/transpile-only.mjs -0 3 backend/node_modules/ts-node/node10/tsconfig.json -0 3 backend/node_modules/ts-node/node12/tsconfig.json -0 3 backend/node_modules/ts-node/node14/tsconfig.json -0 3 backend/node_modules/ts-node/node16/tsconfig.json -0 182 backend/node_modules/ts-node/package.json -0 3 backend/node_modules/ts-node/register/files.js -0 1 backend/node_modules/ts-node/register/index.js -0 3 backend/node_modules/ts-node/register/transpile-only.js -0 3 backend/node_modules/ts-node/register/type-check.js -0 1 backend/node_modules/ts-node/transpilers/swc-experimental.js -0 1 backend/node_modules/ts-node/transpilers/swc.js -0 183 backend/node_modules/ts-node/tsconfig.schema.json -0 1326 backend/node_modules/ts-node/tsconfig.schemastore-schema.json -0 22 backend/node_modules/tsconfig/LICENSE -0 50 backend/node_modules/tsconfig/README.md -0 13 backend/node_modules/tsconfig/dist/tsconfig.d.ts -0 159 backend/node_modules/tsconfig/dist/tsconfig.js -0 1 backend/node_modules/tsconfig/dist/tsconfig.js.map -0 0 backend/node_modules/tsconfig/dist/tsconfig.spec.d.ts -0 140 backend/node_modules/tsconfig/dist/tsconfig.spec.js -0 1 backend/node_modules/tsconfig/dist/tsconfig.spec.js.map -0 63 backend/node_modules/tsconfig/package.json -0 259 backend/node_modules/type-is/HISTORY.md -0 23 backend/node_modules/type-is/LICENSE -0 170 backend/node_modules/type-is/README.md -0 266 backend/node_modules/type-is/index.js -0 45 backend/node_modules/type-is/package.json -0 55 backend/node_modules/typescript/LICENSE.txt -0 50 backend/node_modules/typescript/README.md -0 41 backend/node_modules/typescript/SECURITY.md -0 193 backend/node_modules/typescript/ThirdPartyNoticeText.txt -0 2 backend/node_modules/typescript/bin/tsc -0 2 backend/node_modules/typescript/bin/tsserver -0 90 backend/node_modules/typescript/lib/cancellationToken.js -0 1880 backend/node_modules/typescript/lib/cs/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/de/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/es/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/fr/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/it/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/ja/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/ko/diagnosticMessages.generated.json -0 22 backend/node_modules/typescript/lib/lib.d.ts -0 386 backend/node_modules/typescript/lib/lib.decorators.d.ts -0 22 backend/node_modules/typescript/lib/lib.decorators.legacy.d.ts -0 33 backend/node_modules/typescript/lib/lib.dom.asynciterable.d.ts -0 28596 backend/node_modules/typescript/lib/lib.dom.d.ts -0 475 backend/node_modules/typescript/lib/lib.dom.iterable.d.ts -0 147 backend/node_modules/typescript/lib/lib.es2015.collection.d.ts -0 597 backend/node_modules/typescript/lib/lib.es2015.core.d.ts -0 28 backend/node_modules/typescript/lib/lib.es2015.d.ts -0 77 backend/node_modules/typescript/lib/lib.es2015.generator.d.ts -0 495 backend/node_modules/typescript/lib/lib.es2015.iterable.d.ts -0 81 backend/node_modules/typescript/lib/lib.es2015.promise.d.ts -0 128 backend/node_modules/typescript/lib/lib.es2015.proxy.d.ts -0 144 backend/node_modules/typescript/lib/lib.es2015.reflect.d.ts -0 46 backend/node_modules/typescript/lib/lib.es2015.symbol.d.ts -0 326 backend/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts -0 116 backend/node_modules/typescript/lib/lib.es2016.array.include.d.ts -0 21 backend/node_modules/typescript/lib/lib.es2016.d.ts -0 23 backend/node_modules/typescript/lib/lib.es2016.full.d.ts -0 31 backend/node_modules/typescript/lib/lib.es2016.intl.d.ts -0 25 backend/node_modules/typescript/lib/lib.es2017.d.ts -0 31 backend/node_modules/typescript/lib/lib.es2017.date.d.ts -0 23 backend/node_modules/typescript/lib/lib.es2017.full.d.ts -0 44 backend/node_modules/typescript/lib/lib.es2017.intl.d.ts -0 49 backend/node_modules/typescript/lib/lib.es2017.object.d.ts -0 135 backend/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts -0 45 backend/node_modules/typescript/lib/lib.es2017.string.d.ts -0 53 backend/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts -0 77 backend/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts -0 43 backend/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2018.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2018.full.d.ts -0 83 backend/node_modules/typescript/lib/lib.es2018.intl.d.ts -0 30 backend/node_modules/typescript/lib/lib.es2018.promise.d.ts -0 37 backend/node_modules/typescript/lib/lib.es2018.regexp.d.ts -0 79 backend/node_modules/typescript/lib/lib.es2019.array.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2019.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2019.full.d.ts -0 23 backend/node_modules/typescript/lib/lib.es2019.intl.d.ts -0 33 backend/node_modules/typescript/lib/lib.es2019.object.d.ts -0 37 backend/node_modules/typescript/lib/lib.es2019.string.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2019.symbol.d.ts -0 727 backend/node_modules/typescript/lib/lib.es2020.bigint.d.ts -0 27 backend/node_modules/typescript/lib/lib.es2020.d.ts -0 42 backend/node_modules/typescript/lib/lib.es2020.date.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2020.full.d.ts -0 474 backend/node_modules/typescript/lib/lib.es2020.intl.d.ts -0 28 backend/node_modules/typescript/lib/lib.es2020.number.d.ts -0 47 backend/node_modules/typescript/lib/lib.es2020.promise.d.ts -0 97 backend/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts -0 42 backend/node_modules/typescript/lib/lib.es2020.string.d.ts -0 37 backend/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts -0 23 backend/node_modules/typescript/lib/lib.es2021.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2021.full.d.ts -0 166 backend/node_modules/typescript/lib/lib.es2021.intl.d.ts -0 48 backend/node_modules/typescript/lib/lib.es2021.promise.d.ts -0 33 backend/node_modules/typescript/lib/lib.es2021.string.d.ts -0 76 backend/node_modules/typescript/lib/lib.es2021.weakref.d.ts -0 121 backend/node_modules/typescript/lib/lib.es2022.array.d.ts -0 26 backend/node_modules/typescript/lib/lib.es2022.d.ts -0 73 backend/node_modules/typescript/lib/lib.es2022.error.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2022.full.d.ts -0 117 backend/node_modules/typescript/lib/lib.es2022.intl.d.ts -0 26 backend/node_modules/typescript/lib/lib.es2022.object.d.ts -0 39 backend/node_modules/typescript/lib/lib.es2022.regexp.d.ts -0 39 backend/node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts -0 25 backend/node_modules/typescript/lib/lib.es2022.string.d.ts -0 924 backend/node_modules/typescript/lib/lib.es2023.array.d.ts -0 21 backend/node_modules/typescript/lib/lib.es2023.collection.d.ts -0 22 backend/node_modules/typescript/lib/lib.es2023.d.ts -0 24 backend/node_modules/typescript/lib/lib.es2023.full.d.ts -0 56 backend/node_modules/typescript/lib/lib.es2023.intl.d.ts -0 4585 backend/node_modules/typescript/lib/lib.es5.d.ts -0 23 backend/node_modules/typescript/lib/lib.es6.d.ts -0 35 backend/node_modules/typescript/lib/lib.esnext.array.d.ts -0 106 backend/node_modules/typescript/lib/lib.esnext.collection.d.ts -0 28 backend/node_modules/typescript/lib/lib.esnext.d.ts -0 28 backend/node_modules/typescript/lib/lib.esnext.decorators.d.ts -0 185 backend/node_modules/typescript/lib/lib.esnext.disposable.d.ts -0 24 backend/node_modules/typescript/lib/lib.esnext.full.d.ts -0 21 backend/node_modules/typescript/lib/lib.esnext.intl.d.ts -0 29 backend/node_modules/typescript/lib/lib.esnext.object.d.ts -0 35 backend/node_modules/typescript/lib/lib.esnext.promise.d.ts -0 25 backend/node_modules/typescript/lib/lib.esnext.regexp.d.ts -0 29 backend/node_modules/typescript/lib/lib.esnext.string.d.ts -0 322 backend/node_modules/typescript/lib/lib.scripthost.d.ts -0 33 backend/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts -0 9431 backend/node_modules/typescript/lib/lib.webworker.d.ts -0 23 backend/node_modules/typescript/lib/lib.webworker.importscripts.d.ts -0 276 backend/node_modules/typescript/lib/lib.webworker.iterable.d.ts -0 1880 backend/node_modules/typescript/lib/pl/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/ru/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/tr/diagnosticMessages.generated.json -0 129810 backend/node_modules/typescript/lib/tsc.js -0 621 backend/node_modules/typescript/lib/tsserver.js -0 17 backend/node_modules/typescript/lib/tsserverlibrary.d.ts -0 21 backend/node_modules/typescript/lib/tsserverlibrary.js -0 497 backend/node_modules/typescript/lib/typesMap.json -0 11240 backend/node_modules/typescript/lib/typescript.d.ts -0 195005 backend/node_modules/typescript/lib/typescript.js -0 236 backend/node_modules/typescript/lib/typingsInstaller.js -0 53 backend/node_modules/typescript/lib/watchGuard.js -0 1880 backend/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json -0 1880 backend/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json -0 116 backend/node_modules/typescript/package.json -0 21 backend/node_modules/undici-types/LICENSE -0 6 backend/node_modules/undici-types/README.md -0 31 backend/node_modules/undici-types/agent.d.ts -0 43 backend/node_modules/undici-types/api.d.ts -0 29 backend/node_modules/undici-types/balanced-pool.d.ts -0 36 backend/node_modules/undici-types/cache.d.ts -0 108 backend/node_modules/undici-types/client.d.ts -0 34 backend/node_modules/undici-types/connector.d.ts -0 21 backend/node_modules/undici-types/content-type.d.ts -0 28 backend/node_modules/undici-types/cookies.d.ts -0 66 backend/node_modules/undici-types/diagnostics-channel.d.ts -0 255 backend/node_modules/undici-types/dispatcher.d.ts -0 21 backend/node_modules/undici-types/env-http-proxy-agent.d.ts -0 128 backend/node_modules/undici-types/errors.d.ts -0 63 backend/node_modules/undici-types/eventsource.d.ts -0 209 backend/node_modules/undici-types/fetch.d.ts -0 39 backend/node_modules/undici-types/file.d.ts -0 54 backend/node_modules/undici-types/filereader.d.ts -0 108 backend/node_modules/undici-types/formdata.d.ts -0 9 backend/node_modules/undici-types/global-dispatcher.d.ts -0 7 backend/node_modules/undici-types/global-origin.d.ts -0 15 backend/node_modules/undici-types/handlers.d.ts -0 4 backend/node_modules/undici-types/header.d.ts -0 74 backend/node_modules/undici-types/index.d.ts -0 11 backend/node_modules/undici-types/interceptors.d.ts -0 50 backend/node_modules/undici-types/mock-agent.d.ts -0 25 backend/node_modules/undici-types/mock-client.d.ts -0 12 backend/node_modules/undici-types/mock-errors.d.ts -0 93 backend/node_modules/undici-types/mock-interceptor.d.ts -0 25 backend/node_modules/undici-types/mock-pool.d.ts -0 55 backend/node_modules/undici-types/package.json -0 71 backend/node_modules/undici-types/patch.d.ts -0 19 backend/node_modules/undici-types/pool-stats.d.ts -0 39 backend/node_modules/undici-types/pool.d.ts -0 28 backend/node_modules/undici-types/proxy-agent.d.ts -0 60 backend/node_modules/undici-types/readable.d.ts -0 11 backend/node_modules/undici-types/retry-agent.d.ts -0 116 backend/node_modules/undici-types/retry-handler.d.ts -0 18 backend/node_modules/undici-types/util.d.ts -0 224 backend/node_modules/undici-types/webidl.d.ts -0 152 backend/node_modules/undici-types/websocket.d.ts -0 4 backend/node_modules/unpipe/HISTORY.md -0 22 backend/node_modules/unpipe/LICENSE -0 43 backend/node_modules/unpipe/README.md -0 69 backend/node_modules/unpipe/index.js -0 27 backend/node_modules/unpipe/package.json -0 9 backend/node_modules/utils-merge/.npmignore -0 20 backend/node_modules/utils-merge/LICENSE -0 34 backend/node_modules/utils-merge/README.md -0 23 backend/node_modules/utils-merge/index.js -0 40 backend/node_modules/utils-merge/package.json -0 53 backend/node_modules/v8-compile-cache-lib/CHANGELOG.md -0 21 backend/node_modules/v8-compile-cache-lib/LICENSE -0 60 backend/node_modules/v8-compile-cache-lib/README.md -0 35 backend/node_modules/v8-compile-cache-lib/package.json -0 7 backend/node_modules/v8-compile-cache-lib/v8-compile-cache.d.ts -0 391 backend/node_modules/v8-compile-cache-lib/v8-compile-cache.js -0 39 backend/node_modules/vary/HISTORY.md -0 22 backend/node_modules/vary/LICENSE -0 101 backend/node_modules/vary/README.md -0 149 backend/node_modules/vary/index.js -0 43 backend/node_modules/vary/package.json -0 15 backend/node_modules/wrappy/LICENSE -0 36 backend/node_modules/wrappy/README.md -0 29 backend/node_modules/wrappy/package.json -0 33 backend/node_modules/wrappy/wrappy.js -0 30 backend/node_modules/xtend/.jshintrc -0 20 backend/node_modules/xtend/LICENSE -0 32 backend/node_modules/xtend/README.md -0 19 backend/node_modules/xtend/immutable.js -0 17 backend/node_modules/xtend/mutable.js -0 55 backend/node_modules/xtend/package.json -0 103 backend/node_modules/xtend/test.js -0 65 backend/node_modules/yn/index.d.ts -0 33 backend/node_modules/yn/index.js -0 105 backend/node_modules/yn/lenient.js -0 9 backend/node_modules/yn/license -0 42 backend/node_modules/yn/package.json -0 83 backend/node_modules/yn/readme.md -0 1781 backend/package-lock.json -0 29 backend/package.json -0 3632 backend/public/chunk-2FMXBR5T.js -0 10784 backend/public/chunk-HIPB54JD.js -0 11131 backend/public/chunk-HQFIBEV7.js -0 3632 backend/public/chunk-OVZZS7I2.js -0 10644 backend/public/chunk-QWS3MURL.js -0 3632 backend/public/chunk-UOJ4IBH7.js -- - backend/public/favicon.ico -0 1541 backend/public/index.html -0 77834 backend/public/main-4V2HQDRI.js -0 458 backend/public/main-AIZTFC63.css -0 62671 backend/public/main-C2TU7PNS.js -0 62674 backend/public/main-DAYCMAFW.js -0 62705 backend/public/main-K3WBRXWH.js -0 73390 backend/public/main-MTHSAXW3.js -0 62706 backend/public/main-UJURQ3JB.js -0 62674 backend/public/main-UO5QPOB3.js -0 1 backend/public/ping.txt -0 2030 backend/public/polyfills-SCHOHYNV.js -0 2900 backend/public/styles-KWQ3FGSD.css -0 2442 backend/public/styles-OPAWKEW6.css -0 2913 backend/public/styles-SVPGZYNK.css -0 4 backend/publish.sh -0 17 backend/readme.md -0 182 backend/src/express.ts -0 42 backend/src/index.ts -0 39 backend/src/infrastructure/config.ts -0 60 backend/src/infrastructure/deps.ts -0 88 backend/src/infrastructure/git.ts -0 17 backend/src/infrastructure/log.ts -0 3 backend/src/infrastructure/paths.ts -0 17 backend/src/infrastructure/tree-hash.ts -0 6 backend/src/model/config.ts -0 7 backend/src/model/deps.ts -0 9 backend/src/model/limits.ts -0 17 backend/src/options/options.ts -0 38 backend/src/options/parse-options.ts -0 18 backend/src/options/validate-options.ts -0 63 backend/src/services/change-coupling.ts -0 93 backend/src/services/coupling.ts -0 81 backend/src/services/folders.ts -0 156 backend/src/services/hotspot.ts -0 27 backend/src/services/log-cache.ts -0 27 backend/src/services/module-info.ts -0 113 backend/src/services/team-alignment.ts -0 43 backend/src/utils/complexity.ts -0 6 backend/src/utils/count-lines.ts -0 181 backend/src/utils/git-parser.ts -0 3 backend/src/utils/matrix.ts -0 13 backend/src/utils/normalize-folder.ts -0 30 backend/src/utils/open.ts -0 3 backend/src/utils/to-percent.ts -0 13 backend/tsconfig.json -0 16 frontend/.editorconfig -0 42 frontend/.gitignore -0 6 frontend/.prettierrc -0 4 frontend/.vscode/extensions.json -0 20 frontend/.vscode/launch.json -0 42 frontend/.vscode/tasks.json -0 27 frontend/README.md -0 94 frontend/angular.json -0 3 frontend/build.sh -0 43 frontend/eslint.config.js -0 146 frontend/m3-theme.scss -0 17222 frontend/package-lock.json -0 58 frontend/package.json -0 7 frontend/proxy.conf.json -- - frontend/public/favicon.ico -0 0 frontend/src/app/app.component.css -0 1 frontend/src/app/app.component.html -0 31 frontend/src/app/app.component.spec.ts -0 19 frontend/src/app/app.component.ts -0 24 frontend/src/app/app.config.ts -0 42 frontend/src/app/app.routes.ts -0 19 frontend/src/app/data/cache.service.ts -0 17 frontend/src/app/data/config.service.ts -0 26 frontend/src/app/data/coupling.service.ts -0 13 frontend/src/app/data/folder.service.ts -0 13 frontend/src/app/data/module.service.ts -0 14 frontend/src/app/data/status.service.ts -0 17 frontend/src/app/data/status.store.ts -0 17 frontend/src/app/data/team-alignment.service.ts -0 150 frontend/src/app/features/coupling/graph.adapter.ts -0 60 frontend/src/app/features/coupling/graph.component.css -0 21 frontend/src/app/features/coupling/graph.component.html -0 22 frontend/src/app/features/coupling/graph.component.spec.ts -0 104 frontend/src/app/features/coupling/graph.component.ts -0 246 frontend/src/app/features/coupling/graph.ts -0 43 frontend/src/app/features/hotspot/hotspot-result.ts -0 57 frontend/src/app/features/hotspot/hotspot.component.css -0 105 frontend/src/app/features/hotspot/hotspot.component.html -0 22 frontend/src/app/features/hotspot/hotspot.component.spec.ts -0 195 frontend/src/app/features/hotspot/hotspot.component.ts -0 32 frontend/src/app/features/hotspot/hotspot.service.ts -0 97 frontend/src/app/features/team-alignment/team-alignment-chart.ts -0 0 frontend/src/app/features/team-alignment/team-alignment.component.css -0 20 frontend/src/app/features/team-alignment/team-alignment.component.html -0 22 frontend/src/app/features/team-alignment/team-alignment.component.spec.ts -0 77 frontend/src/app/features/team-alignment/team-alignment.component.ts -0 3 frontend/src/app/model/cache-status.ts -0 8 frontend/src/app/model/config.ts -0 7 frontend/src/app/model/coupling-result.ts -0 5 frontend/src/app/model/folder.ts -0 5 frontend/src/app/model/graph-type.ts -0 9 frontend/src/app/model/limits.ts -0 3 frontend/src/app/model/module-info.ts -0 7 frontend/src/app/model/status.ts -0 8 frontend/src/app/model/team-alignment-result.ts -0 27 frontend/src/app/shell/filter-tree/filter-tree.component.css -0 46 frontend/src/app/shell/filter-tree/filter-tree.component.html -0 22 frontend/src/app/shell/filter-tree/filter-tree.component.spec.ts -0 135 frontend/src/app/shell/filter-tree/filter-tree.component.ts -0 26 frontend/src/app/shell/nav/nav.component.css -0 52 frontend/src/app/shell/nav/nav.component.html -0 25 frontend/src/app/shell/nav/nav.component.spec.ts -0 41 frontend/src/app/shell/nav/nav.component.ts -0 3 frontend/src/app/types.d.ts -0 19 frontend/src/app/ui/limits/limits.component.css -0 34 frontend/src/app/ui/limits/limits.component.html -0 22 frontend/src/app/ui/limits/limits.component.spec.ts -0 82 frontend/src/app/ui/limits/limits.component.ts -0 3 frontend/src/app/ui/loading/loading.component.css -0 12 frontend/src/app/ui/loading/loading.component.html -0 22 frontend/src/app/ui/loading/loading.component.spec.ts -0 11 frontend/src/app/ui/loading/loading.component.ts -0 30 frontend/src/app/utils/cache.guard.ts -0 9 frontend/src/app/utils/debounce.ts -0 24 frontend/src/app/utils/effects.ts -0 34 frontend/src/app/utils/error-handler.ts -0 7 frontend/src/app/utils/event.service.ts -0 8 frontend/src/app/utils/segments.ts -0 21 frontend/src/index.html -0 7 frontend/src/main.ts -0 85 frontend/src/styles.scss -0 11 frontend/tsconfig.app.json -0 28 frontend/tsconfig.json -0 10 frontend/tsconfig.spec.json - -"Manfred Steyer ,Mon Sep 2 23:45:30 2024 +0200 4760cb0cd67b15d913c02646000a231e8e519b49,refactor: run prettier" -0 1 README.md -1 2 apps/backend/.eslintrc.json -12 12 apps/backend/package.json -7 4 apps/backend/project.json -1 1 apps/backend/src/express.ts -2 2 apps/backend/src/infrastructure/config.ts -8 1 apps/backend/src/infrastructure/deps.ts -3 1 apps/backend/src/utils/open.ts -1 1 apps/frontend/src/app/app.component.spec.ts -1 1 apps/frontend/src/app/data/coupling.service.ts -4 4 apps/frontend/src/app/features/coupling/graph.adapter.ts -1 4 apps/frontend/src/app/features/coupling/graph.component.html -19 7 apps/frontend/src/app/features/coupling/graph.component.ts -2 2 apps/frontend/src/app/features/hotspot/hotspot-result.ts -57 60 apps/frontend/src/app/features/hotspot/hotspot.component.html -8 10 apps/frontend/src/app/features/hotspot/hotspot.component.ts -2 2 apps/frontend/src/app/features/hotspot/hotspot.service.ts -2 3 apps/frontend/src/app/features/team-alignment/team-alignment-chart.ts -4 7 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -36 16 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -1 1 apps/frontend/src/app/model/coupling-result.ts -1 1 apps/frontend/src/app/model/team-alignment-result.ts -8 8 apps/frontend/src/app/shell/nav/nav.component.html -1 1 apps/frontend/src/app/shell/nav/nav.component.ts -22 22 apps/frontend/src/app/ui/limits/limits.component.html -1 1 apps/frontend/src/app/utils/cache.guard.ts -2 2 apps/frontend/src/app/utils/debounce.ts -2 4 apps/frontend/src/app/utils/effects.ts -1 1 apps/frontend/src/app/utils/error-handler.ts -1 1 apps/frontend/src/index.html -1 1 apps/frontend/src/main.ts -2 2 apps/frontend/src/styles.scss -0 1 package.json - -"Manfred Steyer ,Mon Sep 2 23:23:20 2024 +0200 784c75067e22f15686e3ecf06e744d19f14e7a04,feat: add error handlers" -0 1 apps/backend/src/express.ts -0 4 apps/backend/src/services/team-alignment.ts -2 2 apps/frontend/src/app/app.config.ts -15 4 apps/frontend/src/app/features/coupling/graph.component.ts -7 2 apps/frontend/src/app/features/hotspot/hotspot.component.ts -11 4 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -8 0 apps/frontend/src/app/model/coupling-result.ts -5 0 apps/frontend/src/app/model/team-alignment-result.ts -15 6 apps/frontend/src/app/utils/error-handler.ts - -"Manfred Steyer ,Mon Sep 2 18:18:28 2024 +0200 3ba40cab78ea64a08043eb91124c9505d3dd6e1c,refactor: migrate to nx" -2 0 README.md -8 0 _backend/.detective/config.json -355 0 _backend/.detective/deps.json -6 0 _backend/.prettierrc -- - _backend/detective-0.0.1.tgz -3 0 _backend/export.txt -1 0 _backend/forensic.conf.json -1781 0 _backend/package-lock.json -29 0 _backend/package.json -3632 0 _backend/public/chunk-2FMXBR5T.js -10784 0 _backend/public/chunk-HIPB54JD.js -11131 0 _backend/public/chunk-HQFIBEV7.js -3632 0 _backend/public/chunk-OVZZS7I2.js -10644 0 _backend/public/chunk-QWS3MURL.js -3632 0 _backend/public/chunk-UOJ4IBH7.js -- - _backend/public/favicon.ico -1541 0 _backend/public/index.html -77834 0 _backend/public/main-4V2HQDRI.js -458 0 _backend/public/main-AIZTFC63.css -62671 0 _backend/public/main-C2TU7PNS.js -62674 0 _backend/public/main-DAYCMAFW.js -62705 0 _backend/public/main-K3WBRXWH.js -73390 0 _backend/public/main-MTHSAXW3.js -62706 0 _backend/public/main-UJURQ3JB.js -62674 0 _backend/public/main-UO5QPOB3.js -1 0 _backend/public/ping.txt -2030 0 _backend/public/polyfills-SCHOHYNV.js -2900 0 _backend/public/styles-KWQ3FGSD.css -2442 0 _backend/public/styles-OPAWKEW6.css -2913 0 _backend/public/styles-SVPGZYNK.css -4 0 _backend/publish.sh -17 0 _backend/readme.md -182 0 _backend/src/express.ts -42 0 _backend/src/index.ts -39 0 _backend/src/infrastructure/config.ts -60 0 _backend/src/infrastructure/deps.ts -88 0 _backend/src/infrastructure/git.ts -17 0 _backend/src/infrastructure/log.ts -3 0 _backend/src/infrastructure/paths.ts -17 0 _backend/src/infrastructure/tree-hash.ts -6 0 _backend/src/model/config.ts -7 0 _backend/src/model/deps.ts -9 0 _backend/src/model/limits.ts -17 0 _backend/src/options/options.ts -38 0 _backend/src/options/parse-options.ts -18 0 _backend/src/options/validate-options.ts -63 0 _backend/src/services/change-coupling.ts -93 0 _backend/src/services/coupling.ts -81 0 _backend/src/services/folders.ts -156 0 _backend/src/services/hotspot.ts -27 0 _backend/src/services/log-cache.ts -27 0 _backend/src/services/module-info.ts -109 0 _backend/src/services/team-alignment.ts -43 0 _backend/src/utils/complexity.ts -6 0 _backend/src/utils/count-lines.ts -181 0 _backend/src/utils/git-parser.ts -3 0 _backend/src/utils/matrix.ts -13 0 _backend/src/utils/normalize-folder.ts -30 0 _backend/src/utils/open.ts -3 0 _backend/src/utils/to-percent.ts -13 0 _backend/tsconfig.json -16 0 _fe/.editorconfig -42 0 _fe/.gitignore -6 0 _fe/.prettierrc -4 0 _fe/.vscode/extensions.json -20 0 _fe/.vscode/launch.json -42 0 _fe/.vscode/tasks.json -27 0 _fe/README.md -94 0 _fe/angular.json -3 0 _fe/build.sh -43 0 _fe/eslint.config.js -146 0 _fe/m3-theme.scss -17222 0 _fe/package-lock.json -58 0 _fe/package.json -7 0 _fe/proxy.conf.json -- - _fe/public/favicon.ico -0 0 _fe/src/app/app.component.css -1 0 _fe/src/app/app.component.html -31 0 _fe/src/app/app.component.spec.ts -19 0 _fe/src/app/app.component.ts -24 0 _fe/src/app/app.config.ts -42 0 _fe/src/app/app.routes.ts -19 0 _fe/src/app/data/cache.service.ts -17 0 _fe/src/app/data/config.service.ts -26 0 _fe/src/app/data/coupling.service.ts -13 0 _fe/src/app/data/folder.service.ts -13 0 _fe/src/app/data/module.service.ts -14 0 _fe/src/app/data/status.service.ts -17 0 _fe/src/app/data/status.store.ts -17 0 _fe/src/app/data/team-alignment.service.ts -150 0 _fe/src/app/features/coupling/graph.adapter.ts -60 0 _fe/src/app/features/coupling/graph.component.css -21 0 _fe/src/app/features/coupling/graph.component.html -22 0 _fe/src/app/features/coupling/graph.component.spec.ts -104 0 _fe/src/app/features/coupling/graph.component.ts -246 0 _fe/src/app/features/coupling/graph.ts -43 0 _fe/src/app/features/hotspot/hotspot-result.ts -57 0 _fe/src/app/features/hotspot/hotspot.component.css -105 0 _fe/src/app/features/hotspot/hotspot.component.html -22 0 _fe/src/app/features/hotspot/hotspot.component.spec.ts -195 0 _fe/src/app/features/hotspot/hotspot.component.ts -32 0 _fe/src/app/features/hotspot/hotspot.service.ts -97 0 _fe/src/app/features/team-alignment/team-alignment-chart.ts -0 0 _fe/src/app/features/team-alignment/team-alignment.component.css -20 0 _fe/src/app/features/team-alignment/team-alignment.component.html -22 0 _fe/src/app/features/team-alignment/team-alignment.component.spec.ts -77 0 _fe/src/app/features/team-alignment/team-alignment.component.ts -3 0 _fe/src/app/model/cache-status.ts -8 0 _fe/src/app/model/config.ts -7 0 _fe/src/app/model/coupling-result.ts -5 0 _fe/src/app/model/folder.ts -5 0 _fe/src/app/model/graph-type.ts -9 0 _fe/src/app/model/limits.ts -3 0 _fe/src/app/model/module-info.ts -7 0 _fe/src/app/model/status.ts -8 0 _fe/src/app/model/team-alignment-result.ts -27 0 _fe/src/app/shell/filter-tree/filter-tree.component.css -46 0 _fe/src/app/shell/filter-tree/filter-tree.component.html -22 0 _fe/src/app/shell/filter-tree/filter-tree.component.spec.ts -135 0 _fe/src/app/shell/filter-tree/filter-tree.component.ts -26 0 _fe/src/app/shell/nav/nav.component.css -52 0 _fe/src/app/shell/nav/nav.component.html -25 0 _fe/src/app/shell/nav/nav.component.spec.ts -41 0 _fe/src/app/shell/nav/nav.component.ts -3 0 _fe/src/app/types.d.ts -19 0 _fe/src/app/ui/limits/limits.component.css -34 0 _fe/src/app/ui/limits/limits.component.html -22 0 _fe/src/app/ui/limits/limits.component.spec.ts -82 0 _fe/src/app/ui/limits/limits.component.ts -3 0 _fe/src/app/ui/loading/loading.component.css -12 0 _fe/src/app/ui/loading/loading.component.html -22 0 _fe/src/app/ui/loading/loading.component.spec.ts -11 0 _fe/src/app/ui/loading/loading.component.ts -30 0 _fe/src/app/utils/cache.guard.ts -9 0 _fe/src/app/utils/debounce.ts -24 0 _fe/src/app/utils/effects.ts -34 0 _fe/src/app/utils/error-handler.ts -7 0 _fe/src/app/utils/event.service.ts -8 0 _fe/src/app/utils/segments.ts -21 0 _fe/src/index.html -7 0 _fe/src/main.ts -85 0 _fe/src/styles.scss -11 0 _fe/tsconfig.app.json -28 0 _fe/tsconfig.json -10 0 _fe/tsconfig.spec.json -18 0 apps/backend-e2e/.eslintrc.json -19 0 apps/backend-e2e/jest.config.ts -17 0 apps/backend-e2e/project.json -10 0 apps/backend-e2e/src/backend/backend.spec.ts -10 0 apps/backend-e2e/src/support/global-setup.ts -7 0 apps/backend-e2e/src/support/global-teardown.ts -10 0 apps/backend-e2e/src/support/test-setup.ts -13 0 apps/backend-e2e/tsconfig.json -9 0 apps/backend-e2e/tsconfig.spec.json -19 0 apps/backend/.eslintrc.json -11 0 apps/backend/jest.config.ts -14 0 apps/backend/package.json -42 0 apps/backend/project.json -0 0 apps/backend/src/assets/.gitkeep -1 0 apps/backend/src/assets/index.html -2 0 apps/backend/src/bin/main.js -178 0 apps/backend/src/express.ts -37 0 apps/backend/src/infrastructure/config.ts -60 0 apps/backend/src/infrastructure/deps.ts -88 0 apps/backend/src/infrastructure/git.ts -17 0 apps/backend/src/infrastructure/log.ts -3 0 apps/backend/src/infrastructure/paths.ts -17 0 apps/backend/src/infrastructure/tree-hash.ts -42 0 apps/backend/src/main.ts -6 0 apps/backend/src/model/config.ts -7 0 apps/backend/src/model/deps.ts -9 0 apps/backend/src/model/limits.ts -17 0 apps/backend/src/options/options.ts -38 0 apps/backend/src/options/parse-options.ts -18 0 apps/backend/src/options/validate-options.ts -63 0 apps/backend/src/services/change-coupling.ts -90 0 apps/backend/src/services/coupling.ts -81 0 apps/backend/src/services/folders.ts -156 0 apps/backend/src/services/hotspot.ts -27 0 apps/backend/src/services/log-cache.ts -27 0 apps/backend/src/services/module-info.ts -113 0 apps/backend/src/services/team-alignment.ts -43 0 apps/backend/src/utils/complexity.ts -6 0 apps/backend/src/utils/count-lines.ts -181 0 apps/backend/src/utils/git-parser.ts -3 0 apps/backend/src/utils/matrix.ts -13 0 apps/backend/src/utils/normalize-folder.ts -30 0 apps/backend/src/utils/open.ts -3 0 apps/backend/src/utils/to-percent.ts -10 0 apps/backend/tsconfig.app.json -16 0 apps/backend/tsconfig.json -14 0 apps/backend/tsconfig.spec.json -20 0 apps/backend/webpack.config.js -36 0 apps/frontend/.eslintrc.json -22 0 apps/frontend/jest.config.ts -89 0 apps/frontend/project.json -7 0 apps/frontend/proxy.conf.json -- - apps/frontend/public/favicon.ico -0 0 apps/frontend/src/app/app.component.css -1 0 apps/frontend/src/app/app.component.html -31 0 apps/frontend/src/app/app.component.spec.ts -19 0 apps/frontend/src/app/app.component.ts -24 0 apps/frontend/src/app/app.config.ts -42 0 apps/frontend/src/app/app.routes.ts -19 0 apps/frontend/src/app/data/cache.service.ts -17 0 apps/frontend/src/app/data/config.service.ts -26 0 apps/frontend/src/app/data/coupling.service.ts -13 0 apps/frontend/src/app/data/folder.service.ts -13 0 apps/frontend/src/app/data/module.service.ts -14 0 apps/frontend/src/app/data/status.service.ts -17 0 apps/frontend/src/app/data/status.store.ts -17 0 apps/frontend/src/app/data/team-alignment.service.ts -150 0 apps/frontend/src/app/features/coupling/graph.adapter.ts -60 0 apps/frontend/src/app/features/coupling/graph.component.css -21 0 apps/frontend/src/app/features/coupling/graph.component.html -22 0 apps/frontend/src/app/features/coupling/graph.component.spec.ts -104 0 apps/frontend/src/app/features/coupling/graph.component.ts -246 0 apps/frontend/src/app/features/coupling/graph.ts -43 0 apps/frontend/src/app/features/hotspot/hotspot-result.ts -57 0 apps/frontend/src/app/features/hotspot/hotspot.component.css -105 0 apps/frontend/src/app/features/hotspot/hotspot.component.html -22 0 apps/frontend/src/app/features/hotspot/hotspot.component.spec.ts -210 0 apps/frontend/src/app/features/hotspot/hotspot.component.ts -32 0 apps/frontend/src/app/features/hotspot/hotspot.service.ts -97 0 apps/frontend/src/app/features/team-alignment/team-alignment-chart.ts -0 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.css -20 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.html -22 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.spec.ts -77 0 apps/frontend/src/app/features/team-alignment/team-alignment.component.ts -3 0 apps/frontend/src/app/model/cache-status.ts -8 0 apps/frontend/src/app/model/config.ts -7 0 apps/frontend/src/app/model/coupling-result.ts -5 0 apps/frontend/src/app/model/folder.ts -5 0 apps/frontend/src/app/model/graph-type.ts -9 0 apps/frontend/src/app/model/limits.ts -3 0 apps/frontend/src/app/model/module-info.ts -7 0 apps/frontend/src/app/model/status.ts -8 0 apps/frontend/src/app/model/team-alignment-result.ts -27 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.css -46 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.html -22 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.spec.ts -135 0 apps/frontend/src/app/shell/filter-tree/filter-tree.component.ts -26 0 apps/frontend/src/app/shell/nav/nav.component.css -52 0 apps/frontend/src/app/shell/nav/nav.component.html -25 0 apps/frontend/src/app/shell/nav/nav.component.spec.ts -41 0 apps/frontend/src/app/shell/nav/nav.component.ts -3 0 apps/frontend/src/app/types.d.ts -19 0 apps/frontend/src/app/ui/limits/limits.component.css -34 0 apps/frontend/src/app/ui/limits/limits.component.html -22 0 apps/frontend/src/app/ui/limits/limits.component.spec.ts -82 0 apps/frontend/src/app/ui/limits/limits.component.ts -3 0 apps/frontend/src/app/ui/loading/loading.component.css -12 0 apps/frontend/src/app/ui/loading/loading.component.html -22 0 apps/frontend/src/app/ui/loading/loading.component.spec.ts -11 0 apps/frontend/src/app/ui/loading/loading.component.ts -30 0 apps/frontend/src/app/utils/cache.guard.ts -9 0 apps/frontend/src/app/utils/debounce.ts -24 0 apps/frontend/src/app/utils/effects.ts -34 0 apps/frontend/src/app/utils/error-handler.ts -7 0 apps/frontend/src/app/utils/event.service.ts -8 0 apps/frontend/src/app/utils/segments.ts -21 0 apps/frontend/src/index.html -7 0 apps/frontend/src/main.ts -81 0 apps/frontend/src/styles.scss -8 0 apps/frontend/src/test-setup.ts -10 0 apps/frontend/tsconfig.app.json -6 0 apps/frontend/tsconfig.editor.json -33 0 apps/frontend/tsconfig.json -16 0 apps/frontend/tsconfig.spec.json -6 0 build.sh -5 0 jest.config.ts -3 0 jest.preset.js -70 0 nx.json -23776 0 package-lock.json -84 0 package.json -2 0 publish-local.sh -20 0 tsconfig.base.json - -"Manfred Steyer ,Mon Sep 2 14:54:57 2024 +0200 a61c82b79d36d78a826c56138ce1269eacfa1c88,refactor: split graph creation in several functions" -51 35 frontend/src/app/features/coupling/graph.ts -1 1 frontend/src/app/features/team-alignment/team-alignment.component.ts - -"Manfred Steyer ,Mon Sep 2 14:40:51 2024 +0200 76163f56096767ab9a1a7a29396cde6bd366bd11,refactor: swtich back to strict mode (after trying out some diagram libs)" -22 10 frontend/src/app/features/coupling/graph.ts -8 0 frontend/src/app/features/hotspot/hotspot-result.ts -1 1 frontend/src/app/features/hotspot/hotspot.component.html -13 8 frontend/src/app/features/hotspot/hotspot.component.ts -5 0 frontend/src/app/features/team-alignment/team-alignment-chart.ts -1 1 frontend/src/app/features/team-alignment/team-alignment.component.ts -1 1 frontend/src/app/shell/filter-tree/filter-tree.component.ts -3 0 frontend/src/app/types.d.ts -2 2 frontend/src/app/utils/cache.guard.ts -11 0 frontend/src/app/utils/{explicit-effect.ts => effects.ts} -1 1 frontend/tsconfig.json - -"Manfred Steyer ,Mon Sep 2 13:41:22 2024 +0200 909f4540b75f64dced184ef9fa1f16a1293e6bc8,refactor: structure" -1 0 backend/node_modules/.bin/acorn -1 0 backend/node_modules/.bin/mime -1 0 backend/node_modules/.bin/mkdirp -1 0 backend/node_modules/.bin/prettier -1 0 backend/node_modules/.bin/resolve -1 0 backend/node_modules/.bin/rimraf -1 0 backend/node_modules/.bin/sheriff -1 0 backend/node_modules/.bin/tree-kill -1 0 backend/node_modules/.bin/ts-node -1 0 backend/node_modules/.bin/ts-node-cwd -1 0 backend/node_modules/.bin/ts-node-dev -1 0 backend/node_modules/.bin/ts-node-esm -1 0 backend/node_modules/.bin/ts-node-script -1 0 backend/node_modules/.bin/ts-node-transpile-only -1 0 backend/node_modules/.bin/ts-script -1 0 backend/node_modules/.bin/tsc -1 0 backend/node_modules/.bin/tsnd -1 0 backend/node_modules/.bin/tsserver -1761 0 backend/node_modules/.package-lock.json -21 0 backend/node_modules/@cspotcode/source-map-support/LICENSE.md -289 0 backend/node_modules/@cspotcode/source-map-support/README.md -114 0 backend/node_modules/@cspotcode/source-map-support/browser-source-map-support.js -50 0 backend/node_modules/@cspotcode/source-map-support/package.json -7 0 backend/node_modules/@cspotcode/source-map-support/register-hook-require.d.ts -3 0 backend/node_modules/@cspotcode/source-map-support/register-hook-require.js -7 0 backend/node_modules/@cspotcode/source-map-support/register.d.ts -1 0 backend/node_modules/@cspotcode/source-map-support/register.js -76 0 backend/node_modules/@cspotcode/source-map-support/source-map-support.d.ts -938 0 backend/node_modules/@cspotcode/source-map-support/source-map-support.js -19 0 backend/node_modules/@jridgewell/resolve-uri/LICENSE -40 0 backend/node_modules/@jridgewell/resolve-uri/README.md -232 0 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs -1 0 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs.map -240 0 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js -1 0 backend/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js.map -4 0 backend/node_modules/@jridgewell/resolve-uri/dist/types/resolve-uri.d.ts -69 0 backend/node_modules/@jridgewell/resolve-uri/package.json -21 0 backend/node_modules/@jridgewell/sourcemap-codec/LICENSE -264 0 backend/node_modules/@jridgewell/sourcemap-codec/README.md -424 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs -1 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map -439 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js -1 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map -49 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/scopes.d.ts -8 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/sourcemap-codec.d.ts -15 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/strings.d.ts -6 0 backend/node_modules/@jridgewell/sourcemap-codec/dist/types/vlq.d.ts -75 0 backend/node_modules/@jridgewell/sourcemap-codec/package.json -19 0 backend/node_modules/@jridgewell/trace-mapping/LICENSE -193 0 backend/node_modules/@jridgewell/trace-mapping/README.md -514 0 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs -1 0 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs.map -528 0 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js -1 0 backend/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js.map -8 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/any-map.d.ts -32 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/binary-search.d.ts -7 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/by-source.d.ts -1 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/resolve.d.ts -2 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/sort.d.ts -16 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/sourcemap-segment.d.ts -4 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/strip-filename.d.ts -70 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/trace-mapping.d.ts -85 0 backend/node_modules/@jridgewell/trace-mapping/dist/types/types.d.ts -70 0 backend/node_modules/@jridgewell/trace-mapping/package.json -21 0 backend/node_modules/@nodelib/fs.scandir/LICENSE -171 0 backend/node_modules/@nodelib/fs.scandir/README.md -20 0 backend/node_modules/@nodelib/fs.scandir/out/adapters/fs.d.ts -19 0 backend/node_modules/@nodelib/fs.scandir/out/adapters/fs.js -4 0 backend/node_modules/@nodelib/fs.scandir/out/constants.d.ts -17 0 backend/node_modules/@nodelib/fs.scandir/out/constants.js -12 0 backend/node_modules/@nodelib/fs.scandir/out/index.d.ts -26 0 backend/node_modules/@nodelib/fs.scandir/out/index.js -7 0 backend/node_modules/@nodelib/fs.scandir/out/providers/async.d.ts -104 0 backend/node_modules/@nodelib/fs.scandir/out/providers/async.js -1 0 backend/node_modules/@nodelib/fs.scandir/out/providers/common.d.ts -13 0 backend/node_modules/@nodelib/fs.scandir/out/providers/common.js -5 0 backend/node_modules/@nodelib/fs.scandir/out/providers/sync.d.ts -54 0 backend/node_modules/@nodelib/fs.scandir/out/providers/sync.js -20 0 backend/node_modules/@nodelib/fs.scandir/out/settings.d.ts -24 0 backend/node_modules/@nodelib/fs.scandir/out/settings.js -20 0 backend/node_modules/@nodelib/fs.scandir/out/types/index.d.ts -2 0 backend/node_modules/@nodelib/fs.scandir/out/types/index.js -2 0 backend/node_modules/@nodelib/fs.scandir/out/utils/fs.d.ts -19 0 backend/node_modules/@nodelib/fs.scandir/out/utils/fs.js -2 0 backend/node_modules/@nodelib/fs.scandir/out/utils/index.d.ts -5 0 backend/node_modules/@nodelib/fs.scandir/out/utils/index.js -44 0 backend/node_modules/@nodelib/fs.scandir/package.json -21 0 backend/node_modules/@nodelib/fs.stat/LICENSE -126 0 backend/node_modules/@nodelib/fs.stat/README.md -13 0 backend/node_modules/@nodelib/fs.stat/out/adapters/fs.d.ts -17 0 backend/node_modules/@nodelib/fs.stat/out/adapters/fs.js -12 0 backend/node_modules/@nodelib/fs.stat/out/index.d.ts -26 0 backend/node_modules/@nodelib/fs.stat/out/index.js -4 0 backend/node_modules/@nodelib/fs.stat/out/providers/async.d.ts -36 0 backend/node_modules/@nodelib/fs.stat/out/providers/async.js -3 0 backend/node_modules/@nodelib/fs.stat/out/providers/sync.d.ts -23 0 backend/node_modules/@nodelib/fs.stat/out/providers/sync.js -16 0 backend/node_modules/@nodelib/fs.stat/out/settings.d.ts -16 0 backend/node_modules/@nodelib/fs.stat/out/settings.js -4 0 backend/node_modules/@nodelib/fs.stat/out/types/index.d.ts -2 0 backend/node_modules/@nodelib/fs.stat/out/types/index.js -37 0 backend/node_modules/@nodelib/fs.stat/package.json -21 0 backend/node_modules/@nodelib/fs.walk/LICENSE -215 0 backend/node_modules/@nodelib/fs.walk/README.md -14 0 backend/node_modules/@nodelib/fs.walk/out/index.d.ts -34 0 backend/node_modules/@nodelib/fs.walk/out/index.js -12 0 backend/node_modules/@nodelib/fs.walk/out/providers/async.d.ts -30 0 backend/node_modules/@nodelib/fs.walk/out/providers/async.js -4 0 backend/node_modules/@nodelib/fs.walk/out/providers/index.d.ts -9 0 backend/node_modules/@nodelib/fs.walk/out/providers/index.js -12 0 backend/node_modules/@nodelib/fs.walk/out/providers/stream.d.ts -34 0 backend/node_modules/@nodelib/fs.walk/out/providers/stream.js -10 0 backend/node_modules/@nodelib/fs.walk/out/providers/sync.d.ts -14 0 backend/node_modules/@nodelib/fs.walk/out/providers/sync.js -30 0 backend/node_modules/@nodelib/fs.walk/out/readers/async.d.ts -97 0 backend/node_modules/@nodelib/fs.walk/out/readers/async.js -7 0 backend/node_modules/@nodelib/fs.walk/out/readers/common.d.ts -31 0 backend/node_modules/@nodelib/fs.walk/out/readers/common.js -6 0 backend/node_modules/@nodelib/fs.walk/out/readers/reader.d.ts -11 0 backend/node_modules/@nodelib/fs.walk/out/readers/reader.js -15 0 backend/node_modules/@nodelib/fs.walk/out/readers/sync.d.ts -59 0 backend/node_modules/@nodelib/fs.walk/out/readers/sync.js -30 0 backend/node_modules/@nodelib/fs.walk/out/settings.d.ts -26 0 backend/node_modules/@nodelib/fs.walk/out/settings.js -8 0 backend/node_modules/@nodelib/fs.walk/out/types/index.d.ts -2 0 backend/node_modules/@nodelib/fs.walk/out/types/index.js -44 0 backend/node_modules/@nodelib/fs.walk/package.json -9 0 backend/node_modules/@softarc/sheriff-core/README.md -23 0 backend/node_modules/@softarc/sheriff-core/package.json -8 0 backend/node_modules/@softarc/sheriff-core/src/bin/main.d.ts -12 0 backend/node_modules/@softarc/sheriff-core/src/bin/main.js -1 0 backend/node_modules/@softarc/sheriff-core/src/bin/main.js.map -8 0 backend/node_modules/@softarc/sheriff-core/src/index.d.ts -18 0 backend/node_modules/@softarc/sheriff-core/src/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/index.js.map -61 0 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.d.ts -51 0 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/api/get-project-data.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.d.ts -9 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/any-tag.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.d.ts -7 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-circular.js.map -9 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.d.ts -39 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-deep-imports.js.map -10 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.d.ts -45 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/check-for-dependency-rule-violation.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.d.ts -11 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/get-afi.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.d.ts -34 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/is-dependency-allowed.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.d.ts -8 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/no-dependencies.js.map -26 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.d.ts -30 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/same-tag.js.map -0 0 frontend/src/app/team-alignment/team-alignment.component.css => backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/checks/unused-files.js.map -7 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.d.ts -11 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/cli.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.d.ts -17 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/export-data.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.d.ts -43 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/init.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.d.ts -28 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/get-entry-from-cli-or-config.js.map -7 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.d.ts -30 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/internal/handle-error.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.d.ts -64 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/list.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.d.ts -46 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/main.js.map -4 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.d.ts -43 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/helpers/mock-cli.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.d.ts -61 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/tests/verify-cli-wrapper.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.d.ts -69 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/verify.js.map -7 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.d.ts -15 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/cli/version.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.d.ts -14 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/default-config.js.map -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/dependency-rules-config.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.d.ts -17 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/find-config.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.d.ts -46 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/parse-config.js.map -4 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/sheriff-config.js.map -20 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/tag-config.js.map -119 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/config/user-sheriff-config.js.map -26 0 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.d.ts -53 0 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/error/user-error.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.d.ts -43 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/deep-import.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.d.ts -57 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/eslint/violates-dependency-rule.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.d.ts -18 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/format-file-info.js.map -16 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.d.ts -50 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/fs-path.js.map -21 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.d.ts -74 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-ts-data.js.map -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.d.ts -27 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/generate-unassigned-file-info.js.map -14 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.d.ts -80 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/get-ts-paths-and-root-dir.js.map -30 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.d.ts -178 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-filesystem.js.map -6 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.d.ts -19 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/traverse-unassigned-file-info.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-config.js.map -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/ts-data.js.map -28 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.d.ts -90 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/file-info/unassigned-file-info.js.map -21 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.d.ts -103 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/default-fs.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test1/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test2/customers/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/data/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/admin/booking/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/customers/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-files/test3/holidays/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.js.map -0 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.d.ts -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.js.map -31 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.d.ts -37 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/fs.js.map -6 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.d.ts -18 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/getFs.js.map -34 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.d.ts -228 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/fs/virtual-fs.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.d.ts -6 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/index.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log-level.js.map -7 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.d.ts -48 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/log.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.d.ts -18 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/log/logger.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.d.ts -14 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/after-init.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.d.ts -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/callback.js.map -50 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.d.ts -40 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/init.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.d.ts -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/callback.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.d.ts -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/internal/initialized.js.map -11 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.d.ts -31 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/main/parse-project.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.d.ts -40 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/create-modules.js.map -25 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.d.ts -52 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/file.info.js.map -4 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.d.ts -15 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/fill-file-info-map.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.d.ts -20 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/find-module-paths.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.d.ts -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/format-modules.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.d.ts -33 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/get-project-dirs-from-file-info.js.map -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.d.ts -27 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/module.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.d.ts -18 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/modules/traverse-file-info.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.d.ts -155 0 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/tags/calc-tags-for-module.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.d.ts -15 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-assigned-file-info.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.d.ts -15 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/find-file-info.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.d.ts -16 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/file-infos.js.map -5 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.d.ts -17 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/fixtures/ts-config.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.d.ts -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/in-vfs.js.map -13 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.d.ts -44 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/matchers.js.map -24 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.d.ts -22 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-configurator.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.d.ts -77 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/project-creator.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.d.ts -28 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/sheriff.config.js.map -4 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.d.ts -17 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/test-init.js.map -3 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.d.ts -21 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/traverse-project.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.d.ts -11 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/test/vfs-file-info.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.d.ts -9 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/assert-not-null.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.d.ts -8 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/get.js.map -2 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.d.ts -9 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/throw-if-null.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.d.ts -14 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.js -1 0 backend/node_modules/@softarc/sheriff-core/src/lib/util/wildcard-to-regex.js.map -1 0 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.d.ts -44 0 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.js -1 0 backend/node_modules/@softarc/sheriff-core/src/sheriff.full-spec.js.map -21 0 backend/node_modules/@tsconfig/node10/LICENSE -38 0 backend/node_modules/@tsconfig/node10/README.md -15 0 backend/node_modules/@tsconfig/node10/package.json -14 0 backend/node_modules/@tsconfig/node10/tsconfig.json -21 0 backend/node_modules/@tsconfig/node12/LICENSE -40 0 backend/node_modules/@tsconfig/node12/README.md -1 0 backend/node_modules/@tsconfig/node12/package.json -16 0 backend/node_modules/@tsconfig/node12/tsconfig.json -21 0 backend/node_modules/@tsconfig/node14/LICENSE -40 0 backend/node_modules/@tsconfig/node14/README.md -1 0 backend/node_modules/@tsconfig/node14/package.json -16 0 backend/node_modules/@tsconfig/node14/tsconfig.json -21 0 backend/node_modules/@tsconfig/node16/LICENSE -40 0 backend/node_modules/@tsconfig/node16/README.md -15 0 backend/node_modules/@tsconfig/node16/package.json -16 0 backend/node_modules/@tsconfig/node16/tsconfig.json -21 0 backend/node_modules/@types/body-parser/LICENSE -15 0 backend/node_modules/@types/body-parser/README.md -95 0 backend/node_modules/@types/body-parser/index.d.ts -58 0 backend/node_modules/@types/body-parser/package.json -21 0 backend/node_modules/@types/connect/LICENSE -15 0 backend/node_modules/@types/connect/README.md -91 0 backend/node_modules/@types/connect/index.d.ts -32 0 backend/node_modules/@types/connect/package.json -21 0 backend/node_modules/@types/express-serve-static-core/LICENSE -15 0 backend/node_modules/@types/express-serve-static-core/README.md -1295 0 backend/node_modules/@types/express-serve-static-core/index.d.ts -50 0 backend/node_modules/@types/express-serve-static-core/package.json -21 0 backend/node_modules/@types/express/LICENSE -15 0 backend/node_modules/@types/express/README.md -128 0 backend/node_modules/@types/express/index.d.ts -45 0 backend/node_modules/@types/express/package.json -21 0 backend/node_modules/@types/http-errors/LICENSE -15 0 backend/node_modules/@types/http-errors/README.md -77 0 backend/node_modules/@types/http-errors/index.d.ts -30 0 backend/node_modules/@types/http-errors/package.json -21 0 backend/node_modules/@types/mime/LICENSE -10 0 backend/node_modules/@types/mime/Mime.d.ts -15 0 backend/node_modules/@types/mime/README.md -31 0 backend/node_modules/@types/mime/index.d.ts -7 0 backend/node_modules/@types/mime/lite.d.ts -30 0 backend/node_modules/@types/mime/package.json -21 0 backend/node_modules/@types/node/LICENSE -15 0 backend/node_modules/@types/node/README.md -1040 0 backend/node_modules/@types/node/assert.d.ts -8 0 backend/node_modules/@types/node/assert/strict.d.ts -541 0 backend/node_modules/@types/node/async_hooks.d.ts -2293 0 backend/node_modules/@types/node/buffer.d.ts -1544 0 backend/node_modules/@types/node/child_process.d.ts -578 0 backend/node_modules/@types/node/cluster.d.ts -452 0 backend/node_modules/@types/node/console.d.ts -19 0 backend/node_modules/@types/node/constants.d.ts -4451 0 backend/node_modules/@types/node/crypto.d.ts -596 0 backend/node_modules/@types/node/dgram.d.ts -554 0 backend/node_modules/@types/node/diagnostics_channel.d.ts -865 0 backend/node_modules/@types/node/dns.d.ts -476 0 backend/node_modules/@types/node/dns/promises.d.ts -124 0 backend/node_modules/@types/node/dom-events.d.ts -170 0 backend/node_modules/@types/node/domain.d.ts -931 0 backend/node_modules/@types/node/events.d.ts -4390 0 backend/node_modules/@types/node/fs.d.ts -1264 0 backend/node_modules/@types/node/fs/promises.d.ts -433 0 backend/node_modules/@types/node/globals.d.ts -1 0 backend/node_modules/@types/node/globals.global.d.ts -1908 0 backend/node_modules/@types/node/http.d.ts -2418 0 backend/node_modules/@types/node/http2.d.ts -550 0 backend/node_modules/@types/node/https.d.ts -89 0 backend/node_modules/@types/node/index.d.ts -2746 0 backend/node_modules/@types/node/inspector.d.ts -301 0 backend/node_modules/@types/node/module.d.ts -999 0 backend/node_modules/@types/node/net.d.ts -495 0 backend/node_modules/@types/node/os.d.ts -217 0 backend/node_modules/@types/node/package.json -191 0 backend/node_modules/@types/node/path.d.ts -941 0 backend/node_modules/@types/node/perf_hooks.d.ts -1873 0 backend/node_modules/@types/node/process.d.ts -117 0 backend/node_modules/@types/node/punycode.d.ts -153 0 backend/node_modules/@types/node/querystring.d.ts -540 0 backend/node_modules/@types/node/readline.d.ts -150 0 backend/node_modules/@types/node/readline/promises.d.ts -430 0 backend/node_modules/@types/node/repl.d.ts -153 0 backend/node_modules/@types/node/sea.d.ts -1707 0 backend/node_modules/@types/node/stream.d.ts -12 0 backend/node_modules/@types/node/stream/consumers.d.ts -83 0 backend/node_modules/@types/node/stream/promises.d.ts -367 0 backend/node_modules/@types/node/stream/web.d.ts -67 0 backend/node_modules/@types/node/string_decoder.d.ts -1990 0 backend/node_modules/@types/node/test.d.ts -240 0 backend/node_modules/@types/node/timers.d.ts -97 0 backend/node_modules/@types/node/timers/promises.d.ts -1217 0 backend/node_modules/@types/node/tls.d.ts -197 0 backend/node_modules/@types/node/trace_events.d.ts -208 0 backend/node_modules/@types/node/tty.d.ts -969 0 backend/node_modules/@types/node/url.d.ts -2292 0 backend/node_modules/@types/node/util.d.ts -808 0 backend/node_modules/@types/node/v8.d.ts -922 0 backend/node_modules/@types/node/vm.d.ts -181 0 backend/node_modules/@types/node/wasi.d.ts -694 0 backend/node_modules/@types/node/worker_threads.d.ts -539 0 backend/node_modules/@types/node/zlib.d.ts -21 0 backend/node_modules/@types/qs/LICENSE -15 0 backend/node_modules/@types/qs/README.md -79 0 backend/node_modules/@types/qs/index.d.ts -65 0 backend/node_modules/@types/qs/package.json -21 0 backend/node_modules/@types/range-parser/LICENSE -53 0 backend/node_modules/@types/range-parser/README.md -34 0 backend/node_modules/@types/range-parser/index.d.ts -25 0 backend/node_modules/@types/range-parser/package.json -21 0 backend/node_modules/@types/send/LICENSE -15 0 backend/node_modules/@types/send/README.md -225 0 backend/node_modules/@types/send/index.d.ts -33 0 backend/node_modules/@types/send/package.json -21 0 backend/node_modules/@types/serve-static/LICENSE -15 0 backend/node_modules/@types/serve-static/README.md -107 0 backend/node_modules/@types/serve-static/index.d.ts -39 0 backend/node_modules/@types/serve-static/package.json -17 0 backend/node_modules/@types/strip-bom/README.md -7 0 backend/node_modules/@types/strip-bom/index.d.ts -17 0 backend/node_modules/@types/strip-bom/package.json -22 0 backend/node_modules/@types/strip-bom/types-metadata.json -21 0 backend/node_modules/@types/strip-json-comments/LICENSE -16 0 backend/node_modules/@types/strip-json-comments/README.md -13 0 backend/node_modules/@types/strip-json-comments/index.d.ts -21 0 backend/node_modules/@types/strip-json-comments/package.json -243 0 backend/node_modules/accepts/HISTORY.md -23 0 backend/node_modules/accepts/LICENSE -140 0 backend/node_modules/accepts/README.md -238 0 backend/node_modules/accepts/index.js -47 0 backend/node_modules/accepts/package.json -193 0 backend/node_modules/acorn-walk/CHANGELOG.md -21 0 backend/node_modules/acorn-walk/LICENSE -124 0 backend/node_modules/acorn-walk/README.md -177 0 backend/node_modules/acorn-walk/dist/walk.d.mts -177 0 backend/node_modules/acorn-walk/dist/walk.d.ts -461 0 backend/node_modules/acorn-walk/dist/walk.js -443 0 backend/node_modules/acorn-walk/dist/walk.mjs -50 0 backend/node_modules/acorn-walk/package.json -910 0 backend/node_modules/acorn/CHANGELOG.md -21 0 backend/node_modules/acorn/LICENSE -282 0 backend/node_modules/acorn/README.md -4 0 backend/node_modules/acorn/bin/acorn -856 0 backend/node_modules/acorn/dist/acorn.d.mts -856 0 backend/node_modules/acorn/dist/acorn.d.ts -6065 0 backend/node_modules/acorn/dist/acorn.js -6036 0 backend/node_modules/acorn/dist/acorn.mjs -90 0 backend/node_modules/acorn/dist/bin.js -50 0 backend/node_modules/acorn/package.json -15 0 backend/node_modules/anymatch/LICENSE -87 0 backend/node_modules/anymatch/README.md -20 0 backend/node_modules/anymatch/index.d.ts -104 0 backend/node_modules/anymatch/index.js -48 0 backend/node_modules/anymatch/package.json -21 0 backend/node_modules/arg/LICENSE.md -280 0 backend/node_modules/arg/README.md -31 0 backend/node_modules/arg/index.d.ts -144 0 backend/node_modules/arg/index.js -30 0 backend/node_modules/arg/package.json -21 0 backend/node_modules/array-flatten/LICENSE -43 0 backend/node_modules/array-flatten/README.md -64 0 backend/node_modules/array-flatten/array-flatten.js -39 0 backend/node_modules/array-flatten/package.json -2 0 backend/node_modules/balanced-match/.github/FUNDING.yml -21 0 backend/node_modules/balanced-match/LICENSE.md -97 0 backend/node_modules/balanced-match/README.md -62 0 backend/node_modules/balanced-match/index.js -48 0 backend/node_modules/balanced-match/package.json -263 0 backend/node_modules/binary-extensions/binary-extensions.json -3 0 backend/node_modules/binary-extensions/binary-extensions.json.d.ts -14 0 backend/node_modules/binary-extensions/index.d.ts -1 0 backend/node_modules/binary-extensions/index.js -10 0 backend/node_modules/binary-extensions/license -40 0 backend/node_modules/binary-extensions/package.json -25 0 backend/node_modules/binary-extensions/readme.md -665 0 backend/node_modules/body-parser/HISTORY.md -23 0 backend/node_modules/body-parser/LICENSE -465 0 backend/node_modules/body-parser/README.md -25 0 backend/node_modules/body-parser/SECURITY.md -156 0 backend/node_modules/body-parser/index.js -205 0 backend/node_modules/body-parser/lib/read.js -247 0 backend/node_modules/body-parser/lib/types/json.js -101 0 backend/node_modules/body-parser/lib/types/raw.js -121 0 backend/node_modules/body-parser/lib/types/text.js -284 0 backend/node_modules/body-parser/lib/types/urlencoded.js -56 0 backend/node_modules/body-parser/package.json -21 0 backend/node_modules/brace-expansion/LICENSE -129 0 backend/node_modules/brace-expansion/README.md -201 0 backend/node_modules/brace-expansion/index.js -47 0 backend/node_modules/brace-expansion/package.json -21 0 backend/node_modules/braces/LICENSE -586 0 backend/node_modules/braces/README.md -170 0 backend/node_modules/braces/index.js -60 0 backend/node_modules/braces/lib/compile.js -57 0 backend/node_modules/braces/lib/constants.js -113 0 backend/node_modules/braces/lib/expand.js -331 0 backend/node_modules/braces/lib/parse.js -32 0 backend/node_modules/braces/lib/stringify.js -122 0 backend/node_modules/braces/lib/utils.js -77 0 backend/node_modules/braces/package.json -21 0 backend/node_modules/buffer-from/LICENSE -72 0 backend/node_modules/buffer-from/index.js -19 0 backend/node_modules/buffer-from/package.json -69 0 backend/node_modules/buffer-from/readme.md -97 0 backend/node_modules/bytes/History.md -23 0 backend/node_modules/bytes/LICENSE -152 0 backend/node_modules/bytes/Readme.md -170 0 backend/node_modules/bytes/index.js -42 0 backend/node_modules/bytes/package.json -1 0 backend/node_modules/call-bind/.eslintignore -16 0 backend/node_modules/call-bind/.eslintrc -12 0 backend/node_modules/call-bind/.github/FUNDING.yml -9 0 backend/node_modules/call-bind/.nycrc -93 0 backend/node_modules/call-bind/CHANGELOG.md -21 0 backend/node_modules/call-bind/LICENSE -64 0 backend/node_modules/call-bind/README.md -15 0 backend/node_modules/call-bind/callBound.js -35 0 backend/node_modules/call-bind/index.js -95 0 backend/node_modules/call-bind/package.json -54 0 backend/node_modules/call-bind/test/callBound.js -80 0 backend/node_modules/call-bind/test/index.js -21 0 backend/node_modules/chokidar/LICENSE -308 0 backend/node_modules/chokidar/README.md -973 0 backend/node_modules/chokidar/index.js -66 0 backend/node_modules/chokidar/lib/constants.js -526 0 backend/node_modules/chokidar/lib/fsevents-handler.js -654 0 backend/node_modules/chokidar/lib/nodefs-handler.js -70 0 backend/node_modules/chokidar/package.json -192 0 backend/node_modules/chokidar/types/index.d.ts -4 0 backend/node_modules/concat-map/.travis.yml -18 0 backend/node_modules/concat-map/LICENSE -62 0 backend/node_modules/concat-map/README.markdown -6 0 backend/node_modules/concat-map/example/map.js -13 0 backend/node_modules/concat-map/index.js -43 0 backend/node_modules/concat-map/package.json -39 0 backend/node_modules/concat-map/test/map.js -60 0 backend/node_modules/content-disposition/HISTORY.md -22 0 backend/node_modules/content-disposition/LICENSE -142 0 backend/node_modules/content-disposition/README.md -458 0 backend/node_modules/content-disposition/index.js -44 0 backend/node_modules/content-disposition/package.json -29 0 backend/node_modules/content-type/HISTORY.md -22 0 backend/node_modules/content-type/LICENSE -94 0 backend/node_modules/content-type/README.md -225 0 backend/node_modules/content-type/index.js -42 0 backend/node_modules/content-type/package.json -4 0 backend/node_modules/cookie-signature/.npmignore -38 0 backend/node_modules/cookie-signature/History.md -42 0 backend/node_modules/cookie-signature/Readme.md -51 0 backend/node_modules/cookie-signature/index.js -18 0 backend/node_modules/cookie-signature/package.json -147 0 backend/node_modules/cookie/HISTORY.md -24 0 backend/node_modules/cookie/LICENSE -317 0 backend/node_modules/cookie/README.md -25 0 backend/node_modules/cookie/SECURITY.md -274 0 backend/node_modules/cookie/index.js -44 0 backend/node_modules/cookie/package.json -33 0 backend/node_modules/create-require/CHANGELOG.md -25 0 backend/node_modules/create-require/LICENSE -46 0 backend/node_modules/create-require/README.md -3 0 backend/node_modules/create-require/create-require.d.ts -49 0 backend/node_modules/create-require/create-require.js -39 0 backend/node_modules/create-require/package.json -1 0 backend/node_modules/debug/.coveralls.yml -11 0 backend/node_modules/debug/.eslintrc -9 0 backend/node_modules/debug/.npmignore -14 0 backend/node_modules/debug/.travis.yml -362 0 backend/node_modules/debug/CHANGELOG.md -19 0 backend/node_modules/debug/LICENSE -50 0 backend/node_modules/debug/Makefile -312 0 backend/node_modules/debug/README.md -19 0 backend/node_modules/debug/component.json -70 0 backend/node_modules/debug/karma.conf.js -1 0 backend/node_modules/debug/node.js -49 0 backend/node_modules/debug/package.json -185 0 backend/node_modules/debug/src/browser.js -202 0 backend/node_modules/debug/src/debug.js -10 0 backend/node_modules/debug/src/index.js -15 0 backend/node_modules/debug/src/inspector-log.js -248 0 backend/node_modules/debug/src/node.js -24 0 backend/node_modules/define-data-property/.eslintrc -12 0 backend/node_modules/define-data-property/.github/FUNDING.yml -13 0 backend/node_modules/define-data-property/.nycrc -70 0 backend/node_modules/define-data-property/CHANGELOG.md -21 0 backend/node_modules/define-data-property/LICENSE -67 0 backend/node_modules/define-data-property/README.md -12 0 backend/node_modules/define-data-property/index.d.ts -56 0 backend/node_modules/define-data-property/index.js -106 0 backend/node_modules/define-data-property/package.json -392 0 backend/node_modules/define-data-property/test/index.js -59 0 backend/node_modules/define-data-property/tsconfig.json -103 0 backend/node_modules/depd/History.md -22 0 backend/node_modules/depd/LICENSE -280 0 backend/node_modules/depd/Readme.md -538 0 backend/node_modules/depd/index.js -77 0 backend/node_modules/depd/lib/browser/index.js -45 0 backend/node_modules/depd/package.json -23 0 backend/node_modules/destroy/LICENSE -63 0 backend/node_modules/destroy/README.md -209 0 backend/node_modules/destroy/index.js -48 0 backend/node_modules/destroy/package.json -39 0 backend/node_modules/diff/CONTRIBUTING.md -31 0 backend/node_modules/diff/LICENSE -207 0 backend/node_modules/diff/README.md -1585 0 backend/node_modules/diff/dist/diff.js -38 0 backend/node_modules/diff/dist/diff.min.js -32 0 backend/node_modules/diff/lib/convert/dmp.js -42 0 backend/node_modules/diff/lib/convert/xml.js -45 0 backend/node_modules/diff/lib/diff/array.js -304 0 backend/node_modules/diff/lib/diff/base.js -37 0 backend/node_modules/diff/lib/diff/character.js -41 0 backend/node_modules/diff/lib/diff/css.js -163 0 backend/node_modules/diff/lib/diff/json.js -89 0 backend/node_modules/diff/lib/diff/line.js -41 0 backend/node_modules/diff/lib/diff/sentence.js -107 0 backend/node_modules/diff/lib/diff/word.js -1519 0 backend/node_modules/diff/lib/index.es6.js -216 0 backend/node_modules/diff/lib/index.js -243 0 backend/node_modules/diff/lib/patch/apply.js -247 0 backend/node_modules/diff/lib/patch/create.js -609 0 backend/node_modules/diff/lib/patch/merge.js -156 0 backend/node_modules/diff/lib/patch/parse.js -32 0 backend/node_modules/diff/lib/util/array.js -57 0 backend/node_modules/diff/lib/util/distance-iterator.js -24 0 backend/node_modules/diff/lib/util/params.js -73 0 backend/node_modules/diff/package.json -261 0 backend/node_modules/diff/release-notes.md -3 0 backend/node_modules/diff/runtime.js -10 0 backend/node_modules/dynamic-dedupe/.jshintrc -15 0 backend/node_modules/dynamic-dedupe/.npmignore -23 0 backend/node_modules/dynamic-dedupe/LICENSE -110 0 backend/node_modules/dynamic-dedupe/README.md -10 0 backend/node_modules/dynamic-dedupe/example/deduped.js -7 0 backend/node_modules/dynamic-dedupe/example/not-deduped.js -4 0 backend/node_modules/dynamic-dedupe/example/pack1/common/dep-uno/foo.js -4 0 backend/node_modules/dynamic-dedupe/example/pack2/common/dep-uno/foo.js -80 0 backend/node_modules/dynamic-dedupe/index.js -44 0 backend/node_modules/dynamic-dedupe/package.json -101 0 backend/node_modules/dynamic-dedupe/test/dedupe.js -1 0 backend/node_modules/dynamic-dedupe/test/fixtures/count.js -4 0 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-dos/foo.js -4 0 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-uno/bar.js -4 0 backend/node_modules/dynamic-dedupe/test/fixtures/pack1/common/dep-uno/foo.js -4 0 backend/node_modules/dynamic-dedupe/test/fixtures/pack2/common/dep-uno/bar.js -4 0 backend/node_modules/dynamic-dedupe/test/fixtures/pack2/common/dep-uno/foo.js -22 0 backend/node_modules/ee-first/LICENSE -80 0 backend/node_modules/ee-first/README.md -95 0 backend/node_modules/ee-first/index.js -29 0 backend/node_modules/ee-first/package.json -14 0 backend/node_modules/encodeurl/HISTORY.md -22 0 backend/node_modules/encodeurl/LICENSE -128 0 backend/node_modules/encodeurl/README.md -60 0 backend/node_modules/encodeurl/index.js -40 0 backend/node_modules/encodeurl/package.json -13 0 backend/node_modules/es-define-property/.eslintrc -12 0 backend/node_modules/es-define-property/.github/FUNDING.yml -9 0 backend/node_modules/es-define-property/.nycrc -15 0 backend/node_modules/es-define-property/CHANGELOG.md -21 0 backend/node_modules/es-define-property/LICENSE -49 0 backend/node_modules/es-define-property/README.md -3 0 backend/node_modules/es-define-property/index.d.ts -16 0 backend/node_modules/es-define-property/index.js -81 0 backend/node_modules/es-define-property/package.json -55 0 backend/node_modules/es-define-property/test/index.js -50 0 backend/node_modules/es-define-property/tsconfig.json -5 0 backend/node_modules/es-errors/.eslintrc -12 0 backend/node_modules/es-errors/.github/FUNDING.yml -40 0 backend/node_modules/es-errors/CHANGELOG.md -21 0 backend/node_modules/es-errors/LICENSE -55 0 backend/node_modules/es-errors/README.md -3 0 backend/node_modules/es-errors/eval.d.ts -4 0 backend/node_modules/es-errors/eval.js -3 0 backend/node_modules/es-errors/index.d.ts -4 0 backend/node_modules/es-errors/index.js -80 0 backend/node_modules/es-errors/package.json -3 0 backend/node_modules/es-errors/range.d.ts -4 0 backend/node_modules/es-errors/range.js -3 0 backend/node_modules/es-errors/ref.d.ts -4 0 backend/node_modules/es-errors/ref.js -3 0 backend/node_modules/es-errors/syntax.d.ts -4 0 backend/node_modules/es-errors/syntax.js -19 0 backend/node_modules/es-errors/test/index.js -49 0 backend/node_modules/es-errors/tsconfig.json -3 0 backend/node_modules/es-errors/type.d.ts -4 0 backend/node_modules/es-errors/type.js -3 0 backend/node_modules/es-errors/uri.d.ts -4 0 backend/node_modules/es-errors/uri.js -24 0 backend/node_modules/escape-html/LICENSE -43 0 backend/node_modules/escape-html/Readme.md -78 0 backend/node_modules/escape-html/index.js -24 0 backend/node_modules/escape-html/package.json -83 0 backend/node_modules/etag/HISTORY.md -22 0 backend/node_modules/etag/LICENSE -159 0 backend/node_modules/etag/README.md -131 0 backend/node_modules/etag/index.js -47 0 backend/node_modules/etag/package.json -3615 0 backend/node_modules/express/History.md -24 0 backend/node_modules/express/LICENSE -166 0 backend/node_modules/express/Readme.md -11 0 backend/node_modules/express/index.js -661 0 backend/node_modules/express/lib/application.js -116 0 backend/node_modules/express/lib/express.js -43 0 backend/node_modules/express/lib/middleware/init.js -47 0 backend/node_modules/express/lib/middleware/query.js -525 0 backend/node_modules/express/lib/request.js -1178 0 backend/node_modules/express/lib/response.js -673 0 backend/node_modules/express/lib/router/index.js -181 0 backend/node_modules/express/lib/router/layer.js -230 0 backend/node_modules/express/lib/router/route.js -303 0 backend/node_modules/express/lib/utils.js -182 0 backend/node_modules/express/lib/view.js -98 0 backend/node_modules/express/package.json -21 0 backend/node_modules/fast-glob/LICENSE -830 0 backend/node_modules/fast-glob/README.md -40 0 backend/node_modules/fast-glob/out/index.d.ts -102 0 backend/node_modules/fast-glob/out/index.js -22 0 backend/node_modules/fast-glob/out/managers/tasks.d.ts -110 0 backend/node_modules/fast-glob/out/managers/tasks.js -9 0 backend/node_modules/fast-glob/out/providers/async.d.ts -23 0 backend/node_modules/fast-glob/out/providers/async.js -16 0 backend/node_modules/fast-glob/out/providers/filters/deep.d.ts -62 0 backend/node_modules/fast-glob/out/providers/filters/deep.js -16 0 backend/node_modules/fast-glob/out/providers/filters/entry.d.ts -63 0 backend/node_modules/fast-glob/out/providers/filters/entry.js -8 0 backend/node_modules/fast-glob/out/providers/filters/error.d.ts -15 0 backend/node_modules/fast-glob/out/providers/filters/error.js -33 0 backend/node_modules/fast-glob/out/providers/matchers/matcher.d.ts -45 0 backend/node_modules/fast-glob/out/providers/matchers/matcher.js -4 0 backend/node_modules/fast-glob/out/providers/matchers/partial.d.ts -38 0 backend/node_modules/fast-glob/out/providers/matchers/partial.js -19 0 backend/node_modules/fast-glob/out/providers/provider.d.ts -48 0 backend/node_modules/fast-glob/out/providers/provider.js -11 0 backend/node_modules/fast-glob/out/providers/stream.d.ts -31 0 backend/node_modules/fast-glob/out/providers/stream.js -9 0 backend/node_modules/fast-glob/out/providers/sync.d.ts -23 0 backend/node_modules/fast-glob/out/providers/sync.js -8 0 backend/node_modules/fast-glob/out/providers/transformers/entry.d.ts -26 0 backend/node_modules/fast-glob/out/providers/transformers/entry.js -10 0 backend/node_modules/fast-glob/out/readers/async.d.ts -35 0 backend/node_modules/fast-glob/out/readers/async.js -15 0 backend/node_modules/fast-glob/out/readers/reader.d.ts -33 0 backend/node_modules/fast-glob/out/readers/reader.js -14 0 backend/node_modules/fast-glob/out/readers/stream.d.ts -55 0 backend/node_modules/fast-glob/out/readers/stream.js -12 0 backend/node_modules/fast-glob/out/readers/sync.d.ts -43 0 backend/node_modules/fast-glob/out/readers/sync.js -164 0 backend/node_modules/fast-glob/out/settings.d.ts -59 0 backend/node_modules/fast-glob/out/settings.js -31 0 backend/node_modules/fast-glob/out/types/index.d.ts -2 0 backend/node_modules/fast-glob/out/types/index.js -2 0 backend/node_modules/fast-glob/out/utils/array.d.ts -22 0 backend/node_modules/fast-glob/out/utils/array.js -2 0 backend/node_modules/fast-glob/out/utils/errno.d.ts -7 0 backend/node_modules/fast-glob/out/utils/errno.js -4 0 backend/node_modules/fast-glob/out/utils/fs.d.ts -19 0 backend/node_modules/fast-glob/out/utils/fs.js -8 0 backend/node_modules/fast-glob/out/utils/index.d.ts -17 0 backend/node_modules/fast-glob/out/utils/index.js -13 0 backend/node_modules/fast-glob/out/utils/path.d.ts -68 0 backend/node_modules/fast-glob/out/utils/path.js -47 0 backend/node_modules/fast-glob/out/utils/pattern.d.ts -188 0 backend/node_modules/fast-glob/out/utils/pattern.js -4 0 backend/node_modules/fast-glob/out/utils/stream.d.ts -17 0 backend/node_modules/fast-glob/out/utils/stream.js -2 0 backend/node_modules/fast-glob/out/utils/string.d.ts -11 0 backend/node_modules/fast-glob/out/utils/string.js -81 0 backend/node_modules/fast-glob/package.json -11 0 backend/node_modules/fastq/.github/dependabot.yml -75 0 backend/node_modules/fastq/.github/workflows/ci.yml -13 0 backend/node_modules/fastq/LICENSE -306 0 backend/node_modules/fastq/README.md -66 0 backend/node_modules/fastq/bench.js -14 0 backend/node_modules/fastq/example.js -11 0 backend/node_modules/fastq/example.mjs -38 0 backend/node_modules/fastq/index.d.ts -53 0 backend/node_modules/fastq/package.json -311 0 backend/node_modules/fastq/queue.js -83 0 backend/node_modules/fastq/test/example.ts -248 0 backend/node_modules/fastq/test/promise.js -642 0 backend/node_modules/fastq/test/test.js -11 0 backend/node_modules/fastq/test/tsconfig.json -21 0 backend/node_modules/fill-range/LICENSE -237 0 backend/node_modules/fill-range/README.md -248 0 backend/node_modules/fill-range/index.js -74 0 backend/node_modules/fill-range/package.json -195 0 backend/node_modules/finalhandler/HISTORY.md -22 0 backend/node_modules/finalhandler/LICENSE -147 0 backend/node_modules/finalhandler/README.md -25 0 backend/node_modules/finalhandler/SECURITY.md -336 0 backend/node_modules/finalhandler/index.js -46 0 backend/node_modules/finalhandler/package.json -21 0 backend/node_modules/forwarded/HISTORY.md -22 0 backend/node_modules/forwarded/LICENSE -57 0 backend/node_modules/forwarded/README.md -90 0 backend/node_modules/forwarded/index.js -45 0 backend/node_modules/forwarded/package.json -70 0 backend/node_modules/fresh/HISTORY.md -23 0 backend/node_modules/fresh/LICENSE -119 0 backend/node_modules/fresh/README.md -137 0 backend/node_modules/fresh/index.js -46 0 backend/node_modules/fresh/package.json -43 0 backend/node_modules/fs.realpath/LICENSE -33 0 backend/node_modules/fs.realpath/README.md -66 0 backend/node_modules/fs.realpath/index.js -303 0 backend/node_modules/fs.realpath/old.js -26 0 backend/node_modules/fs.realpath/package.json -22 0 backend/node_modules/fsevents/LICENSE -89 0 backend/node_modules/fsevents/README.md -46 0 backend/node_modules/fsevents/fsevents.d.ts -83 0 backend/node_modules/fsevents/fsevents.js -- - backend/node_modules/fsevents/fsevents.node -62 0 backend/node_modules/fsevents/package.json -21 0 backend/node_modules/function-bind/.eslintrc -12 0 backend/node_modules/function-bind/.github/FUNDING.yml -3 0 backend/node_modules/function-bind/.github/SECURITY.md -13 0 backend/node_modules/function-bind/.nycrc -136 0 backend/node_modules/function-bind/CHANGELOG.md -20 0 backend/node_modules/function-bind/LICENSE -46 0 backend/node_modules/function-bind/README.md -84 0 backend/node_modules/function-bind/implementation.js -5 0 backend/node_modules/function-bind/index.js -87 0 backend/node_modules/function-bind/package.json -9 0 backend/node_modules/function-bind/test/.eslintrc -252 0 backend/node_modules/function-bind/test/index.js -38 0 backend/node_modules/get-intrinsic/.eslintrc -12 0 backend/node_modules/get-intrinsic/.github/FUNDING.yml -9 0 backend/node_modules/get-intrinsic/.nycrc -143 0 backend/node_modules/get-intrinsic/CHANGELOG.md -21 0 backend/node_modules/get-intrinsic/LICENSE -71 0 backend/node_modules/get-intrinsic/README.md -359 0 backend/node_modules/get-intrinsic/index.js -93 0 backend/node_modules/get-intrinsic/package.json -274 0 backend/node_modules/get-intrinsic/test/GetIntrinsic.js -110 0 backend/node_modules/glob-parent/CHANGELOG.md -15 0 backend/node_modules/glob-parent/LICENSE -137 0 backend/node_modules/glob-parent/README.md -42 0 backend/node_modules/glob-parent/index.js -48 0 backend/node_modules/glob-parent/package.json -21 0 backend/node_modules/glob/LICENSE -378 0 backend/node_modules/glob/README.md -238 0 backend/node_modules/glob/common.js -790 0 backend/node_modules/glob/glob.js -55 0 backend/node_modules/glob/package.json -486 0 backend/node_modules/glob/sync.js -16 0 backend/node_modules/gopd/.eslintrc -12 0 backend/node_modules/gopd/.github/FUNDING.yml -25 0 backend/node_modules/gopd/CHANGELOG.md -21 0 backend/node_modules/gopd/LICENSE -40 0 backend/node_modules/gopd/README.md -16 0 backend/node_modules/gopd/index.js -71 0 backend/node_modules/gopd/package.json -35 0 backend/node_modules/gopd/test/index.js -13 0 backend/node_modules/has-property-descriptors/.eslintrc -12 0 backend/node_modules/has-property-descriptors/.github/FUNDING.yml -9 0 backend/node_modules/has-property-descriptors/.nycrc -35 0 backend/node_modules/has-property-descriptors/CHANGELOG.md -21 0 backend/node_modules/has-property-descriptors/LICENSE -43 0 backend/node_modules/has-property-descriptors/README.md -22 0 backend/node_modules/has-property-descriptors/index.js -77 0 backend/node_modules/has-property-descriptors/package.json -57 0 backend/node_modules/has-property-descriptors/test/index.js -5 0 backend/node_modules/has-proto/.eslintrc -12 0 backend/node_modules/has-proto/.github/FUNDING.yml -38 0 backend/node_modules/has-proto/CHANGELOG.md -21 0 backend/node_modules/has-proto/LICENSE -38 0 backend/node_modules/has-proto/README.md -3 0 backend/node_modules/has-proto/index.d.ts -15 0 backend/node_modules/has-proto/index.js -78 0 backend/node_modules/has-proto/package.json -19 0 backend/node_modules/has-proto/test/index.js -49 0 backend/node_modules/has-proto/tsconfig.json -11 0 backend/node_modules/has-symbols/.eslintrc -12 0 backend/node_modules/has-symbols/.github/FUNDING.yml -9 0 backend/node_modules/has-symbols/.nycrc -75 0 backend/node_modules/has-symbols/CHANGELOG.md -21 0 backend/node_modules/has-symbols/LICENSE -46 0 backend/node_modules/has-symbols/README.md -13 0 backend/node_modules/has-symbols/index.js -101 0 backend/node_modules/has-symbols/package.json -42 0 backend/node_modules/has-symbols/shams.js -22 0 backend/node_modules/has-symbols/test/index.js -28 0 backend/node_modules/has-symbols/test/shams/core-js.js -28 0 backend/node_modules/has-symbols/test/shams/get-own-property-symbols.js -56 0 backend/node_modules/has-symbols/test/tests.js -5 0 backend/node_modules/hasown/.eslintrc -12 0 backend/node_modules/hasown/.github/FUNDING.yml -13 0 backend/node_modules/hasown/.nycrc -40 0 backend/node_modules/hasown/CHANGELOG.md -21 0 backend/node_modules/hasown/LICENSE -40 0 backend/node_modules/hasown/README.md -3 0 backend/node_modules/hasown/index.d.ts -8 0 backend/node_modules/hasown/index.js -92 0 backend/node_modules/hasown/package.json -6 0 backend/node_modules/hasown/tsconfig.json -180 0 backend/node_modules/http-errors/HISTORY.md -23 0 backend/node_modules/http-errors/LICENSE -169 0 backend/node_modules/http-errors/README.md -289 0 backend/node_modules/http-errors/index.js -50 0 backend/node_modules/http-errors/package.json -162 0 backend/node_modules/iconv-lite/Changelog.md -21 0 backend/node_modules/iconv-lite/LICENSE -156 0 backend/node_modules/iconv-lite/README.md -555 0 backend/node_modules/iconv-lite/encodings/dbcs-codec.js -176 0 backend/node_modules/iconv-lite/encodings/dbcs-data.js -22 0 backend/node_modules/iconv-lite/encodings/index.js -188 0 backend/node_modules/iconv-lite/encodings/internal.js -72 0 backend/node_modules/iconv-lite/encodings/sbcs-codec.js -451 0 backend/node_modules/iconv-lite/encodings/sbcs-data-generated.js -174 0 backend/node_modules/iconv-lite/encodings/sbcs-data.js -122 0 backend/node_modules/iconv-lite/encodings/tables/big5-added.json -264 0 backend/node_modules/iconv-lite/encodings/tables/cp936.json -273 0 backend/node_modules/iconv-lite/encodings/tables/cp949.json -177 0 backend/node_modules/iconv-lite/encodings/tables/cp950.json -182 0 backend/node_modules/iconv-lite/encodings/tables/eucjp.json -1 0 backend/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json -55 0 backend/node_modules/iconv-lite/encodings/tables/gbk-added.json -125 0 backend/node_modules/iconv-lite/encodings/tables/shiftjis.json -177 0 backend/node_modules/iconv-lite/encodings/utf16.js -290 0 backend/node_modules/iconv-lite/encodings/utf7.js -52 0 backend/node_modules/iconv-lite/lib/bom-handling.js -217 0 backend/node_modules/iconv-lite/lib/extend-node.js -24 0 backend/node_modules/iconv-lite/lib/index.d.ts -153 0 backend/node_modules/iconv-lite/lib/index.js -121 0 backend/node_modules/iconv-lite/lib/streams.js -46 0 backend/node_modules/iconv-lite/package.json -15 0 backend/node_modules/inflight/LICENSE -37 0 backend/node_modules/inflight/README.md -54 0 backend/node_modules/inflight/inflight.js -29 0 backend/node_modules/inflight/package.json -16 0 backend/node_modules/inherits/LICENSE -42 0 backend/node_modules/inherits/README.md -9 0 backend/node_modules/inherits/inherits.js -27 0 backend/node_modules/inherits/inherits_browser.js -29 0 backend/node_modules/inherits/package.json -19 0 backend/node_modules/ipaddr.js/LICENSE -233 0 backend/node_modules/ipaddr.js/README.md -1 0 backend/node_modules/ipaddr.js/ipaddr.min.js -673 0 backend/node_modules/ipaddr.js/lib/ipaddr.js -68 0 backend/node_modules/ipaddr.js/lib/ipaddr.js.d.ts -35 0 backend/node_modules/ipaddr.js/package.json -17 0 backend/node_modules/is-binary-path/index.d.ts -7 0 backend/node_modules/is-binary-path/index.js -9 0 backend/node_modules/is-binary-path/license -40 0 backend/node_modules/is-binary-path/package.json -34 0 backend/node_modules/is-binary-path/readme.md -18 0 backend/node_modules/is-core-module/.eslintrc -9 0 backend/node_modules/is-core-module/.nycrc -195 0 backend/node_modules/is-core-module/CHANGELOG.md -20 0 backend/node_modules/is-core-module/LICENSE -40 0 backend/node_modules/is-core-module/README.md -161 0 backend/node_modules/is-core-module/core.json -69 0 backend/node_modules/is-core-module/index.js -76 0 backend/node_modules/is-core-module/package.json -138 0 backend/node_modules/is-core-module/test/index.js -21 0 backend/node_modules/is-extglob/LICENSE -107 0 backend/node_modules/is-extglob/README.md -20 0 backend/node_modules/is-extglob/index.js -69 0 backend/node_modules/is-extglob/package.json -21 0 backend/node_modules/is-glob/LICENSE -206 0 backend/node_modules/is-glob/README.md -150 0 backend/node_modules/is-glob/index.js -81 0 backend/node_modules/is-glob/package.json -21 0 backend/node_modules/is-number/LICENSE -187 0 backend/node_modules/is-number/README.md -18 0 backend/node_modules/is-number/index.js -82 0 backend/node_modules/is-number/package.json -5 0 backend/node_modules/make-error/LICENSE -112 0 backend/node_modules/make-error/README.md -1 0 backend/node_modules/make-error/dist/make-error.js -47 0 backend/node_modules/make-error/index.d.ts -151 0 backend/node_modules/make-error/index.js -62 0 backend/node_modules/make-error/package.json -22 0 backend/node_modules/media-typer/HISTORY.md -22 0 backend/node_modules/media-typer/LICENSE -81 0 backend/node_modules/media-typer/README.md -270 0 backend/node_modules/media-typer/index.js -26 0 backend/node_modules/media-typer/package.json -21 0 backend/node_modules/merge-descriptors/HISTORY.md -23 0 backend/node_modules/merge-descriptors/LICENSE -48 0 backend/node_modules/merge-descriptors/README.md -60 0 backend/node_modules/merge-descriptors/index.js -32 0 backend/node_modules/merge-descriptors/package.json -21 0 backend/node_modules/merge2/LICENSE -144 0 backend/node_modules/merge2/README.md -144 0 backend/node_modules/merge2/index.js -43 0 backend/node_modules/merge2/package.json -29 0 backend/node_modules/methods/HISTORY.md -24 0 backend/node_modules/methods/LICENSE -51 0 backend/node_modules/methods/README.md -69 0 backend/node_modules/methods/index.js -36 0 backend/node_modules/methods/package.json -21 0 backend/node_modules/micromatch/LICENSE -1024 0 backend/node_modules/micromatch/README.md -474 0 backend/node_modules/micromatch/index.js -119 0 backend/node_modules/micromatch/package.json -507 0 backend/node_modules/mime-db/HISTORY.md -23 0 backend/node_modules/mime-db/LICENSE -100 0 backend/node_modules/mime-db/README.md -8519 0 backend/node_modules/mime-db/db.json -12 0 backend/node_modules/mime-db/index.js -60 0 backend/node_modules/mime-db/package.json -397 0 backend/node_modules/mime-types/HISTORY.md -23 0 backend/node_modules/mime-types/LICENSE -113 0 backend/node_modules/mime-types/README.md -188 0 backend/node_modules/mime-types/index.js -44 0 backend/node_modules/mime-types/package.json -0 0 backend/node_modules/mime/.npmignore -164 0 backend/node_modules/mime/CHANGELOG.md -21 0 backend/node_modules/mime/LICENSE -90 0 backend/node_modules/mime/README.md -8 0 backend/node_modules/mime/cli.js -108 0 backend/node_modules/mime/mime.js -44 0 backend/node_modules/mime/package.json -53 0 backend/node_modules/mime/src/build.js -60 0 backend/node_modules/mime/src/test.js -1 0 backend/node_modules/mime/types.json -15 0 backend/node_modules/minimatch/LICENSE -230 0 backend/node_modules/minimatch/README.md -947 0 backend/node_modules/minimatch/minimatch.js -33 0 backend/node_modules/minimatch/package.json -29 0 backend/node_modules/minimist/.eslintrc -12 0 backend/node_modules/minimist/.github/FUNDING.yml -14 0 backend/node_modules/minimist/.nycrc -298 0 backend/node_modules/minimist/CHANGELOG.md -18 0 backend/node_modules/minimist/LICENSE -121 0 backend/node_modules/minimist/README.md -4 0 backend/node_modules/minimist/example/parse.js -263 0 backend/node_modules/minimist/index.js -75 0 backend/node_modules/minimist/package.json -34 0 backend/node_modules/minimist/test/all_bool.js -177 0 backend/node_modules/minimist/test/bool.js -43 0 backend/node_modules/minimist/test/dash.js -37 0 backend/node_modules/minimist/test/default_bool.js -24 0 backend/node_modules/minimist/test/dotted.js -32 0 backend/node_modules/minimist/test/kv_short.js -33 0 backend/node_modules/minimist/test/long.js -38 0 backend/node_modules/minimist/test/num.js -209 0 backend/node_modules/minimist/test/parse.js -11 0 backend/node_modules/minimist/test/parse_modified.js -64 0 backend/node_modules/minimist/test/proto.js -69 0 backend/node_modules/minimist/test/short.js -17 0 backend/node_modules/minimist/test/stop_early.js -104 0 backend/node_modules/minimist/test/unknown.js -10 0 backend/node_modules/minimist/test/whitespace.js -15 0 backend/node_modules/mkdirp/CHANGELOG.md -21 0 backend/node_modules/mkdirp/LICENSE -68 0 backend/node_modules/mkdirp/bin/cmd.js -31 0 backend/node_modules/mkdirp/index.js -29 0 backend/node_modules/mkdirp/lib/find-made.js -64 0 backend/node_modules/mkdirp/lib/mkdirp-manual.js -39 0 backend/node_modules/mkdirp/lib/mkdirp-native.js -23 0 backend/node_modules/mkdirp/lib/opts-arg.js -29 0 backend/node_modules/mkdirp/lib/path-arg.js -10 0 backend/node_modules/mkdirp/lib/use-native.js -44 0 backend/node_modules/mkdirp/package.json -266 0 backend/node_modules/mkdirp/readme.markdown -152 0 backend/node_modules/ms/index.js -21 0 backend/node_modules/ms/license.md -37 0 backend/node_modules/ms/package.json -51 0 backend/node_modules/ms/readme.md -108 0 backend/node_modules/negotiator/HISTORY.md -24 0 backend/node_modules/negotiator/LICENSE -203 0 backend/node_modules/negotiator/README.md -82 0 backend/node_modules/negotiator/index.js -169 0 backend/node_modules/negotiator/lib/charset.js -184 0 backend/node_modules/negotiator/lib/encoding.js -179 0 backend/node_modules/negotiator/lib/language.js -294 0 backend/node_modules/negotiator/lib/mediaType.js -42 0 backend/node_modules/negotiator/package.json -21 0 backend/node_modules/normalize-path/LICENSE -127 0 backend/node_modules/normalize-path/README.md -35 0 backend/node_modules/normalize-path/index.js -77 0 backend/node_modules/normalize-path/package.json -53 0 backend/node_modules/object-inspect/.eslintrc -12 0 backend/node_modules/object-inspect/.github/FUNDING.yml -13 0 backend/node_modules/object-inspect/.nycrc -404 0 backend/node_modules/object-inspect/CHANGELOG.md -21 0 backend/node_modules/object-inspect/LICENSE -23 0 backend/node_modules/object-inspect/example/all.js -6 0 backend/node_modules/object-inspect/example/circular.js -5 0 backend/node_modules/object-inspect/example/fn.js -10 0 backend/node_modules/object-inspect/example/inspect.js -527 0 backend/node_modules/object-inspect/index.js -20 0 backend/node_modules/object-inspect/package-support.json -104 0 backend/node_modules/object-inspect/package.json -84 0 backend/node_modules/object-inspect/readme.markdown -26 0 backend/node_modules/object-inspect/test-core-js.js -58 0 backend/node_modules/object-inspect/test/bigint.js -15 0 backend/node_modules/object-inspect/test/browser/dom.js -16 0 backend/node_modules/object-inspect/test/circular.js -12 0 backend/node_modules/object-inspect/test/deep.js -53 0 backend/node_modules/object-inspect/test/element.js -48 0 backend/node_modules/object-inspect/test/err.js -29 0 backend/node_modules/object-inspect/test/fakes.js -76 0 backend/node_modules/object-inspect/test/fn.js -17 0 backend/node_modules/object-inspect/test/global.js -15 0 backend/node_modules/object-inspect/test/has.js -15 0 backend/node_modules/object-inspect/test/holes.js -271 0 backend/node_modules/object-inspect/test/indent-option.js -139 0 backend/node_modules/object-inspect/test/inspect.js -12 0 backend/node_modules/object-inspect/test/lowbyte.js -58 0 backend/node_modules/object-inspect/test/number.js -17 0 backend/node_modules/object-inspect/test/quoteStyle.js -40 0 backend/node_modules/object-inspect/test/toStringTag.js -12 0 backend/node_modules/object-inspect/test/undef.js -211 0 backend/node_modules/object-inspect/test/values.js -1 0 backend/node_modules/object-inspect/util.inspect.js -98 0 backend/node_modules/on-finished/HISTORY.md -23 0 backend/node_modules/on-finished/LICENSE -162 0 backend/node_modules/on-finished/README.md -234 0 backend/node_modules/on-finished/index.js -39 0 backend/node_modules/on-finished/package.json -15 0 backend/node_modules/once/LICENSE -79 0 backend/node_modules/once/README.md -42 0 backend/node_modules/once/once.js -33 0 backend/node_modules/once/package.json -58 0 backend/node_modules/parseurl/HISTORY.md -24 0 backend/node_modules/parseurl/LICENSE -133 0 backend/node_modules/parseurl/README.md -158 0 backend/node_modules/parseurl/index.js -40 0 backend/node_modules/parseurl/package.json -20 0 backend/node_modules/path-is-absolute/index.js -21 0 backend/node_modules/path-is-absolute/license -43 0 backend/node_modules/path-is-absolute/package.json -59 0 backend/node_modules/path-is-absolute/readme.md -21 0 backend/node_modules/path-parse/LICENSE -42 0 backend/node_modules/path-parse/README.md -75 0 backend/node_modules/path-parse/index.js -33 0 backend/node_modules/path-parse/package.json -36 0 backend/node_modules/path-to-regexp/History.md -21 0 backend/node_modules/path-to-regexp/LICENSE -35 0 backend/node_modules/path-to-regexp/Readme.md -129 0 backend/node_modules/path-to-regexp/index.js -30 0 backend/node_modules/path-to-regexp/package.json -136 0 backend/node_modules/picomatch/CHANGELOG.md -21 0 backend/node_modules/picomatch/LICENSE -708 0 backend/node_modules/picomatch/README.md -3 0 backend/node_modules/picomatch/index.js -179 0 backend/node_modules/picomatch/lib/constants.js -1091 0 backend/node_modules/picomatch/lib/parse.js -342 0 backend/node_modules/picomatch/lib/picomatch.js -391 0 backend/node_modules/picomatch/lib/scan.js -64 0 backend/node_modules/picomatch/lib/utils.js -81 0 backend/node_modules/picomatch/package.json -4379 0 backend/node_modules/prettier/LICENSE -109 0 backend/node_modules/prettier/README.md -64 0 backend/node_modules/prettier/bin/prettier.cjs -243 0 backend/node_modules/prettier/doc.d.ts -1319 0 backend/node_modules/prettier/doc.js -1291 0 backend/node_modules/prettier/doc.mjs -648 0 backend/node_modules/prettier/index.cjs -941 0 backend/node_modules/prettier/index.d.ts -22795 0 backend/node_modules/prettier/index.mjs -3884 0 backend/node_modules/prettier/internal/cli.mjs -198 0 backend/node_modules/prettier/package.json -6 0 backend/node_modules/prettier/plugins/acorn.d.ts -15 0 backend/node_modules/prettier/plugins/acorn.js -15 0 backend/node_modules/prettier/plugins/acorn.mjs -8 0 backend/node_modules/prettier/plugins/angular.d.ts -1 0 backend/node_modules/prettier/plugins/angular.js -1 0 backend/node_modules/prettier/plugins/angular.mjs -18 0 backend/node_modules/prettier/plugins/babel.d.ts -15 0 backend/node_modules/prettier/plugins/babel.js -15 0 backend/node_modules/prettier/plugins/babel.mjs -1 0 backend/node_modules/prettier/plugins/estree.d.ts -36 0 backend/node_modules/prettier/plugins/estree.js -36 0 backend/node_modules/prettier/plugins/estree.mjs -5 0 backend/node_modules/prettier/plugins/flow.d.ts -19 0 backend/node_modules/prettier/plugins/flow.js -19 0 backend/node_modules/prettier/plugins/flow.mjs -5 0 backend/node_modules/prettier/plugins/glimmer.d.ts -30 0 backend/node_modules/prettier/plugins/glimmer.js -30 0 backend/node_modules/prettier/plugins/glimmer.mjs -5 0 backend/node_modules/prettier/plugins/graphql.d.ts -29 0 backend/node_modules/prettier/plugins/graphql.js -29 0 backend/node_modules/prettier/plugins/graphql.mjs -8 0 backend/node_modules/prettier/plugins/html.d.ts -22 0 backend/node_modules/prettier/plugins/html.js -22 0 backend/node_modules/prettier/plugins/html.mjs -7 0 backend/node_modules/prettier/plugins/markdown.d.ts -62 0 backend/node_modules/prettier/plugins/markdown.js -62 0 backend/node_modules/prettier/plugins/markdown.mjs -5 0 backend/node_modules/prettier/plugins/meriyah.d.ts -4 0 backend/node_modules/prettier/plugins/meriyah.js -4 0 backend/node_modules/prettier/plugins/meriyah.mjs -7 0 backend/node_modules/prettier/plugins/postcss.d.ts -52 0 backend/node_modules/prettier/plugins/postcss.js -52 0 backend/node_modules/prettier/plugins/postcss.mjs -5 0 backend/node_modules/prettier/plugins/typescript.d.ts -20 0 backend/node_modules/prettier/plugins/typescript.js -20 0 backend/node_modules/prettier/plugins/typescript.mjs -5 0 backend/node_modules/prettier/plugins/yaml.d.ts -161 0 backend/node_modules/prettier/plugins/yaml.js -161 0 backend/node_modules/prettier/plugins/yaml.mjs -33 0 backend/node_modules/prettier/standalone.d.ts -35 0 backend/node_modules/prettier/standalone.js -35 0 backend/node_modules/prettier/standalone.mjs -161 0 backend/node_modules/proxy-addr/HISTORY.md -22 0 backend/node_modules/proxy-addr/LICENSE -139 0 backend/node_modules/proxy-addr/README.md -327 0 backend/node_modules/proxy-addr/index.js -47 0 backend/node_modules/proxy-addr/package.json -43 0 backend/node_modules/qs/.editorconfig -38 0 backend/node_modules/qs/.eslintrc -12 0 backend/node_modules/qs/.github/FUNDING.yml -13 0 backend/node_modules/qs/.nycrc -546 0 backend/node_modules/qs/CHANGELOG.md -29 0 backend/node_modules/qs/LICENSE.md -625 0 backend/node_modules/qs/README.md -2054 0 backend/node_modules/qs/dist/qs.js -23 0 backend/node_modules/qs/lib/formats.js -11 0 backend/node_modules/qs/lib/index.js -263 0 backend/node_modules/qs/lib/parse.js -326 0 backend/node_modules/qs/lib/stringify.js -252 0 backend/node_modules/qs/lib/utils.js -77 0 backend/node_modules/qs/package.json -855 0 backend/node_modules/qs/test/parse.js -909 0 backend/node_modules/qs/test/stringify.js -136 0 backend/node_modules/qs/test/utils.js -20 0 backend/node_modules/queue-microtask/LICENSE -90 0 backend/node_modules/queue-microtask/README.md -2 0 backend/node_modules/queue-microtask/index.d.ts -9 0 backend/node_modules/queue-microtask/index.js -55 0 backend/node_modules/queue-microtask/package.json -56 0 backend/node_modules/range-parser/HISTORY.md -23 0 backend/node_modules/range-parser/LICENSE -84 0 backend/node_modules/range-parser/README.md -162 0 backend/node_modules/range-parser/index.js -44 0 backend/node_modules/range-parser/package.json -308 0 backend/node_modules/raw-body/HISTORY.md -22 0 backend/node_modules/raw-body/LICENSE -223 0 backend/node_modules/raw-body/README.md -24 0 backend/node_modules/raw-body/SECURITY.md -87 0 backend/node_modules/raw-body/index.d.ts -336 0 backend/node_modules/raw-body/index.js -49 0 backend/node_modules/raw-body/package.json -21 0 backend/node_modules/readdirp/LICENSE -122 0 backend/node_modules/readdirp/README.md -43 0 backend/node_modules/readdirp/index.d.ts -287 0 backend/node_modules/readdirp/index.js -122 0 backend/node_modules/readdirp/package.json -37 0 backend/node_modules/resolve/.editorconfig -65 0 backend/node_modules/resolve/.eslintrc -12 0 backend/node_modules/resolve/.github/FUNDING.yml -21 0 backend/node_modules/resolve/LICENSE -3 0 backend/node_modules/resolve/SECURITY.md -3 0 backend/node_modules/resolve/async.js -50 0 backend/node_modules/resolve/bin/resolve -5 0 backend/node_modules/resolve/example/async.js -3 0 backend/node_modules/resolve/example/sync.js -6 0 backend/node_modules/resolve/index.js -329 0 backend/node_modules/resolve/lib/async.js -8 0 backend/node_modules/resolve/lib/caller.js -12 0 backend/node_modules/resolve/lib/core.js -158 0 backend/node_modules/resolve/lib/core.json -24 0 backend/node_modules/resolve/lib/homedir.js -5 0 backend/node_modules/resolve/lib/is-core.js -42 0 backend/node_modules/resolve/lib/node-modules-paths.js -10 0 backend/node_modules/resolve/lib/normalize-options.js -208 0 backend/node_modules/resolve/lib/sync.js -72 0 backend/node_modules/resolve/package.json -301 0 backend/node_modules/resolve/readme.markdown -3 0 backend/node_modules/resolve/sync.js -88 0 backend/node_modules/resolve/test/core.js -29 0 backend/node_modules/resolve/test/dotdot.js -2 0 backend/node_modules/resolve/test/dotdot/abc/index.js -1 0 backend/node_modules/resolve/test/dotdot/index.js -29 0 backend/node_modules/resolve/test/faulty_basedir.js -34 0 backend/node_modules/resolve/test/filter.js -33 0 backend/node_modules/resolve/test/filter_sync.js -127 0 backend/node_modules/resolve/test/home_paths.js -114 0 backend/node_modules/resolve/test/home_paths_sync.js -315 0 backend/node_modules/resolve/test/mock.js -214 0 backend/node_modules/resolve/test/mock_sync.js -56 0 backend/node_modules/resolve/test/module_dir.js -1 0 backend/node_modules/resolve/test/module_dir/xmodules/aaa/index.js -1 0 backend/node_modules/resolve/test/module_dir/ymodules/aaa/index.js -1 0 backend/node_modules/resolve/test/module_dir/zmodules/bbb/main.js -3 0 backend/node_modules/resolve/test/module_dir/zmodules/bbb/package.json -143 0 backend/node_modules/resolve/test/node-modules-paths.js -70 0 backend/node_modules/resolve/test/node_path.js -1 0 backend/node_modules/resolve/test/node_path/x/aaa/index.js -1 0 backend/node_modules/resolve/test/node_path/x/ccc/index.js -1 0 backend/node_modules/resolve/test/node_path/y/bbb/index.js -1 0 backend/node_modules/resolve/test/node_path/y/ccc/index.js -9 0 backend/node_modules/resolve/test/nonstring.js -75 0 backend/node_modules/resolve/test/pathfilter.js -0 0 backend/node_modules/resolve/test/pathfilter/deep_ref/main.js -23 0 backend/node_modules/resolve/test/precedence.js -1 0 backend/node_modules/resolve/test/precedence/aaa.js -1 0 backend/node_modules/resolve/test/precedence/aaa/index.js -1 0 backend/node_modules/resolve/test/precedence/aaa/main.js -1 0 backend/node_modules/resolve/test/precedence/bbb.js -1 0 backend/node_modules/resolve/test/precedence/bbb/main.js -597 0 backend/node_modules/resolve/test/resolver.js -0 0 backend/node_modules/resolve/test/resolver/baz/doom.js -4 0 backend/node_modules/resolve/test/resolver/baz/package.json -1 0 backend/node_modules/resolve/test/resolver/baz/quux.js -0 0 backend/node_modules/resolve/test/resolver/browser_field/a.js -0 0 backend/node_modules/resolve/test/resolver/browser_field/b.js -5 0 backend/node_modules/resolve/test/resolver/browser_field/package.json -1 0 backend/node_modules/resolve/test/resolver/cup.coffee -1 0 backend/node_modules/resolve/test/resolver/dot_main/index.js -3 0 backend/node_modules/resolve/test/resolver/dot_main/package.json -1 0 backend/node_modules/resolve/test/resolver/dot_slash_main/index.js -3 0 backend/node_modules/resolve/test/resolver/dot_slash_main/package.json -0 0 backend/node_modules/resolve/test/resolver/false_main/index.js -4 0 backend/node_modules/resolve/test/resolver/false_main/package.json -1 0 backend/node_modules/resolve/test/resolver/foo.js -2 0 backend/node_modules/resolve/test/resolver/incorrect_main/index.js -3 0 backend/node_modules/resolve/test/resolver/incorrect_main/package.json -7 0 backend/node_modules/resolve/test/resolver/invalid_main/package.json -0 0 backend/node_modules/resolve/test/resolver/mug.coffee -0 0 backend/node_modules/resolve/test/resolver/mug.js -6 0 backend/node_modules/resolve/test/resolver/multirepo/lerna.json -20 0 backend/node_modules/resolve/test/resolver/multirepo/package.json -35 0 backend/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js -14 0 backend/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json -0 0 backend/node_modules/resolve/test/resolver/multirepo/packages/package-b/index.js -14 0 backend/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json -26 0 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js -15 0 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json -12 0 backend/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js -0 0 backend/node_modules/resolve/test/resolver/other_path/lib/other-lib.js -0 0 backend/node_modules/resolve/test/resolver/other_path/root.js -1 0 backend/node_modules/resolve/test/resolver/quux/foo/index.js -1 0 backend/node_modules/resolve/test/resolver/same_names/foo.js -1 0 backend/node_modules/resolve/test/resolver/same_names/foo/index.js -0 0 backend/node_modules/resolve/test/resolver/symlinked/_/node_modules/foo.js -0 0 backend/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep -1 0 backend/node_modules/resolve/test/resolver/symlinked/package/bar.js -3 0 backend/node_modules/resolve/test/resolver/symlinked/package/package.json -5 0 backend/node_modules/resolve/test/resolver/without_basedir/main.js -730 0 backend/node_modules/resolve/test/resolver_sync.js -54 0 backend/node_modules/resolve/test/shadowed_core.js -0 0 backend/node_modules/resolve/test/shadowed_core/node_modules/util/index.js -13 0 backend/node_modules/resolve/test/subdirs.js -176 0 backend/node_modules/resolve/test/symlinks.js -1 0 backend/node_modules/reusify/.coveralls.yml -28 0 backend/node_modules/reusify/.travis.yml -22 0 backend/node_modules/reusify/LICENSE -145 0 backend/node_modules/reusify/README.md -30 0 backend/node_modules/reusify/benchmarks/createNoCodeFunction.js -13 0 backend/node_modules/reusify/benchmarks/fib.js -38 0 backend/node_modules/reusify/benchmarks/reuseNoCodeFunction.js -45 0 backend/node_modules/reusify/package.json -33 0 backend/node_modules/reusify/reusify.js -66 0 backend/node_modules/reusify/test.js -15 0 backend/node_modules/rimraf/LICENSE -101 0 backend/node_modules/rimraf/README.md -50 0 backend/node_modules/rimraf/bin.js -29 0 backend/node_modules/rimraf/package.json -372 0 backend/node_modules/rimraf/rimraf.js -20 0 backend/node_modules/run-parallel/LICENSE -85 0 backend/node_modules/run-parallel/README.md -51 0 backend/node_modules/run-parallel/index.js -58 0 backend/node_modules/run-parallel/package.json -21 0 backend/node_modules/safe-buffer/LICENSE -584 0 backend/node_modules/safe-buffer/README.md -187 0 backend/node_modules/safe-buffer/index.d.ts -65 0 backend/node_modules/safe-buffer/index.js -51 0 backend/node_modules/safe-buffer/package.json -21 0 backend/node_modules/safer-buffer/LICENSE -268 0 backend/node_modules/safer-buffer/Porting-Buffer.md -156 0 backend/node_modules/safer-buffer/Readme.md -58 0 backend/node_modules/safer-buffer/dangerous.js -34 0 backend/node_modules/safer-buffer/package.json -77 0 backend/node_modules/safer-buffer/safer.js -406 0 backend/node_modules/safer-buffer/tests.js -521 0 backend/node_modules/send/HISTORY.md -23 0 backend/node_modules/send/LICENSE -327 0 backend/node_modules/send/README.md -24 0 backend/node_modules/send/SECURITY.md -1143 0 backend/node_modules/send/index.js -162 0 backend/node_modules/send/node_modules/ms/index.js -21 0 backend/node_modules/send/node_modules/ms/license.md -38 0 backend/node_modules/send/node_modules/ms/package.json -59 0 backend/node_modules/send/node_modules/ms/readme.md -62 0 backend/node_modules/send/package.json -471 0 backend/node_modules/serve-static/HISTORY.md -25 0 backend/node_modules/serve-static/LICENSE -257 0 backend/node_modules/serve-static/README.md -210 0 backend/node_modules/serve-static/index.js -42 0 backend/node_modules/serve-static/package.json -27 0 backend/node_modules/set-function-length/.eslintrc -12 0 backend/node_modules/set-function-length/.github/FUNDING.yml -13 0 backend/node_modules/set-function-length/.nycrc -70 0 backend/node_modules/set-function-length/CHANGELOG.md -21 0 backend/node_modules/set-function-length/LICENSE -56 0 backend/node_modules/set-function-length/README.md -9 0 backend/node_modules/set-function-length/env.d.ts -25 0 backend/node_modules/set-function-length/env.js -7 0 backend/node_modules/set-function-length/index.d.ts -42 0 backend/node_modules/set-function-length/index.js -102 0 backend/node_modules/set-function-length/package.json -9 0 backend/node_modules/set-function-length/tsconfig.json -13 0 backend/node_modules/setprototypeof/LICENSE -31 0 backend/node_modules/setprototypeof/README.md -2 0 backend/node_modules/setprototypeof/index.d.ts -17 0 backend/node_modules/setprototypeof/index.js -38 0 backend/node_modules/setprototypeof/package.json -24 0 backend/node_modules/setprototypeof/test/index.js -9 0 backend/node_modules/side-channel/.editorconfig -11 0 backend/node_modules/side-channel/.eslintrc -12 0 backend/node_modules/side-channel/.github/FUNDING.yml -13 0 backend/node_modules/side-channel/.nycrc -95 0 backend/node_modules/side-channel/CHANGELOG.md -21 0 backend/node_modules/side-channel/LICENSE -2 0 backend/node_modules/side-channel/README.md -27 0 backend/node_modules/side-channel/index.d.ts -129 0 backend/node_modules/side-channel/index.js -84 0 backend/node_modules/side-channel/package.json -83 0 backend/node_modules/side-channel/test/index.js -50 0 backend/node_modules/side-channel/tsconfig.json -21 0 backend/node_modules/source-map-support/LICENSE.md -284 0 backend/node_modules/source-map-support/README.md -114 0 backend/node_modules/source-map-support/browser-source-map-support.js -31 0 backend/node_modules/source-map-support/package.json -1 0 backend/node_modules/source-map-support/register-hook-require.js -1 0 backend/node_modules/source-map-support/register.js -625 0 backend/node_modules/source-map-support/source-map-support.js -301 0 backend/node_modules/source-map/CHANGELOG.md -28 0 backend/node_modules/source-map/LICENSE -742 0 backend/node_modules/source-map/README.md -3234 0 backend/node_modules/source-map/dist/source-map.debug.js -3233 0 backend/node_modules/source-map/dist/source-map.js -2 0 backend/node_modules/source-map/dist/source-map.min.js -1 0 backend/node_modules/source-map/dist/source-map.min.js.map -121 0 backend/node_modules/source-map/lib/array-set.js -140 0 backend/node_modules/source-map/lib/base64-vlq.js -67 0 backend/node_modules/source-map/lib/base64.js -111 0 backend/node_modules/source-map/lib/binary-search.js -79 0 backend/node_modules/source-map/lib/mapping-list.js -114 0 backend/node_modules/source-map/lib/quick-sort.js -1145 0 backend/node_modules/source-map/lib/source-map-consumer.js -425 0 backend/node_modules/source-map/lib/source-map-generator.js -413 0 backend/node_modules/source-map/lib/source-node.js -488 0 backend/node_modules/source-map/lib/util.js -73 0 backend/node_modules/source-map/package.json -98 0 backend/node_modules/source-map/source-map.d.ts -8 0 backend/node_modules/source-map/source-map.js -82 0 backend/node_modules/statuses/HISTORY.md -23 0 backend/node_modules/statuses/LICENSE -136 0 backend/node_modules/statuses/README.md -65 0 backend/node_modules/statuses/codes.json -146 0 backend/node_modules/statuses/index.js -49 0 backend/node_modules/statuses/package.json -14 0 backend/node_modules/strip-bom/index.js -21 0 backend/node_modules/strip-bom/license -40 0 backend/node_modules/strip-bom/package.json -36 0 backend/node_modules/strip-bom/readme.md -70 0 backend/node_modules/strip-json-comments/index.js -21 0 backend/node_modules/strip-json-comments/license -42 0 backend/node_modules/strip-json-comments/package.json -64 0 backend/node_modules/strip-json-comments/readme.md -14 0 backend/node_modules/supports-preserve-symlinks-flag/.eslintrc -12 0 backend/node_modules/supports-preserve-symlinks-flag/.github/FUNDING.yml -9 0 backend/node_modules/supports-preserve-symlinks-flag/.nycrc -22 0 backend/node_modules/supports-preserve-symlinks-flag/CHANGELOG.md -21 0 backend/node_modules/supports-preserve-symlinks-flag/LICENSE -42 0 backend/node_modules/supports-preserve-symlinks-flag/README.md -3 0 backend/node_modules/supports-preserve-symlinks-flag/browser.js -9 0 backend/node_modules/supports-preserve-symlinks-flag/index.js -70 0 backend/node_modules/supports-preserve-symlinks-flag/package.json -29 0 backend/node_modules/supports-preserve-symlinks-flag/test/index.js -21 0 backend/node_modules/to-regex-range/LICENSE -305 0 backend/node_modules/to-regex-range/README.md -288 0 backend/node_modules/to-regex-range/index.js -88 0 backend/node_modules/to-regex-range/package.json -9 0 backend/node_modules/toidentifier/HISTORY.md -21 0 backend/node_modules/toidentifier/LICENSE -61 0 backend/node_modules/toidentifier/README.md -32 0 backend/node_modules/toidentifier/index.js -38 0 backend/node_modules/toidentifier/package.json -21 0 backend/node_modules/tree-kill/LICENSE -89 0 backend/node_modules/tree-kill/README.md -14 0 backend/node_modules/tree-kill/cli.js -13 0 backend/node_modules/tree-kill/index.d.ts -118 0 backend/node_modules/tree-kill/index.js -51 0 backend/node_modules/tree-kill/package.json -22 0 backend/node_modules/ts-node-dev/LICENSE -90 0 backend/node_modules/ts-node-dev/README.md -- - backend/node_modules/ts-node-dev/icons/node_error.png -- - backend/node_modules/ts-node-dev/icons/node_info.png -136 0 backend/node_modules/ts-node-dev/lib/bin.js -59 0 backend/node_modules/ts-node-dev/lib/cfg.js -16 0 backend/node_modules/ts-node-dev/lib/check-file-exists.js -140 0 backend/node_modules/ts-node-dev/lib/child-require-hook.js -290 0 backend/node_modules/ts-node-dev/lib/compiler.js -2 0 backend/node_modules/ts-node-dev/lib/dedupe.js -18 0 backend/node_modules/ts-node-dev/lib/get-compiled-path.js -49 0 backend/node_modules/ts-node-dev/lib/get-cwd.js -68 0 backend/node_modules/ts-node-dev/lib/hook.js -286 0 backend/node_modules/ts-node-dev/lib/index.js -35 0 backend/node_modules/ts-node-dev/lib/ipc.js -50 0 backend/node_modules/ts-node-dev/lib/log.js -33 0 backend/node_modules/ts-node-dev/lib/notify.js -33 0 backend/node_modules/ts-node-dev/lib/resolveMain.js -104 0 backend/node_modules/ts-node-dev/lib/wrap.js -101 0 backend/node_modules/ts-node-dev/package.json -21 0 backend/node_modules/ts-node/LICENSE -1442 0 backend/node_modules/ts-node/README.md -8 0 backend/node_modules/ts-node/child-loader.mjs -24 0 backend/node_modules/ts-node/dist-raw/NODE-LICENSE.md -36 0 backend/node_modules/ts-node/dist-raw/README.md -4 0 backend/node_modules/ts-node/dist-raw/node-internal-constants.js -82 0 backend/node_modules/ts-node/dist-raw/node-internal-errors.js -89 0 backend/node_modules/ts-node/dist-raw/node-internal-modules-cjs-helpers.js -593 0 backend/node_modules/ts-node/dist-raw/node-internal-modules-cjs-loader.js -106 0 backend/node_modules/ts-node/dist-raw/node-internal-modules-esm-get_format.js -962 0 backend/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js -44 0 backend/node_modules/ts-node/dist-raw/node-internal-modules-package_json_reader.js -254 0 backend/node_modules/ts-node/dist-raw/node-internal-repl-await.js -58 0 backend/node_modules/ts-node/dist-raw/node-internalBinding-fs.js -9 0 backend/node_modules/ts-node/dist-raw/node-nativemodule.js -103 0 backend/node_modules/ts-node/dist-raw/node-options.js -37 0 backend/node_modules/ts-node/dist-raw/node-primordials.js -9 0 backend/node_modules/ts-node/dist-raw/runmain-hack.js -2 0 backend/node_modules/ts-node/dist/bin-cwd.d.ts -6 0 backend/node_modules/ts-node/dist/bin-cwd.js -1 0 backend/node_modules/ts-node/dist/bin-cwd.js.map -2 0 backend/node_modules/ts-node/dist/bin-esm.d.ts -6 0 backend/node_modules/ts-node/dist/bin-esm.js -1 0 backend/node_modules/ts-node/dist/bin-esm.js.map -2 0 backend/node_modules/ts-node/dist/bin-script-deprecated.d.ts -7 0 backend/node_modules/ts-node/dist/bin-script-deprecated.js -1 0 backend/node_modules/ts-node/dist/bin-script-deprecated.js.map -2 0 backend/node_modules/ts-node/dist/bin-script.d.ts -6 0 backend/node_modules/ts-node/dist/bin-script.js -1 0 backend/node_modules/ts-node/dist/bin-script.js.map -2 0 backend/node_modules/ts-node/dist/bin-transpile.d.ts -6 0 backend/node_modules/ts-node/dist/bin-transpile.js -1 0 backend/node_modules/ts-node/dist/bin-transpile.js.map -11 0 backend/node_modules/ts-node/dist/bin.d.ts -581 0 backend/node_modules/ts-node/dist/bin.js -1 0 backend/node_modules/ts-node/dist/bin.js.map -1 0 backend/node_modules/ts-node/dist/child/argv-payload.d.ts -19 0 backend/node_modules/ts-node/dist/child/argv-payload.js -1 0 backend/node_modules/ts-node/dist/child/argv-payload.js.map -1 0 backend/node_modules/ts-node/dist/child/child-entrypoint.d.ts -24 0 backend/node_modules/ts-node/dist/child/child-entrypoint.js -1 0 backend/node_modules/ts-node/dist/child/child-entrypoint.js.map -1 0 backend/node_modules/ts-node/dist/child/child-loader.d.ts -32 0 backend/node_modules/ts-node/dist/child/child-loader.js -1 0 backend/node_modules/ts-node/dist/child/child-loader.js.map -7 0 backend/node_modules/ts-node/dist/child/child-require.d.ts -22 0 backend/node_modules/ts-node/dist/child/child-require.js -1 0 backend/node_modules/ts-node/dist/child/child-require.js.map -1 0 backend/node_modules/ts-node/dist/child/spawn-child.d.ts -49 0 backend/node_modules/ts-node/dist/child/spawn-child.js -1 0 backend/node_modules/ts-node/dist/child/spawn-child.js.map -1 0 backend/node_modules/ts-node/dist/cjs-resolve-hooks.d.ts -29 0 backend/node_modules/ts-node/dist/cjs-resolve-hooks.js -1 0 backend/node_modules/ts-node/dist/cjs-resolve-hooks.js.map -1 0 backend/node_modules/ts-node/dist/configuration.d.ts -308 0 backend/node_modules/ts-node/dist/configuration.js -1 0 backend/node_modules/ts-node/dist/configuration.js.map -53 0 backend/node_modules/ts-node/dist/esm.d.ts -228 0 backend/node_modules/ts-node/dist/esm.js -1 0 backend/node_modules/ts-node/dist/esm.js.map -1 0 backend/node_modules/ts-node/dist/file-extensions.d.ts -133 0 backend/node_modules/ts-node/dist/file-extensions.js -1 0 backend/node_modules/ts-node/dist/file-extensions.js.map -332 0 backend/node_modules/ts-node/dist/index.d.ts -953 0 backend/node_modules/ts-node/dist/index.js -1 0 backend/node_modules/ts-node/dist/index.js.map -1 0 backend/node_modules/ts-node/dist/module-type-classifier.d.ts -64 0 backend/node_modules/ts-node/dist/module-type-classifier.js -1 0 backend/node_modules/ts-node/dist/module-type-classifier.js.map -1 0 backend/node_modules/ts-node/dist/node-module-type-classifier.d.ts -39 0 backend/node_modules/ts-node/dist/node-module-type-classifier.js -1 0 backend/node_modules/ts-node/dist/node-module-type-classifier.js.map -78 0 backend/node_modules/ts-node/dist/repl.d.ts -561 0 backend/node_modules/ts-node/dist/repl.js -1 0 backend/node_modules/ts-node/dist/repl.js.map -1 0 backend/node_modules/ts-node/dist/resolver-functions.d.ts -143 0 backend/node_modules/ts-node/dist/resolver-functions.js -1 0 backend/node_modules/ts-node/dist/resolver-functions.js.map -11 0 backend/node_modules/ts-node/dist/transpilers/swc.d.ts -218 0 backend/node_modules/ts-node/dist/transpilers/swc.js -1 0 backend/node_modules/ts-node/dist/transpilers/swc.js.map -35 0 backend/node_modules/ts-node/dist/transpilers/types.d.ts -3 0 backend/node_modules/ts-node/dist/transpilers/types.js -1 0 backend/node_modules/ts-node/dist/transpilers/types.js.map -63 0 backend/node_modules/ts-node/dist/ts-compiler-types.d.ts -3 0 backend/node_modules/ts-node/dist/ts-compiler-types.js -1 0 backend/node_modules/ts-node/dist/ts-compiler-types.js.map -6 0 backend/node_modules/ts-node/dist/ts-internals.d.ts -321 0 backend/node_modules/ts-node/dist/ts-internals.js -1 0 backend/node_modules/ts-node/dist/ts-internals.js.map -1 0 backend/node_modules/ts-node/dist/ts-transpile-module.d.ts -100 0 backend/node_modules/ts-node/dist/ts-transpile-module.js -1 0 backend/node_modules/ts-node/dist/ts-transpile-module.js.map -13 0 backend/node_modules/ts-node/dist/tsconfig-schema.d.ts -3 0 backend/node_modules/ts-node/dist/tsconfig-schema.js -1 0 backend/node_modules/ts-node/dist/tsconfig-schema.js.map -1 0 backend/node_modules/ts-node/dist/tsconfigs.d.ts -36 0 backend/node_modules/ts-node/dist/tsconfigs.js -1 0 backend/node_modules/ts-node/dist/tsconfigs.js.map -4 0 backend/node_modules/ts-node/dist/util.d.ts -175 0 backend/node_modules/ts-node/dist/util.js -1 0 backend/node_modules/ts-node/dist/util.js.map -8 0 backend/node_modules/ts-node/esm.mjs -8 0 backend/node_modules/ts-node/esm/transpile-only.mjs -3 0 backend/node_modules/ts-node/node10/tsconfig.json -3 0 backend/node_modules/ts-node/node12/tsconfig.json -3 0 backend/node_modules/ts-node/node14/tsconfig.json -3 0 backend/node_modules/ts-node/node16/tsconfig.json -182 0 backend/node_modules/ts-node/package.json -3 0 backend/node_modules/ts-node/register/files.js -1 0 backend/node_modules/ts-node/register/index.js -3 0 backend/node_modules/ts-node/register/transpile-only.js -3 0 backend/node_modules/ts-node/register/type-check.js -1 0 backend/node_modules/ts-node/transpilers/swc-experimental.js -1 0 backend/node_modules/ts-node/transpilers/swc.js -183 0 backend/node_modules/ts-node/tsconfig.schema.json -1326 0 backend/node_modules/ts-node/tsconfig.schemastore-schema.json -22 0 backend/node_modules/tsconfig/LICENSE -50 0 backend/node_modules/tsconfig/README.md -13 0 backend/node_modules/tsconfig/dist/tsconfig.d.ts -159 0 backend/node_modules/tsconfig/dist/tsconfig.js -1 0 backend/node_modules/tsconfig/dist/tsconfig.js.map -0 0 backend/node_modules/tsconfig/dist/tsconfig.spec.d.ts -140 0 backend/node_modules/tsconfig/dist/tsconfig.spec.js -1 0 backend/node_modules/tsconfig/dist/tsconfig.spec.js.map -63 0 backend/node_modules/tsconfig/package.json -259 0 backend/node_modules/type-is/HISTORY.md -23 0 backend/node_modules/type-is/LICENSE -170 0 backend/node_modules/type-is/README.md -266 0 backend/node_modules/type-is/index.js -45 0 backend/node_modules/type-is/package.json -55 0 backend/node_modules/typescript/LICENSE.txt -50 0 backend/node_modules/typescript/README.md -41 0 backend/node_modules/typescript/SECURITY.md -193 0 backend/node_modules/typescript/ThirdPartyNoticeText.txt -2 0 backend/node_modules/typescript/bin/tsc -2 0 backend/node_modules/typescript/bin/tsserver -90 0 backend/node_modules/typescript/lib/cancellationToken.js -1880 0 backend/node_modules/typescript/lib/cs/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/de/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/es/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/fr/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/it/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/ja/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/ko/diagnosticMessages.generated.json -22 0 backend/node_modules/typescript/lib/lib.d.ts -386 0 backend/node_modules/typescript/lib/lib.decorators.d.ts -22 0 backend/node_modules/typescript/lib/lib.decorators.legacy.d.ts -33 0 backend/node_modules/typescript/lib/lib.dom.asynciterable.d.ts -28596 0 backend/node_modules/typescript/lib/lib.dom.d.ts -475 0 backend/node_modules/typescript/lib/lib.dom.iterable.d.ts -147 0 backend/node_modules/typescript/lib/lib.es2015.collection.d.ts -597 0 backend/node_modules/typescript/lib/lib.es2015.core.d.ts -28 0 backend/node_modules/typescript/lib/lib.es2015.d.ts -77 0 backend/node_modules/typescript/lib/lib.es2015.generator.d.ts -495 0 backend/node_modules/typescript/lib/lib.es2015.iterable.d.ts -81 0 backend/node_modules/typescript/lib/lib.es2015.promise.d.ts -128 0 backend/node_modules/typescript/lib/lib.es2015.proxy.d.ts -144 0 backend/node_modules/typescript/lib/lib.es2015.reflect.d.ts -46 0 backend/node_modules/typescript/lib/lib.es2015.symbol.d.ts -326 0 backend/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts -116 0 backend/node_modules/typescript/lib/lib.es2016.array.include.d.ts -21 0 backend/node_modules/typescript/lib/lib.es2016.d.ts -23 0 backend/node_modules/typescript/lib/lib.es2016.full.d.ts -31 0 backend/node_modules/typescript/lib/lib.es2016.intl.d.ts -25 0 backend/node_modules/typescript/lib/lib.es2017.d.ts -31 0 backend/node_modules/typescript/lib/lib.es2017.date.d.ts -23 0 backend/node_modules/typescript/lib/lib.es2017.full.d.ts -44 0 backend/node_modules/typescript/lib/lib.es2017.intl.d.ts -49 0 backend/node_modules/typescript/lib/lib.es2017.object.d.ts -135 0 backend/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts -45 0 backend/node_modules/typescript/lib/lib.es2017.string.d.ts -53 0 backend/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts -77 0 backend/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts -43 0 backend/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2018.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2018.full.d.ts -83 0 backend/node_modules/typescript/lib/lib.es2018.intl.d.ts -30 0 backend/node_modules/typescript/lib/lib.es2018.promise.d.ts -37 0 backend/node_modules/typescript/lib/lib.es2018.regexp.d.ts -79 0 backend/node_modules/typescript/lib/lib.es2019.array.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2019.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2019.full.d.ts -23 0 backend/node_modules/typescript/lib/lib.es2019.intl.d.ts -33 0 backend/node_modules/typescript/lib/lib.es2019.object.d.ts -37 0 backend/node_modules/typescript/lib/lib.es2019.string.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2019.symbol.d.ts -727 0 backend/node_modules/typescript/lib/lib.es2020.bigint.d.ts -27 0 backend/node_modules/typescript/lib/lib.es2020.d.ts -42 0 backend/node_modules/typescript/lib/lib.es2020.date.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2020.full.d.ts -474 0 backend/node_modules/typescript/lib/lib.es2020.intl.d.ts -28 0 backend/node_modules/typescript/lib/lib.es2020.number.d.ts -47 0 backend/node_modules/typescript/lib/lib.es2020.promise.d.ts -97 0 backend/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts -42 0 backend/node_modules/typescript/lib/lib.es2020.string.d.ts -37 0 backend/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts -23 0 backend/node_modules/typescript/lib/lib.es2021.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2021.full.d.ts -166 0 backend/node_modules/typescript/lib/lib.es2021.intl.d.ts -48 0 backend/node_modules/typescript/lib/lib.es2021.promise.d.ts -33 0 backend/node_modules/typescript/lib/lib.es2021.string.d.ts -76 0 backend/node_modules/typescript/lib/lib.es2021.weakref.d.ts -121 0 backend/node_modules/typescript/lib/lib.es2022.array.d.ts -26 0 backend/node_modules/typescript/lib/lib.es2022.d.ts -73 0 backend/node_modules/typescript/lib/lib.es2022.error.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2022.full.d.ts -117 0 backend/node_modules/typescript/lib/lib.es2022.intl.d.ts -26 0 backend/node_modules/typescript/lib/lib.es2022.object.d.ts -39 0 backend/node_modules/typescript/lib/lib.es2022.regexp.d.ts -39 0 backend/node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts -25 0 backend/node_modules/typescript/lib/lib.es2022.string.d.ts -924 0 backend/node_modules/typescript/lib/lib.es2023.array.d.ts -21 0 backend/node_modules/typescript/lib/lib.es2023.collection.d.ts -22 0 backend/node_modules/typescript/lib/lib.es2023.d.ts -24 0 backend/node_modules/typescript/lib/lib.es2023.full.d.ts -56 0 backend/node_modules/typescript/lib/lib.es2023.intl.d.ts -4585 0 backend/node_modules/typescript/lib/lib.es5.d.ts -23 0 backend/node_modules/typescript/lib/lib.es6.d.ts -35 0 backend/node_modules/typescript/lib/lib.esnext.array.d.ts -106 0 backend/node_modules/typescript/lib/lib.esnext.collection.d.ts -28 0 backend/node_modules/typescript/lib/lib.esnext.d.ts -28 0 backend/node_modules/typescript/lib/lib.esnext.decorators.d.ts -185 0 backend/node_modules/typescript/lib/lib.esnext.disposable.d.ts -24 0 backend/node_modules/typescript/lib/lib.esnext.full.d.ts -21 0 backend/node_modules/typescript/lib/lib.esnext.intl.d.ts -29 0 backend/node_modules/typescript/lib/lib.esnext.object.d.ts -35 0 backend/node_modules/typescript/lib/lib.esnext.promise.d.ts -25 0 backend/node_modules/typescript/lib/lib.esnext.regexp.d.ts -29 0 backend/node_modules/typescript/lib/lib.esnext.string.d.ts -322 0 backend/node_modules/typescript/lib/lib.scripthost.d.ts -33 0 backend/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts -9431 0 backend/node_modules/typescript/lib/lib.webworker.d.ts -23 0 backend/node_modules/typescript/lib/lib.webworker.importscripts.d.ts -276 0 backend/node_modules/typescript/lib/lib.webworker.iterable.d.ts -1880 0 backend/node_modules/typescript/lib/pl/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/ru/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/tr/diagnosticMessages.generated.json -129810 0 backend/node_modules/typescript/lib/tsc.js -621 0 backend/node_modules/typescript/lib/tsserver.js -17 0 backend/node_modules/typescript/lib/tsserverlibrary.d.ts -21 0 backend/node_modules/typescript/lib/tsserverlibrary.js -497 0 backend/node_modules/typescript/lib/typesMap.json -11240 0 backend/node_modules/typescript/lib/typescript.d.ts -195005 0 backend/node_modules/typescript/lib/typescript.js -236 0 backend/node_modules/typescript/lib/typingsInstaller.js -53 0 backend/node_modules/typescript/lib/watchGuard.js -1880 0 backend/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json -1880 0 backend/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json -116 0 backend/node_modules/typescript/package.json -21 0 backend/node_modules/undici-types/LICENSE -6 0 backend/node_modules/undici-types/README.md -31 0 backend/node_modules/undici-types/agent.d.ts -43 0 backend/node_modules/undici-types/api.d.ts -29 0 backend/node_modules/undici-types/balanced-pool.d.ts -36 0 backend/node_modules/undici-types/cache.d.ts -108 0 backend/node_modules/undici-types/client.d.ts -34 0 backend/node_modules/undici-types/connector.d.ts -21 0 backend/node_modules/undici-types/content-type.d.ts -28 0 backend/node_modules/undici-types/cookies.d.ts -66 0 backend/node_modules/undici-types/diagnostics-channel.d.ts -255 0 backend/node_modules/undici-types/dispatcher.d.ts -21 0 backend/node_modules/undici-types/env-http-proxy-agent.d.ts -128 0 backend/node_modules/undici-types/errors.d.ts -63 0 backend/node_modules/undici-types/eventsource.d.ts -209 0 backend/node_modules/undici-types/fetch.d.ts -39 0 backend/node_modules/undici-types/file.d.ts -54 0 backend/node_modules/undici-types/filereader.d.ts -108 0 backend/node_modules/undici-types/formdata.d.ts -9 0 backend/node_modules/undici-types/global-dispatcher.d.ts -7 0 backend/node_modules/undici-types/global-origin.d.ts -15 0 backend/node_modules/undici-types/handlers.d.ts -4 0 backend/node_modules/undici-types/header.d.ts -74 0 backend/node_modules/undici-types/index.d.ts -11 0 backend/node_modules/undici-types/interceptors.d.ts -50 0 backend/node_modules/undici-types/mock-agent.d.ts -25 0 backend/node_modules/undici-types/mock-client.d.ts -12 0 backend/node_modules/undici-types/mock-errors.d.ts -93 0 backend/node_modules/undici-types/mock-interceptor.d.ts -25 0 backend/node_modules/undici-types/mock-pool.d.ts -55 0 backend/node_modules/undici-types/package.json -71 0 backend/node_modules/undici-types/patch.d.ts -19 0 backend/node_modules/undici-types/pool-stats.d.ts -39 0 backend/node_modules/undici-types/pool.d.ts -28 0 backend/node_modules/undici-types/proxy-agent.d.ts -60 0 backend/node_modules/undici-types/readable.d.ts -11 0 backend/node_modules/undici-types/retry-agent.d.ts -116 0 backend/node_modules/undici-types/retry-handler.d.ts -18 0 backend/node_modules/undici-types/util.d.ts -224 0 backend/node_modules/undici-types/webidl.d.ts -152 0 backend/node_modules/undici-types/websocket.d.ts -4 0 backend/node_modules/unpipe/HISTORY.md -22 0 backend/node_modules/unpipe/LICENSE -43 0 backend/node_modules/unpipe/README.md -69 0 backend/node_modules/unpipe/index.js -27 0 backend/node_modules/unpipe/package.json -9 0 backend/node_modules/utils-merge/.npmignore -20 0 backend/node_modules/utils-merge/LICENSE -34 0 backend/node_modules/utils-merge/README.md -23 0 backend/node_modules/utils-merge/index.js -40 0 backend/node_modules/utils-merge/package.json -53 0 backend/node_modules/v8-compile-cache-lib/CHANGELOG.md -21 0 backend/node_modules/v8-compile-cache-lib/LICENSE -60 0 backend/node_modules/v8-compile-cache-lib/README.md -35 0 backend/node_modules/v8-compile-cache-lib/package.json -7 0 backend/node_modules/v8-compile-cache-lib/v8-compile-cache.d.ts -391 0 backend/node_modules/v8-compile-cache-lib/v8-compile-cache.js -39 0 backend/node_modules/vary/HISTORY.md -22 0 backend/node_modules/vary/LICENSE -101 0 backend/node_modules/vary/README.md -149 0 backend/node_modules/vary/index.js -43 0 backend/node_modules/vary/package.json -15 0 backend/node_modules/wrappy/LICENSE -36 0 backend/node_modules/wrappy/README.md -29 0 backend/node_modules/wrappy/package.json -33 0 backend/node_modules/wrappy/wrappy.js -30 0 backend/node_modules/xtend/.jshintrc -20 0 backend/node_modules/xtend/LICENSE -32 0 backend/node_modules/xtend/README.md -19 0 backend/node_modules/xtend/immutable.js -17 0 backend/node_modules/xtend/mutable.js -55 0 backend/node_modules/xtend/package.json -103 0 backend/node_modules/xtend/test.js -65 0 backend/node_modules/yn/index.d.ts -33 0 backend/node_modules/yn/index.js -105 0 backend/node_modules/yn/lenient.js -9 0 backend/node_modules/yn/license -42 0 backend/node_modules/yn/package.json -83 0 backend/node_modules/yn/readme.md -1 1 frontend/src/app/app.component.ts -4 4 frontend/src/app/app.routes.ts -1 1 frontend/src/app/{filter-tree => data}/config.service.ts -2 2 frontend/src/app/{coupling => data}/coupling.service.ts -1 1 frontend/src/app/{filter-tree => data}/folder.service.ts -1 1 frontend/src/app/{coupling => data}/module.service.ts -1 1 frontend/src/app/{team-alignment => data}/team-alignment.service.ts -2 2 frontend/src/app/{coupling/graph => features/coupling}/graph.adapter.ts -0 0 frontend/src/app/{coupling/graph => features/coupling}/graph.component.css -0 0 frontend/src/app/{coupling/graph => features/coupling}/graph.component.html -1 1 frontend/src/app/{coupling/graph => features/coupling}/graph.component.spec.ts -4 4 frontend/src/app/{coupling/graph => features/coupling}/graph.component.ts -0 0 frontend/src/app/{coupling/graph => features/coupling}/graph.ts -0 0 frontend/src/app/{ => features}/hotspot/hotspot-result.ts -0 0 frontend/src/app/{ => features}/hotspot/hotspot.component.css -0 0 frontend/src/app/{ => features}/hotspot/hotspot.component.html -0 0 frontend/src/app/{ => features}/hotspot/hotspot.component.spec.ts -6 6 frontend/src/app/{ => features}/hotspot/hotspot.component.ts -1 1 frontend/src/app/{ => features}/hotspot/hotspot.service.ts -2 2 frontend/src/app/{ => features}/team-alignment/team-alignment-chart.ts -0 0 frontend/src/app/features/team-alignment/team-alignment.component.css -0 0 frontend/src/app/{ => features}/team-alignment/team-alignment.component.html -0 0 frontend/src/app/{ => features}/team-alignment/team-alignment.component.spec.ts -15 25 frontend/src/app/{ => features}/team-alignment/team-alignment.component.ts -0 0 frontend/src/app/{filter-tree => model}/config.ts -0 0 frontend/src/app/{coupling => model}/coupling-result.ts -0 0 frontend/src/app/{filter-tree => model}/folder.ts -0 0 frontend/src/app/{coupling/graph => model}/graph-type.ts -0 0 frontend/src/app/{coupling => model}/module-info.ts -0 0 frontend/src/app/{team-alignment => model}/team-alignment-result.ts -0 0 frontend/src/app/{ => shell}/filter-tree/filter-tree.component.css -0 0 frontend/src/app/{ => shell}/filter-tree/filter-tree.component.html -0 0 frontend/src/app/{ => shell}/filter-tree/filter-tree.component.spec.ts -5 5 frontend/src/app/{ => shell}/filter-tree/filter-tree.component.ts -0 0 frontend/src/app/{ => shell}/nav/nav.component.css -0 0 frontend/src/app/{ => shell}/nav/nav.component.html -0 0 frontend/src/app/{ => shell}/nav/nav.component.spec.ts -1 1 frontend/src/app/{ => shell}/nav/nav.component.ts -0 0 frontend/src/app/{ => utils}/event.service.ts - -"Manfred Steyer ,Mon Sep 2 02:13:39 2024 +0200 88266c6c6f8f1cf03c0b83922ea09f77d2ea17c3,feat: add entry point for cli workspace with projects folder" -1 1 backend/src/infrastructure/deps.ts - -"Manfred Steyer ,Mon Sep 2 02:03:16 2024 +0200 033f653317e469a4b4efff34fe52a04693b08081,refactor: add prettier" -4 9 backend/.detective/config.json -64 232 backend/.detective/deps.json -6 0 backend/.prettierrc -317 190 backend/dist/express.js -24 19 backend/dist/index.js -34 34 backend/dist/infrastructure/config.js -56 50 backend/dist/infrastructure/deps.js -73 54 backend/dist/infrastructure/git.js -2 2 backend/dist/model/config.js -2 2 backend/dist/model/deps.js -4 4 backend/dist/model/limits.js -8 8 backend/dist/options/options.js -46 51 backend/dist/options/parse-options.js -19 18 backend/dist/options/validate-options.js -193 80 backend/dist/services/change-coupling.js -63 61 backend/dist/services/coupling.js -79 53 backend/dist/services/folders.js -304 151 backend/dist/services/hotspot.js -18 18 backend/dist/services/module-info.js -237 121 backend/dist/services/team-alignment.js -75 49 backend/dist/utils/complexity.js -261 144 backend/dist/utils/git-parser.js -5 3 backend/dist/utils/matrix.js -10 10 backend/dist/utils/normalize-folder.js -31 24 backend/dist/utils/open.js -4 4 backend/dist/utils/round.js -3 3 backend/dist/utils/to-percent.js -1 1 backend/forensic.conf.json -17 0 backend/package-lock.json -1 0 backend/package.json -3632 1 backend/public/chunk-2FMXBR5T.js -10784 7 backend/public/chunk-HIPB54JD.js -11131 7 backend/public/chunk-HQFIBEV7.js -3632 1 backend/public/chunk-OVZZS7I2.js -10644 7 backend/public/chunk-QWS3MURL.js -3632 1 backend/public/chunk-UOJ4IBH7.js -1538 12 backend/public/index.html -77830 24 backend/public/main-4V2HQDRI.js -458 1 backend/public/main-AIZTFC63.css -62669 19 backend/public/main-C2TU7PNS.js -62672 19 backend/public/main-DAYCMAFW.js -62703 19 backend/public/main-K3WBRXWH.js -73386 23 backend/public/main-MTHSAXW3.js -62704 19 backend/public/main-UJURQ3JB.js -62672 19 backend/public/main-UO5QPOB3.js -2030 2 backend/public/polyfills-SCHOHYNV.js -2900 1 backend/public/styles-KWQ3FGSD.css -2442 1 backend/public/styles-OPAWKEW6.css -2913 1 backend/public/styles-SVPGZYNK.css -1 1 backend/readme.md -49 49 backend/src/express.ts -11 11 backend/src/index.ts -18 28 backend/src/infrastructure/config.ts -9 15 backend/src/infrastructure/deps.ts -54 44 backend/src/infrastructure/git.ts -6 6 backend/src/infrastructure/log.ts -1 1 backend/src/infrastructure/paths.ts -5 5 backend/src/infrastructure/tree-hash.ts -4 4 backend/src/model/config.ts -5 5 backend/src/model/deps.ts -5 5 backend/src/model/limits.ts -14 14 backend/src/options/options.ts -32 41 backend/src/options/parse-options.ts -3 3 backend/src/options/validate-options.ts -53 50 backend/src/services/change-coupling.ts -10 10 backend/src/services/coupling.ts -53 46 backend/src/services/folders.ts -63 37 backend/src/services/hotspot.ts -13 13 backend/src/services/log-cache.ts -4 4 backend/src/services/module-info.ts -12 12 backend/src/services/team-alignment.ts -4 4 backend/src/utils/complexity.ts -2 2 backend/src/utils/count-lines.ts -26 26 backend/src/utils/git-parser.ts -9 9 backend/src/utils/normalize-folder.ts -25 21 backend/src/utils/open.ts -1 1 backend/src/utils/to-percent.ts -12 13 backend/tsconfig.json -6 0 frontend/.prettierrc -4 14 frontend/angular.json -16 16 frontend/eslint.config.js -10 8 frontend/m3-theme.scss -17 0 frontend/package-lock.json -2 1 frontend/package.json -5 6 frontend/proxy.conf.json -3 1 frontend/src/app/app.component.spec.ts -1 2 frontend/src/app/app.component.ts -8 7 frontend/src/app/app.config.ts -31 31 frontend/src/app/app.routes.ts -16 11 frontend/src/app/coupling/coupling.service.ts -1 1 frontend/src/app/coupling/graph/graph-type.ts -20 18 frontend/src/app/coupling/graph/graph.adapter.ts -35 35 frontend/src/app/coupling/graph/graph.component.css -7 11 frontend/src/app/coupling/graph/graph.component.html -2 3 frontend/src/app/coupling/graph/graph.component.spec.ts -13 7 frontend/src/app/coupling/graph/graph.component.ts -2 2 frontend/src/app/coupling/graph/graph.ts -6 6 frontend/src/app/coupling/module.service.ts -14 14 frontend/src/app/data/cache.service.ts -10 11 frontend/src/app/data/status.service.ts -9 9 frontend/src/app/data/status.store.ts -3 5 frontend/src/app/event.service.ts -9 10 frontend/src/app/filter-tree/config.service.ts -3 3 frontend/src/app/filter-tree/config.ts -37 28 frontend/src/app/filter-tree/filter-tree.component.html -2 3 frontend/src/app/filter-tree/filter-tree.component.spec.ts -8 15 frontend/src/app/filter-tree/filter-tree.component.ts -6 7 frontend/src/app/filter-tree/folder.service.ts -3 3 frontend/src/app/filter-tree/folder.ts -1 1 frontend/src/app/hotspot/hotspot-result.ts -4 4 frontend/src/app/hotspot/hotspot.component.css -62 60 frontend/src/app/hotspot/hotspot.component.html -2 3 frontend/src/app/hotspot/hotspot.component.spec.ts -4 4 frontend/src/app/hotspot/hotspot.component.ts -23 13 frontend/src/app/hotspot/hotspot.service.ts -1 1 frontend/src/app/model/cache-status.ts -4 4 frontend/src/app/model/limits.ts -2 3 frontend/src/app/model/status.ts -29 15 frontend/src/app/nav/nav.component.html -1 1 frontend/src/app/nav/nav.component.spec.ts -8 7 frontend/src/app/nav/nav.component.ts -9 2 frontend/src/app/team-alignment/team-alignment-chart.ts -13 11 frontend/src/app/team-alignment/team-alignment.component.html -2 3 frontend/src/app/team-alignment/team-alignment.component.spec.ts -2 10 frontend/src/app/team-alignment/team-alignment.component.ts -6 6 frontend/src/app/ui/limits/limits.component.css -31 19 frontend/src/app/ui/limits/limits.component.html -2 3 frontend/src/app/ui/limits/limits.component.spec.ts -19 8 frontend/src/app/ui/limits/limits.component.ts -2 2 frontend/src/app/ui/loading/loading.component.css -4 1 frontend/src/app/ui/loading/loading.component.html -2 3 frontend/src/app/ui/loading/loading.component.spec.ts -27 28 frontend/src/app/utils/cache.guard.ts -2 2 frontend/src/app/utils/debounce.ts -6 7 frontend/src/app/utils/error-handler.ts -10 7 frontend/src/app/utils/explicit-effect.ts -18 12 frontend/src/index.html -3 2 frontend/src/main.ts -48 43 frontend/src/styles.scss -2 6 frontend/tsconfig.app.json -1 6 frontend/tsconfig.json -2 7 frontend/tsconfig.spec.json - -"Manfred Steyer ,Mon Sep 2 01:53:58 2024 +0200 7b2cfa38abaa551e13bec76b20d18414cea47980,refactor: linting" -14 0 frontend/angular.json -43 0 frontend/eslint.config.js -2790 983 frontend/package-lock.json -7 3 frontend/package.json -0 1 frontend/src/app/app.component.ts -0 57 frontend/src/app/coupling/chord/chord.component.css -0 57 frontend/src/app/coupling/chord/chord.component.html -0 391 frontend/src/app/coupling/chord/chord.component.ts -2 2 frontend/src/app/coupling/coupling-result.ts -2 2 frontend/src/app/coupling/coupling.service.ts -2 2 frontend/src/app/coupling/graph/graph-type.ts -11 11 frontend/src/app/coupling/graph/graph.adapter.ts -2 2 frontend/src/app/coupling/graph/graph.component.ts -15 6 frontend/src/app/coupling/graph/graph.ts -2 2 frontend/src/app/coupling/module-info.ts -1 1 frontend/src/app/data/status.store.ts -2 2 frontend/src/app/filter-tree/config.ts -0 1 frontend/src/app/filter-tree/filter-tree.component.html -2 2 frontend/src/app/filter-tree/filter-tree.component.ts -2 2 frontend/src/app/filter-tree/folder.ts -12 12 frontend/src/app/hotspot/hotspot-result.ts -2 2 frontend/src/app/hotspot/hotspot.component.ts -2 2 frontend/src/app/model/cache-status.ts -3 3 frontend/src/app/model/limits.ts -2 2 frontend/src/app/model/status.ts -0 2 frontend/src/app/nav/nav.component.ts -1 1 frontend/src/app/team-alignment/team-alignment-chart.ts -4 4 frontend/src/app/team-alignment/team-alignment-result.ts -3 3 frontend/src/app/ui/limits/limits.component.ts -9 1 frontend/src/app/utils/error-handler.ts - -"Manfred Steyer ,Mon Sep 2 01:19:12 2024 +0200 c7c7d4cecfa72cf51bcc2105ac927be7d2246057,refactor: clean up hotspots and team alignment" -1 0 backend/src/infrastructure/config.ts -2 7 frontend/src/app/app.routes.ts -0 1 frontend/src/app/coupling/coupling.service.ts -2 3 frontend/src/app/coupling/graph/graph.adapter.ts -2 1 frontend/src/app/coupling/graph/graph.component.ts -22 22 frontend/src/app/filter-tree/filter-tree.component.ts -15 6 frontend/src/app/hotspot/hotspot.component.html -68 56 frontend/src/app/hotspot/hotspot.component.ts -85 0 frontend/src/app/team-alignment/team-alignment-chart.ts -3 3 frontend/src/app/team-alignment/team-alignment.component.html -58 119 frontend/src/app/team-alignment/team-alignment.component.ts -9 0 frontend/src/app/utils/debounce.ts - -"Manfred Steyer ,Sun Sep 1 21:55:14 2024 +0200 8c0c177093febf038f6ebff8e7e6c69af5d2f4d1,refactor: use rendering effect in graph" -0 1 frontend/src/app/coupling/graph/graph.adapter.ts -1 1 frontend/src/app/coupling/graph/graph.component.html -15 18 frontend/src/app/coupling/graph/graph.component.ts -1 24 frontend/src/app/coupling/graph/graph.ts - -"Manfred Steyer ,Sun Sep 1 21:31:35 2024 +0200 3306c9393605b0073d07d9ba1357436ab9ce265c,refactor: split graph component" -2 3 backend/src/services/coupling.ts -150 0 frontend/src/app/coupling/graph/graph.adapter.ts -2 7 frontend/src/app/coupling/graph/graph.component.html -48 453 frontend/src/app/coupling/graph/graph.component.ts -232 0 frontend/src/app/coupling/graph/graph.ts -3 4 frontend/src/app/nav/nav.component.ts -1 0 frontend/src/app/utils/error-handler.ts - -"Manfred Steyer ,Sun Sep 1 18:10:52 2024 +0200 ed57ce00e6da585311a945564f077d4bf3788616,fix: correct break point observer for filter" -2 2 frontend/src/app/nav/nav.component.ts - -"Manfred Steyer ,Sun Sep 1 18:01:02 2024 +0200 6bfe42262107a8a8a490a8eafc6553e864b40a33,feat: removing chord diagramm from navigation" -5 0 frontend/src/app/nav/nav.component.css -1 1 frontend/src/app/nav/nav.component.html - -"Manfred Steyer ,Sun Sep 1 17:47:44 2024 +0200 07f7dce32827e5685f21a4468d86b1723b4e8cf9,feat: add loading indicator and pagination to hotspots" -4 0 frontend/src/app/hotspot/hotspot.component.css -45 36 frontend/src/app/hotspot/hotspot.component.html -49 24 frontend/src/app/hotspot/hotspot.component.ts -1 1 frontend/src/app/ui/limits/limits.component.html -3 4 frontend/src/app/ui/loading/loading.component.ts - -"Manfred Steyer ,Sun Sep 1 17:15:34 2024 +0200 3954986c2f56d0e0bc2bbb55e354d0de483fe130,feat: show total commit count in tool tip text" -25 8 backend/src/express.ts -14 0 backend/src/infrastructure/git.ts -10 3 frontend/src/app/app.component.ts -1 1 frontend/src/app/coupling/graph/graph.component.html -5 0 frontend/src/app/coupling/graph/graph.component.ts -15 0 frontend/src/app/data/status.service.ts -17 0 frontend/src/app/data/status.store.ts -1 1 frontend/src/app/hotspot/hotspot.component.html -4 0 frontend/src/app/hotspot/hotspot.component.ts -8 0 frontend/src/app/model/status.ts -1 1 frontend/src/app/team-alignment/team-alignment.component.html -4 0 frontend/src/app/team-alignment/team-alignment.component.ts -1 1 frontend/src/app/ui/limits/limits.component.html -16 2 frontend/src/app/ui/limits/limits.component.ts - -"Manfred Steyer ,Sun Sep 1 16:40:28 2024 +0200 2b45b50ab87a5c77a6f0ee667090785842907fba,feat: show commits in change-coupling graph" -4 1 backend/src/services/change-coupling.ts -3 1 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Sun Sep 1 16:05:54 2024 +0200 bf1e0d9599f1dbd6d8a82df9ff2c6e9630d5291b,feat: add by-user option to team-alignment" -1 1 backend/src/express.ts -2 1 backend/src/services/hotspot.ts -5 2 backend/src/services/team-alignment.ts -0 1 backend/src/utils/git-parser.ts -1 0 frontend/src/app/hotspot/hotspot.component.ts -7 1 frontend/src/app/team-alignment/team-alignment.component.html -8 5 frontend/src/app/team-alignment/team-alignment.component.ts -2 2 frontend/src/app/team-alignment/team-alignment.service.ts - -"Manfred Steyer ,Sun Sep 1 15:31:40 2024 +0200 d25bb57be8a810c5868be0e008660fc119ea0514,feat: allow to choose b/w mc cabe and file length for hotspot analysis" -5 3 backend/src/express.ts -19 10 backend/src/services/hotspot.ts -1 1 backend/src/utils/complexity.ts -3 0 frontend/src/app/hotspot/hotspot-result.ts -12 0 frontend/src/app/hotspot/hotspot.component.css -11 2 frontend/src/app/hotspot/hotspot.component.html -34 5 frontend/src/app/hotspot/hotspot.component.ts -6 6 frontend/src/app/hotspot/hotspot.service.ts -1 2 frontend/src/app/ui/limits/limits.component.ts - -"Manfred Steyer ,Sun Sep 1 14:35:56 2024 +0200 80c68b7cc3f2732587dd46a3f0c4efbb565f314f,feat: auto refresh cache on demand" -2 0 backend/src/services/hotspot.ts -6 0 backend/src/utils/count-lines.ts -6 8 backend/src/utils/git-parser.ts -5 2 frontend/src/app/app.config.ts -20 13 frontend/src/app/app.routes.ts -19 0 frontend/src/app/data/cache.service.ts -3 0 frontend/src/app/model/cache-status.ts -1 1 frontend/src/app/model/limits.ts -1 1 frontend/src/app/ui/limits/limits.component.ts -3 0 frontend/src/app/ui/loading/loading.component.css -9 0 frontend/src/app/ui/loading/loading.component.html -23 0 frontend/src/app/ui/loading/loading.component.spec.ts -12 0 frontend/src/app/ui/loading/loading.component.ts -31 0 frontend/src/app/utils/cache.guard.ts - -"Manfred Steyer ,Sun Sep 1 02:11:52 2024 +0200 670087ad20d590cdf281acff329ae1742b41eb4b,feat: cache git log" -3 0 backend/export.txt -24 0 backend/src/express.ts -1 2 backend/src/infrastructure/config.ts -19 3 backend/src/infrastructure/git.ts -17 0 backend/src/infrastructure/log.ts -3 0 backend/src/infrastructure/paths.ts -17 0 backend/src/infrastructure/tree-hash.ts -1 1 backend/src/model/limits.ts -27 0 backend/src/services/log-cache.ts -2 1 backend/src/services/team-alignment.ts -71 26 backend/src/utils/git-parser.ts - -"Manfred Steyer ,Sat Aug 31 15:13:49 2024 +0200 09d4c532b7fb0860e8edf9795b77cb557186c99d,fix: normalize paths in sheriff dump" -- - backend/detective-0.0.1.tgz -209 0 backend/dist/express.js -16 123 backend/dist/index.js -9 0 backend/dist/infrastructure/config.js -61 4 backend/dist/infrastructure/deps.js -39 4 backend/dist/infrastructure/git.js -7 0 backend/dist/model/limits.js -2 1 backend/dist/options/options.js -7 0 backend/dist/options/parse-options.js -91 0 backend/dist/services/change-coupling.js -15 15 backend/dist/services/coupling.js -36 1 backend/dist/services/folders.js -176 0 backend/dist/services/hotspot.js -26 19 backend/dist/services/team-alignment.js -59 0 backend/dist/utils/complexity.js -51 8 backend/dist/utils/git-parser.js -6 0 backend/dist/utils/matrix.js -16 0 backend/dist/utils/normalize-folder.js -6 0 backend/dist/utils/to-percent.js -1 0 backend/public/chunk-2FMXBR5T.js -7 0 backend/public/chunk-HQFIBEV7.js -2 2 backend/public/index.html -28 0 backend/public/main-4V2HQDRI.js -1 0 backend/public/styles-SVPGZYNK.css -8 7 backend/src/infrastructure/deps.ts - -"Manfred Steyer ,Fri Aug 30 20:21:44 2024 +0200 8da81d9d277378754ba6c7b4f60ee294f542eb61,feat: add ui for limiting the git log" -1 1 backend/src/express.ts -1 1 backend/src/infrastructure/git.ts -0 0 backend/src/model/{limit.ts => limits.ts} -1 1 backend/src/services/change-coupling.ts -1 1 backend/src/services/hotspot.ts -1 1 backend/src/services/team-alignment.ts -1 1 backend/src/utils/git-parser.ts -4 2 frontend/src/app/coupling/coupling.service.ts -5 1 frontend/src/app/coupling/graph/graph.component.css -23 11 frontend/src/app/coupling/graph/graph.component.html -15 14 frontend/src/app/coupling/graph/graph.component.ts -12 1 frontend/src/app/hotspot/hotspot.component.html -12 7 frontend/src/app/hotspot/hotspot.component.ts -5 4 frontend/src/app/hotspot/hotspot.service.ts -9 0 frontend/src/app/model/limits.ts -5 0 frontend/src/app/team-alignment/team-alignment.component.html -17 10 frontend/src/app/team-alignment/team-alignment.component.ts -9 5 frontend/src/app/team-alignment/team-alignment.service.ts -19 0 frontend/src/app/ui/limits/limits.component.css -22 0 frontend/src/app/ui/limits/limits.component.html -23 0 frontend/src/app/ui/limits/limits.component.spec.ts -58 0 frontend/src/app/ui/limits/limits.component.ts -10 0 frontend/src/app/utils/explicit-effect.ts -7 0 frontend/src/styles.scss - -"Manfred Steyer ,Fri Aug 30 18:41:31 2024 +0200 0e076356472bfd926ec6d97bcb1e36e8d8a254f9,feat: allow limiting forensic analyses by commit cound and time" -124 111 backend/src/express.ts -13 2 backend/src/infrastructure/git.ts -9 0 backend/src/model/limit.ts -3 2 backend/src/services/change-coupling.ts -7 9 backend/src/services/hotspot.ts -3 1 backend/src/services/team-alignment.ts -3 2 backend/src/utils/git-parser.ts - -"Manfred Steyer ,Fri Aug 30 17:22:20 2024 +0200 9c240f5e685b141fe275400aadb62ab559d68a2a,feat: allow disabling opening a browser window with --open false" -1 1 backend/src/options/parse-options.ts - -"Manfred Steyer ,Fri Aug 30 17:16:55 2024 +0200 23c2c55a94e4b8979c43a80a9c65928d980010a7,refactor: refactor start of server" -126 0 backend/src/express.ts -5 121 backend/src/index.ts -1 0 backend/src/infrastructure/config.ts -3 1 backend/src/options/options.ts -8 1 backend/src/options/parse-options.ts - -"Manfred Steyer ,Fri Aug 30 17:03:20 2024 +0200 1a1b191f31ce8e5b9c2abcfbfaa8a630ef48b589,feat: show at least the first two layers in tree" -3 4 frontend/src/app/filter-tree/filter-tree.component.ts - -"Manfred Steyer ,Fri Aug 30 16:59:36 2024 +0200 7e68917a729ac05de2d4eb7e303f17c2370df6f4,fix: min connections cannot be less than 0" -1 1 frontend/src/app/coupling/graph/graph.component.html - -"Manfred Steyer ,Fri Aug 30 16:58:14 2024 +0200 d606a376803f70450ab95ef7e7b0e55eec005078,feat: add error handling" -34 28 backend/src/index.ts -3 3 backend/src/infrastructure/git.ts -2 0 frontend/src/app/app.config.ts -1 1 frontend/src/app/team-alignment/team-alignment.component.html -26 0 frontend/src/app/utils/error-handler.ts -9 1 frontend/src/styles.scss - -"Manfred Steyer ,Thu Aug 29 23:03:59 2024 +0200 ebbbf4828cc0a3ed5bbee04be13ae4538c51ad47,feat: use latest official sheriff version" -4 4 backend/package-lock.json -1 1 backend/package.json -2 1 backend/src/index.ts -5 4 backend/src/infrastructure/deps.ts - -"Manfred Steyer ,Thu Aug 29 21:40:54 2024 +0200 da837a9343fb5ef1e120768b3050605d0681ebca,feat: infer sheriff dump when starting detective" -147 522 backend/package-lock.json -3 1 backend/package.json -12 6 backend/src/index.ts -12 1 backend/src/infrastructure/config.ts -58 5 backend/src/infrastructure/deps.ts -5 0 backend/src/infrastructure/git.ts -1 0 backend/src/model/config.ts - -"Manfred Steyer ,Thu Aug 29 00:00:47 2024 +0200 5ff8d1bc0f2273784018e6b41df3369f84c72016,feat: handle renames in git history" -3 14 backend/src/index.ts -29 4 backend/src/utils/git-parser.ts - -"Manfred Steyer ,Wed Aug 28 22:39:34 2024 +0200 453c4e9afa8ac8ca9c21a6958fe869d78088cdbd,feat: infer folder structure from sheriff dump" -1 6 backend/.detective/config.json -- - backend/detective-0.0.1.tgz -7 0 backend/public/chunk-HIPB54JD.js -1 0 backend/public/chunk-OVZZS7I2.js -1 1 backend/public/index.html -27 0 backend/public/main-MTHSAXW3.js -13 2 backend/src/index.ts -47 0 backend/src/services/folders.ts - -"Manfred Steyer ,Wed Aug 28 19:07:00 2024 +0200 d61c984d150ef9803ddba6b13d5f2b231ecfe244,fix: use trailing / when comparing folders using startsWith" -1 0 backend/src/index.ts -5 2 backend/src/services/change-coupling.ts -14 10 backend/src/services/coupling.ts -7 5 backend/src/services/hotspot.ts -47 40 backend/src/services/team-alignment.ts -13 0 backend/src/utils/normalize-folder.ts -0 4 backend/src/utils/round.ts -3 0 backend/src/utils/to-percent.ts -4 1 frontend/src/app/app.routes.ts -2 2 frontend/src/app/coupling/chord/chord.component.ts -12 0 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Sat Aug 24 23:41:32 2024 +0200 5bd6bc4547e006937f00e92f5cffba3390fc9de4,fix: make sure initial y >= 0 for graph" -1 1 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Sat Aug 24 23:39:09 2024 +0200 a161f29c7537509f8b197d115b7a58548ddb7a62,feat: add ui for change coupling" -10 2 backend/src/services/change-coupling.ts -6 2 frontend/src/app/app.config.ts -8 0 frontend/src/app/app.routes.ts -9 2 frontend/src/app/coupling/coupling.service.ts -5 0 frontend/src/app/coupling/graph/graph-type.ts -11 0 frontend/src/app/coupling/graph/graph.component.css -13 2 frontend/src/app/coupling/graph/graph.component.html -50 28 frontend/src/app/coupling/graph/graph.component.ts -38 16 frontend/src/app/hotspot/hotspot.component.ts -3 1 frontend/src/app/nav/nav.component.html - -"Manfred Steyer ,Sat Aug 24 22:17:10 2024 +0200 eb943f3e75c613f6b610c25c8b3de5e7c148742f,feat: hotspot ui" -2 2 backend/src/services/hotspot.ts -5 0 frontend/src/app/app.routes.ts -0 1 frontend/src/app/filter-tree/filter-tree.component.ts -32 0 frontend/src/app/hotspot/hotspot-result.ts -41 0 frontend/src/app/hotspot/hotspot.component.css -65 0 frontend/src/app/hotspot/hotspot.component.html -23 0 frontend/src/app/hotspot/hotspot.component.spec.ts -92 0 frontend/src/app/hotspot/hotspot.component.ts -21 0 frontend/src/app/hotspot/hotspot.service.ts -1 0 frontend/src/app/nav/nav.component.html -1 9 frontend/src/app/team-alignment/team-alignment.component.ts -8 0 frontend/src/app/utils/segments.ts - -"Manfred Steyer ,Sat Aug 24 01:38:34 2024 +0200 a4cd89a21f20a3ae9b79d5c25200107dd986444f,feat: add change coupling" -11 1 backend/src/index.ts -45 0 backend/src/services/change-coupling.ts -1 4 backend/src/services/coupling.ts -3 0 backend/src/utils/matrix.ts - -"Manfred Steyer ,Sat Aug 24 01:07:40 2024 +0200 66c4e507635ef39d4b68b992ca14fb552ff56820,feat: aggregate hotspots" -18 2 backend/src/index.ts -38 9 backend/src/services/hotspot.ts - -"Manfred Steyer ,Fri Aug 23 21:51:32 2024 +0200 bbdcfbfe83d1f9510b9cef286d16ce539f71ed2c,feat: filter hotspots by score" -3 3 backend/src/index.ts -55 17 backend/src/services/hotspot.ts - -"Manfred Steyer ,Fri Aug 23 19:57:20 2024 +0200 653797f1fd3403d53a141aa312259be58e8bcf40,feat: use dagre layout i/o cola" -83 1 frontend/package-lock.json -4 0 frontend/package.json -62 30 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Fri Aug 23 18:33:35 2024 +0200 050c5a55f1566186459934ae6bde1e9ae3a50c08,feat: add cc to hotspot analysis" -7 7 backend/src/services/hotspot.ts -43 0 backend/src/utils/complexity.ts -4 1 frontend/src/app/coupling/graph/graph.component.ts -1 1 frontend/src/app/nav/nav.component.html - -"Manfred Steyer ,Thu Aug 22 23:41:55 2024 +0200 32590d49b33c298ac99745b90087ddf4a236a349,feat: add hotspot analysis" -- - backend/detective-0.0.1.tgz -2 1 backend/dist/options/options.js -3 0 backend/dist/options/parse-options.js -11 10 backend/dist/services/team-alignment.js -504 123 backend/package-lock.json -1 1 backend/public/index.html -21 0 backend/public/main-UJURQ3JB.js -4 0 backend/publish.sh -13 0 backend/src/index.ts -3 1 backend/src/options/options.ts -3 0 backend/src/options/parse-options.ts -1 1 backend/src/services/folders.ts -51 0 backend/src/services/hotspot.ts -17 14 backend/src/services/team-alignment.ts -7 5 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Thu Aug 22 15:11:59 2024 +0200 04965b779da2b91d7694b57b8ea08851e6858e4f,docs: first simple readme" -- - backend/detective-0.0.1.tgz -0 3 backend/dist/options/validate-options.js -13 3 backend/dist/services/team-alignment.js -1 1 backend/public/index.html -21 0 backend/public/main-K3WBRXWH.js -17 0 backend/readme.md - -"Manfred Steyer ,Thu Aug 22 14:20:55 2024 +0200 7ab78fd8e4c667426237f671ce2803f20b839beb,feat: small improvements for v0.0.1" -- - backend/{.forensic => .detective}/.DS_Store -18 0 backend/.detective/config.json -0 0 backend/{.forensic => .detective}/deps.json -0 30 backend/.forensic/config.json -- - backend/detective-0.0.1.tgz -112 18 backend/dist/index.js -36 0 backend/dist/infrastructure/config.js -14 0 backend/dist/infrastructure/deps.js -30 0 backend/dist/infrastructure/git.js -9 0 backend/dist/options/options.js -49 0 backend/dist/options/parse-options.js -26 0 backend/dist/options/validate-options.js -20 19 backend/dist/services/coupling.js -34 0 backend/dist/services/folders.js -22 0 backend/dist/services/module-info.js -121 0 backend/dist/services/team-alignment.js -129 0 backend/dist/utils/git-parser.js -27 0 backend/dist/utils/open.js -7 0 backend/dist/utils/round.js -143 6 backend/package-lock.json -3 3 backend/package.json -7 0 backend/public/chunk-QWS3MURL.js -1 0 backend/public/chunk-UOJ4IBH7.js -- - backend/public/favicon.ico -14 10 backend/public/index.html -1 0 backend/public/main-AIZTFC63.css -21 0 backend/public/main-C2TU7PNS.js -21 0 backend/public/main-DAYCMAFW.js -21 0 backend/public/main-UO5QPOB3.js -1 0 backend/public/ping.txt -2 0 backend/public/polyfills-SCHOHYNV.js -1 0 backend/public/styles-KWQ3FGSD.css -1 0 backend/public/styles-OPAWKEW6.css -15 6 backend/src/index.ts -26 0 backend/src/infrastructure/config.ts -4 2 backend/src/options/options.ts -8 1 backend/src/options/parse-options.ts -1 4 backend/src/options/validate-options.ts -16 6 backend/src/services/team-alignment.ts -26 0 backend/src/utils/open.ts -1 12 frontend/angular.json -3 0 frontend/build.sh -12 0 frontend/src/app/coupling/chord/chord.component.html -9 1 frontend/src/app/coupling/chord/chord.component.ts -1 1 frontend/src/app/coupling/graph/graph.component.ts -1 1 frontend/src/app/nav/nav.component.css -13 3 frontend/src/app/team-alignment/team-alignment.component.ts -1 1 frontend/src/styles.scss - -"Manfred Steyer ,Thu Aug 22 00:27:56 2024 +0200 a1f26c1caf5f1a7d1a9a1bd1f3820845d12042bc,feat: add team alignment" -9 1 backend/.forensic/config.json -14 0 backend/src/index.ts -32 0 backend/src/infrastructure/git.ts -1 0 backend/src/model/config.ts -2 4 backend/src/services/folders.ts -87 0 backend/src/services/team-alignment.ts -113 0 backend/src/utils/git-parser.ts -19 0 frontend/package-lock.json -1 0 frontend/package.json -5 0 frontend/src/app/app.routes.ts -0 2 frontend/src/app/coupling/graph/graph.component.ts -4 3 frontend/src/app/filter-tree/filter-tree.component.ts -1 0 frontend/src/app/nav/nav.component.html -8 0 frontend/src/app/team-alignment/team-alignment-result.ts -0 0 frontend/src/app/team-alignment/team-alignment.component.css -7 0 frontend/src/app/team-alignment/team-alignment.component.html -23 0 frontend/src/app/team-alignment/team-alignment.component.spec.ts -140 0 frontend/src/app/team-alignment/team-alignment.component.ts -13 0 frontend/src/app/team-alignment/team-alignment.service.ts -59 0 frontend/src/styles.scss - -"Manfred Steyer ,Wed Aug 21 03:00:24 2024 +0200 d54cd5e5c1fd3dca5dee3d7dfcf0c30f81f5da67,feat: grouping nodes in graph" -16 1 backend/.forensic/config.json -1 1 backend/src/infrastructure/config.ts -1 0 backend/src/model/config.ts -2 0 backend/src/services/coupling.ts -0 1 frontend/src/app/coupling/chord/chord.component.ts -1 0 frontend/src/app/coupling/coupling-result.ts -5 0 frontend/src/app/coupling/graph/graph.component.css -1 0 frontend/src/app/coupling/graph/graph.component.html -204 62 frontend/src/app/coupling/graph/graph.component.ts -45 0 frontend/src/app/filter-tree/filter-tree.component.ts - -"Manfred Steyer ,Tue Aug 20 18:22:21 2024 +0200 869453984c88dd4341e94e0e2b55396fa4533c65,feat: use cytoscape for graph" -208 179 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Mon Aug 19 22:19:48 2024 +0200 e1f9a56b7df557fe237777c979f51e3eb423f7d8,feat: poc of new graph implementation" -2 4 backend/.forensic/config.json -1 1 backend/src/services/coupling.ts -76 0 frontend/package-lock.json -5 0 frontend/package.json -7 0 frontend/src/app/coupling/graph/graph.component.css -3 6 frontend/src/app/coupling/graph/graph.component.html -190 868 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Mon Aug 19 14:02:47 2024 +0200 1ed0bffc38aeda939ec69c3ab3877cb0696dc6ea,feat: add option for showing groups" -7 0 frontend/src/app/coupling/graph/graph.component.html -95 82 frontend/src/app/coupling/graph/graph.component.ts -1 0 frontend/src/app/filter-tree/config.ts - -"Manfred Steyer ,Mon Aug 19 03:00:13 2024 +0200 1d39e93d46088555d6bc595195fe166fcd3b6821,feat: use cola simulation for graph" -3 10 backend/.forensic/config.json -56 0 frontend/package-lock.json -2 1 frontend/package.json -0 1 frontend/src/app/coupling/chord/chord.component.ts -6 0 frontend/src/app/coupling/graph/graph.component.css -817 164 frontend/src/app/coupling/graph/graph.component.ts - -"Manfred Steyer ,Sun Aug 18 17:17:13 2024 +0200 b6e113eb2e1b1805ece623a940f8cb2d823af512,feat: add coherence" -11 2 backend/.forensic/config.json -50 37 backend/src/index.ts -11 0 backend/src/infrastructure/config.ts -11 0 backend/src/infrastructure/deps.ts -29 29 backend/src/services/coupling.ts -27 0 backend/src/services/module-info.ts -4 0 backend/src/utils/round.ts -17 1 frontend/src/app/app.routes.ts -13 0 frontend/src/app/coupling/chord/chord.component.css -24 10 frontend/src/app/coupling/chord/chord.component.html -37 6 frontend/src/app/coupling/chord/chord.component.ts -2 0 frontend/src/app/coupling/coupling-result.ts -1 0 frontend/src/app/coupling/graph/graph.component.css -2 1 frontend/src/app/coupling/graph/graph.component.ts -3 0 frontend/src/app/coupling/module-info.ts -13 0 frontend/src/app/coupling/module.service.ts -4 0 frontend/src/app/nav/nav.component.css -5 3 frontend/src/app/nav/nav.component.html -2 0 frontend/src/app/nav/nav.component.ts -1 1 frontend/src/styles.scss - -"Manfred Steyer ,Sun Aug 18 12:57:09 2024 +0200 3b61603f52c52ea6e5a3c96f5366730e342517f1,feat: show node details" -1 1 backend/.forensic/config.json -15 0 frontend/src/app/coupling/chord/chord.component.css -22 1 frontend/src/app/coupling/chord/chord.component.html -0 23 frontend/src/app/coupling/chord/chord.component.spec.ts -51 115 frontend/src/app/coupling/chord/chord.component.ts - -"Manfred Steyer ,Sun Aug 18 12:18:41 2024 +0200 ef040177a1d0f27107c6d712696d8e7686da1305,chor: rename coupling component to chord component" -1 2 frontend/src/app/app.component.ts -0 0 frontend/src/app/coupling/{coupling.component.css => chord/chord.component.css} -0 0 frontend/src/app/coupling/{coupling.component.html => chord/chord.component.html} -0 0 frontend/src/app/coupling/{coupling.component.spec.ts => chord/chord.component.spec.ts} -167 113 frontend/src/app/coupling/{coupling.component.ts => chord/chord.component.ts} -1 1 frontend/src/app/nav/nav.component.html -3 3 frontend/src/app/nav/nav.component.ts - -"Manfred Steyer ,Sun Aug 18 00:26:37 2024 +0200 76c4c8045ab07edab56db96f5436b734cfc96dee,feat: add graph component for coupling" -1 1 backend/.forensic/config.json -1 1 frontend/src/app/app.component.html -143 46 frontend/src/app/coupling/coupling.component.ts -26 0 frontend/src/app/coupling/graph/graph.component.css -2 0 frontend/src/app/coupling/graph/graph.component.html -23 0 frontend/src/app/coupling/graph/graph.component.spec.ts -268 0 frontend/src/app/coupling/graph/graph.component.ts -2 0 frontend/src/app/filter-tree/filter-tree.component.ts -1 0 frontend/src/app/nav/nav.component.html -3 1 frontend/src/app/nav/nav.component.ts - -"Manfred Steyer ,Sat Aug 17 00:24:49 2024 +0200 e3f3a2e873647648aba468b9d002d36b6689abcd,feat: add filter" -6 6 backend/.forensic/config.json -14 1 backend/src/index.ts -29 0 backend/src/services/folders.ts -27 13 frontend/src/app/coupling/coupling.component.css -2 1 frontend/src/app/coupling/coupling.component.html -77 71 frontend/src/app/coupling/coupling.component.ts -9 0 frontend/src/app/event.service.ts -18 0 frontend/src/app/filter-tree/config.service.ts -7 0 frontend/src/app/filter-tree/config.ts -27 0 frontend/src/app/filter-tree/filter-tree.component.css -38 0 frontend/src/app/filter-tree/filter-tree.component.html -23 0 frontend/src/app/filter-tree/filter-tree.component.spec.ts -96 0 frontend/src/app/filter-tree/filter-tree.component.ts -14 0 frontend/src/app/filter-tree/folder.service.ts -5 0 frontend/src/app/filter-tree/folder.ts -1 1 frontend/src/app/nav/nav.component.css -5 5 frontend/src/app/nav/nav.component.html -4 1 frontend/src/app/nav/nav.component.ts -3 0 frontend/src/styles.scss - -"Manfred Steyer ,Fri Aug 16 15:08:15 2024 +0200 7cc28733552dda6c92d9b5d03448aa063b47ecba,feat: add angular material" -- - backend/.forensic/.DS_Store -0 0 backend/{data => .forensic}/config.json -0 0 backend/{data => .forensic}/deps.json -0 44 backend/index.ts -0 3 backend/model/config.ts -0 7 backend/model/deps.ts -0 90 backend/services/coupling.ts -16 0 frontend/.editorconfig -42 0 frontend/.gitignore -4 0 frontend/.vscode/extensions.json -20 0 frontend/.vscode/launch.json -42 0 frontend/.vscode/tasks.json -27 0 frontend/README.md -101 0 frontend/angular.json -144 0 frontend/m3-theme.scss -15165 0 frontend/package-lock.json -42 0 frontend/package.json -8 0 frontend/proxy.conf.json -- - frontend/public/favicon.ico -0 0 frontend/src/app/app.component.css -1 0 frontend/src/app/app.component.html -29 0 frontend/src/app/app.component.spec.ts -15 0 frontend/src/app/app.component.ts -14 0 frontend/src/app/app.config.ts -3 0 frontend/src/app/app.routes.ts -4 0 frontend/src/app/coupling/coupling-result.ts -15 0 frontend/src/app/coupling/coupling.component.css -9 0 frontend/src/app/coupling/coupling.component.html -23 0 frontend/src/app/coupling/coupling.component.spec.ts -261 0 frontend/src/app/coupling/coupling.component.ts -13 0 frontend/src/app/coupling/coupling.service.ts -17 0 frontend/src/app/nav/nav.component.css -31 0 frontend/src/app/nav/nav.component.html -25 0 frontend/src/app/nav/nav.component.spec.ts -36 0 frontend/src/app/nav/nav.component.ts -15 0 frontend/src/index.html -6 0 frontend/src/main.ts -3 0 frontend/src/styles.scss -15 0 frontend/tsconfig.app.json -33 0 frontend/tsconfig.json -15 0 frontend/tsconfig.spec.json - -"Manfred Steyer ,Thu Aug 15 22:16:14 2024 +0200 eee51b3b353c01582a6dba48f5a913b32342602f,feat: parse commandline options" -44 0 backend/dist/index.js -2 0 backend/dist/model/config.js -2 0 backend/dist/model/deps.js -74 0 backend/dist/services/coupling.js -1 0 backend/forensic.conf.json -7 4 backend/package.json -56 0 backend/src/index.ts -3 0 backend/src/model/config.ts -7 0 backend/src/model/deps.ts -11 0 backend/src/options/options.ts -30 0 backend/src/options/parse-options.ts -21 0 backend/src/options/validate-options.ts -91 0 backend/src/services/coupling.ts -1 1 backend/tsconfig.json - -"Manfred Steyer ,Wed Aug 14 20:19:26 2024 +0200 d5819bb9916eb122992192ede59aaa2835793786,feat(backend): add api for coupling" -7 0 backend/data/config.json -523 0 backend/data/deps.json -44 0 backend/index.ts -3 0 backend/model/config.ts -7 0 backend/model/deps.ts -1621 0 backend/package-lock.json -23 0 backend/package.json -11 0 backend/public/index.html -90 0 backend/services/coupling.ts -14 0 backend/tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f1f6ed8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:4300", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/apps/backend/package.json b/apps/backend/package.json index aede1fc..8ea95fb 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -6,7 +6,13 @@ "@softarc/detective": "./bin/main.js" }, "dependencies": { - "tslib": "^2.0.0" + "@jscpd/core": "^4.0.1", + "@jscpd/tokenizer": "^4.0.1", + "@modelcontextprotocol/sdk": "1.17.3", + "reflect-metadata": "^0.2.2", + "tslib": "^2.0.0", + "zod": "3.25.76", + "zod-to-json-schema": "^3.23.5" }, "author": "Manfred Steyer", "license": "MIT", diff --git a/apps/backend/project.json b/apps/backend/project.json index d339c51..a6f2e11 100644 --- a/apps/backend/project.json +++ b/apps/backend/project.json @@ -12,12 +12,8 @@ "options": { "buildTarget": "backend:build", "runBuildTargetDependencies": false, - "args": [ - "--path", - "/Users/manfredsteyer/projects/public/standalone-example-cli", - "--open", - "false" - ] + "inspect": true, + "args": ["--path", "."] }, "configurations": { "development": { diff --git a/apps/backend/src/express.ts b/apps/backend/src/express.ts index 7b6be8c..8134de5 100644 --- a/apps/backend/src/express.ts +++ b/apps/backend/src/express.ts @@ -5,6 +5,8 @@ import { cwd } from 'process'; import express from 'express'; import { getCommitCount } from './infrastructure/git'; +import { createMcpHttpRouter } from './mcp/http-router'; +import { createMcpServer } from './mcp/server'; import { Limits } from './model/limits'; import { Options } from './options/options'; import { calcChangeCoupling } from './services/change-coupling'; @@ -19,12 +21,40 @@ import { import { isStale, updateLogCache } from './services/log-cache'; import { calcModuleInfo } from './services/module-info'; import { calcTeamAlignment } from './services/team-alignment'; +import { + runTrendAnalysis, + formatTrendAnalysisForAPI, + GitService, +} from './services/trend-analysis'; + +// Global trend analysis status +const trendAnalysisStatus: { + isRunning: boolean; + lastRun: Date | null; + lastResult: unknown; +} = { + isRunning: false, + lastRun: null as Date | null, + lastResult: null as unknown, +}; + +export function updateTrendAnalysisStatus( + update: Partial +) { + Object.assign(trendAnalysisStatus, update); +} export function setupExpress(options: Options) { const app = express(); app.use(express.json()); + // MCP integration (Streamable HTTP) enabled by default at /mcp + app.use( + '/mcp', + createMcpHttpRouter(() => createMcpServer(options)) + ); + app.get('/api/config', (req, res) => { res.sendFile(path.join(cwd(), options.config)); }); @@ -157,6 +187,250 @@ export function setupExpress(options: Options) { } }); + app.get('/api/trend-analysis/status', (req, res) => { + res.json({ + isRunning: trendAnalysisStatus.isRunning, + lastRun: trendAnalysisStatus.lastRun?.toISOString(), + hasResults: !!trendAnalysisStatus.lastResult, + }); + }); + + app.get('/api/trend-analysis', async (req, res) => { + const maxCommits = Number(req.query.maxCommits) || 50; + const parallelWorkers = Math.max( + 1, + Math.min(10, Number(req.query.parallelWorkers) || 5) + ); // Limit between 1-10 workers + const fileExtensions = req.query.fileExtensions + ? String(req.query.fileExtensions).split(',') + : ['.ts', '.js', '.tsx', '.jsx']; + + // Check if we have cached results and no new analysis is requested + if (trendAnalysisStatus.lastResult && !req.query.fresh) { + res.json(trendAnalysisStatus.lastResult); + return; + } + + // Prevent multiple concurrent analyses + if (trendAnalysisStatus.isRunning) { + res.status(429).json({ + error: + 'Trend analysis is already running. Check /api/trend-analysis/status for progress.', + }); + return; + } + + try { + trendAnalysisStatus.isRunning = true; + + const result = await runTrendAnalysis(options, { + maxCommits, + fileExtensions, + parallelWorkers, + }); + + const formattedResult = await formatTrendAnalysisForAPI( + result, + options.path, + fileExtensions + ); + + // Cache the results + trendAnalysisStatus.lastResult = formattedResult; + trendAnalysisStatus.lastRun = new Date(); + + res.json(formattedResult); + } catch (e: unknown) { + handleError(e, res); + } finally { + trendAnalysisStatus.isRunning = false; + } + }); + + // Streaming trend analysis endpoint with Server-Sent Events + app.get('/api/trend-analysis/stream', async (req, res) => { + const maxCommits = Number(req.query.maxCommits) || 50; + const parallelWorkers = Math.max( + 1, + Math.min(10, Number(req.query.parallelWorkers) || 5) + ); // Limit between 1-10 workers + const fileExtensions = req.query.fileExtensions + ? String(req.query.fileExtensions).split(',') + : ['.ts', '.js', '.tsx', '.jsx']; + + // Prevent multiple concurrent analyses + if (trendAnalysisStatus.isRunning) { + res.status(429).json({ + error: + 'Trend analysis is already running. Check /api/trend-analysis/status for progress.', + }); + return; + } + + // Set up SSE headers + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Cache-Control', + }); + + // Send initial connection message + res.write( + `data: ${JSON.stringify({ + type: 'connected', + message: 'Connected to trend analysis stream', + })}\n\n` + ); + + try { + trendAnalysisStatus.isRunning = true; + + // FIRST: Send the complete file structure immediately + const gitService = new GitService(options.path); + const currentFiles = await gitService.getCurrentFiles(fileExtensions); + + const initialFileStructure = currentFiles.map((filePath) => ({ + filePath, + changeFrequency: 0, + averageComplexity: 0, + averageSize: 0, + totalChanges: 0, + commits: [], + complexityTrend: [], + sizeTrend: [], + })); + + // Send initial file structure + res.write( + `data: ${JSON.stringify({ + type: 'initial_files', + message: `Loaded ${currentFiles.length} files from current commit`, + data: { + files: initialFileStructure, + summary: { + totalProcessingTimeMs: 0, + commitsAnalyzed: 0, + filesAnalyzed: currentFiles.length, + commitHashes: [], + }, + }, + })}\n\n` + ); + + // THEN: Start the trend analysis with streaming updates + const result = await runTrendAnalysis(options, { + maxCommits, + fileExtensions, + parallelWorkers, + progressCallback: (update) => { + // Send progress update via SSE + res.write(`data: ${JSON.stringify(update)}\n\n`); + }, + }); + + const formattedResult = await formatTrendAnalysisForAPI( + result, + options.path, + fileExtensions + ); + + // Cache the results + trendAnalysisStatus.lastResult = formattedResult; + trendAnalysisStatus.lastRun = new Date(); + + // Send final result + res.write( + `data: ${JSON.stringify({ + type: 'final_result', + message: 'Analysis complete', + data: formattedResult, + })}\n\n` + ); + } catch (error: unknown) { + const message = + typeof error === 'object' && error && 'message' in error + ? error.message + : '' + error; + res.write( + `data: ${JSON.stringify({ + type: 'error', + message: `Analysis failed: ${message}`, + progress: 100, + })}\n\n` + ); + } finally { + trendAnalysisStatus.isRunning = false; + res.write( + `event: close\ndata: ${JSON.stringify({ + type: 'stream_end', + message: 'Stream ended', + })}\n\n` + ); + res.end(); + } + }); + + // X-Ray code analysis endpoint + app.get('/api/x-ray', async (req, res) => { + const filePath = req.query.file as string; + const includeSource = req.query.includeSource === 'true'; + + if (!filePath) { + res.status(400).json({ error: 'file query parameter is required' }); + return; + } + + try { + // Resolve the file path relative to the project root + const fullPath = path.resolve(options.path, filePath); + + // Check if file exists + if (!fs.existsSync(fullPath)) { + res.status(404).json({ error: `File not found: ${filePath}` }); + return; + } + + // Create analyzer and run analysis (lazy import to prevent startup scanning) + const { CodeAnalyzer } = await import( + './services/trend-analysis/x-ray/code-analyzer' + ); + const analyzer = new CodeAnalyzer(fullPath); + const metrics = await analyzer.analyze(includeSource); + + res.json({ ...metrics, schemaUrl: '/api/x-ray/schema?v=1' }); + } catch (e: unknown) { + handleError(e, res); + } + }); + + // X-Ray schema endpoint + app.get('/api/x-ray/schema', async (_req, res) => { + try { + const { CodeAnalyzer } = await import( + './services/trend-analysis/x-ray/code-analyzer' + ); + const { buildBaseXRaySchema } = await import( + './services/trend-analysis/x-ray/x-ray.schema' + ); + + const jsonSchema = CodeAnalyzer.buildJSONSchema() as Record< + string, + unknown + >; + // Prefer UI schema embedded under jsonSchema['x-ui'] for a single-source payload + const uiSchema = + (jsonSchema as Record)['x-ui'] ?? + CodeAnalyzer.buildUISchema(); + const base = buildBaseXRaySchema(); + + res.json({ version: base.version, jsonSchema, uiSchema }); + } catch (e: unknown) { + handleError(e, res); + } + }); + app.use(express.static(path.join(__dirname, 'assets'))); app.get('*', (req, res) => { diff --git a/apps/backend/src/infrastructure/deps.ts b/apps/backend/src/infrastructure/deps.ts index 5cc2bf6..e000d4a 100644 --- a/apps/backend/src/infrastructure/deps.ts +++ b/apps/backend/src/infrastructure/deps.ts @@ -37,16 +37,17 @@ export function loadDeps(_options: Options): Deps { export function inferDeps(options: Options): boolean { const entryGlobs = getEntryGlobs(options); - const entries = globSync(entryGlobs); + + const analysisDir = options.path || cwd(); + + const entries = globSync(entryGlobs, { cwd: analysisDir }); if (entries.length === 0) { return false; } - const dir = cwd(); - const sheriffDump = entries - .map((e) => getProjectData(e, dir)) + .map((e) => getProjectData(e, analysisDir)) .reduce((acc, curr) => ({ ...acc, ...curr })); deps = normalizeObject(sheriffDump); diff --git a/apps/backend/src/infrastructure/git.ts b/apps/backend/src/infrastructure/git.ts index 3b92b0d..4d62279 100644 --- a/apps/backend/src/infrastructure/git.ts +++ b/apps/backend/src/infrastructure/git.ts @@ -1,10 +1,25 @@ import { spawn, spawnSync } from 'child_process'; import * as fs from 'fs'; +import * as path from 'path'; import { noLimits } from '../model/limits'; -export function isRepo(): boolean { - return fs.existsSync('.git'); +export function findGitRoot(startPath?: string): string | null { + let currentPath = startPath ? path.resolve(startPath) : process.cwd(); + + while (currentPath !== path.dirname(currentPath)) { + const gitPath = path.join(currentPath, '.git'); + if (fs.existsSync(gitPath)) { + return currentPath; + } + currentPath = path.dirname(currentPath); + } + + return null; +} + +export function isRepo(startPath?: string): boolean { + return findGitRoot(startPath) !== null; } export function calcTreeHash(): string { diff --git a/apps/backend/src/infrastructure/paths.ts b/apps/backend/src/infrastructure/paths.ts index c502497..a22894f 100644 --- a/apps/backend/src/infrastructure/paths.ts +++ b/apps/backend/src/infrastructure/paths.ts @@ -1,3 +1,4 @@ export const DETECTIVE_DIR = '.detective'; export const HASH_FILE = 'hash'; export const LOG_FILE = 'log'; +export const TREND_CACHE_FILE = 'trend-cache.json'; diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index 4780166..0e1c14f 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -1,33 +1,93 @@ #!/usr/bin/env node +import * as os from 'os'; +import * as path from 'path'; + import { setupExpress } from './express'; +import { updateTrendAnalysisStatus } from './express'; import { ensureConfig } from './infrastructure/config'; import { getEntryGlobs, inferDeps } from './infrastructure/deps'; -import { isRepo } from './infrastructure/git'; +import { isRepo, findGitRoot } from './infrastructure/git'; import { DETECTIVE_VERSION } from './infrastructure/version'; import { parseOptions } from './options/parse-options'; +import { + runTrendAnalysis, + formatTrendAnalysisForAPI, +} from './services/trend-analysis'; import { openSync } from './utils/open'; const options = parseOptions(process.argv.slice(2)); +let originalUserPath = process.cwd(); + if (options.path) { - process.chdir(options.path); + // Expand ~ to home directory if needed + let targetPath = options.path; + if (targetPath.startsWith('~/')) { + targetPath = path.join(os.homedir(), targetPath.slice(2)); + } else if (targetPath === '~') { + targetPath = os.homedir(); + } + + originalUserPath = path.resolve(targetPath); + console.log(`User selected directory: ${originalUserPath}`); + + // Find the git root from the user's selected path + const gitRoot = findGitRoot(targetPath); + if (gitRoot) { + console.log(`Found git repository root: ${gitRoot}`); + if (gitRoot !== originalUserPath) { + const relativePath = path.relative(gitRoot, originalUserPath); + if (relativePath && relativePath !== '.') { + console.log( + `Note: Analysis will be scoped to the selected subdirectory: ${relativePath}` + ); + } + } + process.chdir(gitRoot); + options.path = originalUserPath; + } else { + // No git root found, use the user's selected path as before + console.log(`Changing to directory: ${targetPath}`); + process.chdir(targetPath); + options.path = originalUserPath; + } +} else { + // If no path specified, check if current directory is in a git repo + const gitRoot = findGitRoot(); + if (gitRoot && gitRoot !== process.cwd()) { + console.log(`Found git repository root: ${gitRoot}`); + console.log(`Changing to git root for analysis`); + process.chdir(gitRoot); + } } ensureConfig(options); -if (!inferDeps(options)) { +try { + if (!inferDeps(options)) { + console.error( + 'No entry points found. Tried:', + getEntryGlobs(options).join(', ') + ); + console.error( + '\nPlease configured your entry points in .detective/config.json' + ); + process.exit(1); + } +} catch (error) { + console.error('Error analyzing project dependencies:'); + console.error(error.message); console.error( - 'No entry points found. Tried:', - getEntryGlobs(options).join(', ') + '\nThis might be due to complex TypeScript configurations or missing dependencies.' ); console.error( - '\nPlease configured your entry points in .detective/config.json' + 'Detective may still work for trend analysis if this is a git repository.' ); - process.exit(1); + console.error('\nContinuing with limited functionality...'); } -if (!isRepo()) { +if (!isRepo(originalUserPath)) { console.warn('This does not seem to be a git repository.'); console.warn('Most diagrams provided by detective do not work without git!'); } @@ -37,6 +97,30 @@ const app = setupExpress(options); app.listen(options.port, () => { const url = `http://localhost:${options.port}`; console.log(`Detective v${DETECTIVE_VERSION} runs at ${url}`); + + if (options.trendAnalysis) { + console.log('Starting trend analysis in background...'); + // Run trend analysis asynchronously without blocking startup + setImmediate(async () => { + try { + updateTrendAnalysisStatus({ isRunning: true }); + const result = await runTrendAnalysis(options, { maxCommits: 5 }); + console.log( + `Background trend analysis complete: analyzed ${result.commitsAnalyzed} commits and ${result.filesAnalyzed} files in ${result.totalProcessingTimeMs}ms` + ); + const formattedResult = await formatTrendAnalysisForAPI(result); + updateTrendAnalysisStatus({ + isRunning: false, + lastRun: new Date(), + lastResult: formattedResult, + }); + } catch (error) { + console.error('Background trend analysis failed:', error); + updateTrendAnalysisStatus({ isRunning: false }); + } + }); + } + if (options.open) { openSync(url); } diff --git a/apps/backend/src/mcp/README.md b/apps/backend/src/mcp/README.md new file mode 100644 index 0000000..324ca6f --- /dev/null +++ b/apps/backend/src/mcp/README.md @@ -0,0 +1,578 @@ +# Detective MCP Server + +A Model Context Protocol (MCP) server that enables LLMs to analyze code quality, detect problems, and understand code evolution in JavaScript/TypeScript projects using Detective's powerful analysis tools. + +## Overview + +This MCP server provides AI assistants with comprehensive code analysis capabilities: + +- **Repository Intelligence**: Understand project structure and metadata +- **Trend Analysis**: Track code quality evolution over time +- **X-Ray Analysis**: Deep inspection of individual files +- **Hotspot Detection**: Identify problematic code areas +- **Project Navigation**: Explore file and folder structures + +## Available Tools + +### 1. `get_repository_info` + +**Purpose**: Get comprehensive repository metadata to understand the codebase context. + +**When to use**: Always call this first to understand what repository you are analyzing. + +**Parameters**: None + +**Returns**: + +```json +{ + "repository": { + "name": "project-name", + "path": "/absolute/path/to/repo", + "isGitRepo": true, + "remotes": { + "origin": "https://github.com/user/repo.git" + } + }, + "git": { + "currentBranch": "main", + "lastCommit": { + "hash": "abc123...", + "message": "Latest commit message", + "author": "Developer Name", + "date": "2024-01-20" + }, + "totalCommits": 1234, + "contributors": 15 + }, + "project": { + "type": "nx-monorepo", + "language": "typescript", + "frameworks": ["angular", "express"], + "fileStats": { + "totalFiles": 500, + "byExtension": { + ".ts": 300, + ".html": 100, + ".css": 50 + } + } + }, + "capabilities": { + "trendAnalysis": true, + "xrayAnalysis": true, + "hotspotDetection": true, + "supportedExtensions": [".ts", ".js", ".tsx", ".jsx"] + } +} +``` + +### 2. `analyze_trends` + +**Purpose**: Analyze how code quality changes over time to identify deteriorating files. + +**When to use**: To understand code evolution and identify files that are getting more complex. + +**Parameters**: + +- `maxCommits` (number, 1-1000, default: 50): Number of commits to analyze +- `fileExtensions` (string[], default: [".ts", ".js", ".tsx", ".jsx"]): File types to include +- `parallelWorkers` (number, 1-10, default: 5): Number of parallel analysis workers + +**Returns**: + +```json +{ + "success": true, + "data": { + "fileMetrics": [ + { + "filePath": "src/components/Button.tsx", + "changeFrequency": 15, + "averageComplexity": 8.5, + "averageSize": 120, + "complexityTrend": [5, 7, 8, 9, 8], + "sizeTrend": [80, 95, 110, 130, 120] + } + ], + "commitsAnalyzed": 50, + "filesAnalyzed": 42, + "totalProcessingTimeMs": 5432 + }, + "summary": { + "filesAnalyzed": 42, + "commitsAnalyzed": 50, + "processingTimeMs": 5432, + "topChangedFiles": [ + { + "filePath": "src/components/Button.tsx", + "changeFrequency": 15, + "averageComplexity": 8.5 + } + ] + } +} +``` + +### 3. `xray_analyze` + +**Purpose**: Deep code inspection of a single file's structure and quality. + +**When to use**: To understand code organization, complexity, and dependencies of specific files. + +**Parameters**: + +- `filePath` (string, required): Path to the file to analyze +- `includeSource` (boolean, default: false): Whether to include source code in response + +**Returns**: + +```json +{ + "success": true, + "data": { + "file": "src/components/Button.tsx", + "metrics": { + "methodLevel": { + "Button.render": { + "complexity": 5, + "lines": 25, + "parameters": 2 + } + }, + "classLevel": { + "Button": { + "methods": 3, + "complexity": 12, + "cohesion": 0.8 + } + }, + "dataStructure": { + "interfaces": 2, + "types": 1 + }, + "inheritance": { + "depth": 1, + "children": 0 + }, + "organization": { + "imports": 8, + "exports": 1, + "coupling": 0.3 + }, + "typescript": { + "typeComplexity": 3, + "genericUsage": 1 + } + } + }, + "summary": { + "filePath": "src/components/Button.tsx", + "totalMethods": 3, + "totalClasses": 1, + "averageComplexity": 8.5, + "maxComplexity": 12, + "analysisTimeMs": 150 + } +} +``` + +### 4. `detect_hotspots` + +**Purpose**: Find problematic areas in the codebase that need attention. + +**When to use**: To identify files that need refactoring based on change frequency and complexity. + +**Parameters**: + +- `minScore` (number, default: -1): Minimum hotspot score threshold +- `module` (string, default: ""): Filter by module/directory +- `metric` ("McCabe" | "Length", default: "McCabe"): Complexity metric to use + +**Returns**: + +```json +{ + "success": true, + "data": { + "hotspots": [ + { + "fileName": "src/legacy/OldComponent.tsx", + "commits": 45, + "changedLines": 1200, + "complexity": 25, + "score": 1125 + } + ] + }, + "summary": { + "totalHotspots": 12, + "averageScore": 285.5, + "maxScore": 1125, + "criticalFiles": ["src/legacy/OldComponent.tsx", "src/utils/ComplexHelper.ts"] + } +} +``` + +### 5. `get_file_structure` + +**Purpose**: Get the hierarchical file and folder structure of the project. + +**When to use**: To understand project organization and navigate the codebase. + +**Parameters**: + +- `path` (string, default: "."): Starting path for structure analysis +- `extensions` (string[], default: [".ts", ".js", ".tsx", ".jsx"]): File extensions to include +- `includeGitIgnored` (boolean, default: false): Whether to include git-ignored files + +**Returns**: + +```json +{ + "success": true, + "data": { + "name": "project-root", + "path": ".", + "type": "directory", + "children": [ + { + "name": "src", + "path": "src", + "type": "directory", + "children": [ + { + "name": "index.ts", + "path": "src/index.ts", + "type": "file", + "extension": ".ts", + "size": 2048 + } + ] + } + ] + }, + "summary": { + "totalFiles": 156, + "totalDirectories": 23, + "filesByExtension": { + ".ts": 98, + ".tsx": 45, + ".html": 8, + ".css": 5 + } + } +} +``` + +## Usage Patterns for LLMs + +### Initial Analysis Workflow + +1. **Start with repository context**: + + ``` + get_repository_info -> understand what you're analyzing + ``` + +2. **Explore project structure**: + + ``` + get_file_structure -> navigate and understand organization + ``` + +3. **Identify problem areas**: + + ``` + detect_hotspots -> find files that need attention + analyze_trends -> understand quality evolution + ``` + +4. **Deep dive into specific files**: + ``` + xray_analyze -> detailed analysis of problematic files + ``` + +### Analysis Scenarios + +#### "What are the biggest problems in this codebase?" + +1. `get_repository_info` - Understand the project +2. `detect_hotspots` with `metric: "McCabe"` - Find complex, frequently changed files +3. `analyze_trends` with `maxCommits: 100` - See quality trends +4. `xray_analyze` on top hotspots - Understand specific issues + +#### "How has code quality changed recently?" + +1. `get_repository_info` - Get context +2. `analyze_trends` with `maxCommits: 50` - Recent quality trends +3. `detect_hotspots` - Current problem areas +4. Compare results to understand deterioration patterns + +#### "Analyze this specific file" + +1. `xray_analyze` with target file - Detailed metrics +2. `analyze_trends` filtered to that file - Historical context +3. Provide specific recommendations based on metrics + +### Interpreting Results + +#### Complexity Scores + +- **0-10**: Simple, well-structured code +- **11-20**: Moderate complexity, acceptable +- **21-50**: High complexity, consider refactoring +- **50+**: Very high complexity, needs immediate attention + +#### Hotspot Scores + +- **Low scores (0-100)**: Normal files, no immediate concern +- **Medium scores (100-500)**: Files to monitor +- **High scores (500-1000)**: Files needing attention +- **Critical scores (1000+)**: Urgent refactoring candidates + +#### Change Frequency Patterns + +- **High changes + High complexity**: Critical hotspot - needs refactoring +- **High changes + Low complexity**: Active development area - good +- **Low changes + High complexity**: Technical debt - schedule refactoring +- **Low changes + Low complexity**: Stable code - good + +## Installation and Setup + +### Prerequisites + +- Node.js 20.17.0 or higher +- npm 10.8.2 or higher +- A TypeScript/JavaScript project with git history + +### Building the MCP Server + +```bash +# Build the Detective backend (includes MCP server) +npm run mcp:build + +# Or build everything +npm run build +``` + +### Testing the Server + +```bash +# Test with MCP inspector tool +npm run mcp:inspect +``` + +### Running the Server + +The MCP server supports two transport modes: + +#### STDIO Transport (Default) + +Best for integration with Claude Desktop and other MCP clients: + +```bash +# Run the MCP server with STDIO transport +npm run mcp:serve + +# Development mode with auto-restart +npm run mcp:dev +``` + +#### Streamable HTTP Transport + +Best for web applications and streaming responses: + +```bash +# Run the MCP server with HTTP transport on port 3001 +npm run mcp:serve:http + +# Development mode with HTTP transport +npm run mcp:dev:http + +# Custom port +MCP_PORT=4000 MCP_TRANSPORT=http npm run mcp:serve +``` + +### Streaming Support + +The MCP server supports **real-time streaming** for long-running operations like trend analysis: + +- **STDIO Transport**: Streaming handled via MCP protocol +- **HTTP Transport**: Uses Streamable HTTP for real-time updates +- Add `"streaming": true` to tool parameters to enable streaming +- Currently supported by: `analyze_trends` + +## Integration with AI Assistants + +### Claude Desktop Configuration + +For **STDIO transport** (default), add to your `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "detective": { + "command": "node", + "args": ["/absolute/path/to/detective/dist/apps/backend/src/mcp/index.js"], + "cwd": "/path/to/repository/you/want/to/analyze" + } + } +} +``` + +For **Streamable HTTP transport**, use: + +```json +{ + "mcpServers": { + "detective": { + "command": "node", + "args": ["/absolute/path/to/detective/dist/apps/backend/src/mcp/index.js", "http"], + "env": { + "MCP_PORT": "3001" + }, + "cwd": "/path/to/repository/you/want/to/analyze" + } + } +} +``` + +**Important**: + +- Use absolute paths for the `command` and `args` +- Set `cwd` to the repository you want to analyze +- For streaming support, use HTTP transport +- The MCP server will analyze the repository specified in `cwd` + +### Example Claude Desktop Config Locations + +- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` +- **Linux**: `~/.config/claude/claude_desktop_config.json` + +### Testing the Integration + +1. Start Claude Desktop +2. Verify the MCP server appears in Claude's tools +3. Test with: "What repository am I working with?" (should call `get_repository_info`) +4. Test analysis: "Find the hotspots in this codebase" (should call `detect_hotspots`) + +### Using Streaming Features + +When using the HTTP transport, you can enable streaming for long-running operations: + +```bash +# Example API call with streaming enabled +curl -X POST http://localhost:3001/mcp \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "analyze_trends", + "arguments": { + "maxCommits": 100, + "streaming": true + } + } + }' +``` + +**For LLMs**: Simply add `"streaming": true` to your tool call parameters: + +- "Analyze trends for the last 100 commits with streaming enabled" +- The LLM will receive real-time progress updates during analysis + +## Advanced Usage + +### Custom Analysis Workflows + +The MCP server enables sophisticated analysis workflows: + +```javascript +// Example workflow for code review +1. get_repository_info() // Understand context +2. analyze_trends(maxCommits: 20) // Recent changes +3. detect_hotspots(minScore: 100) // Problem areas +4. xray_analyze() on each hotspot // Detailed analysis +5. get_file_structure() // Navigate to related files +``` + +### Performance Considerations + +- **Trend analysis**: Can be slow for large repositories or many commits +- **X-ray analysis**: Fast for individual files +- **Hotspot detection**: Moderate speed, depends on git history size +- **File structure**: Fast for most projects + +### Limitations + +- Supports TypeScript/JavaScript projects primarily +- Requires git history for trend analysis and hotspot detection +- File size limits may apply for very large files +- Complex monorepos may require longer processing times + +## Troubleshooting + +### Common Issues + +1. **"Path not found" errors**: + + - Ensure `cwd` in MCP config points to correct repository + - Use absolute paths in configuration + +2. **"Not a git repository" warnings**: + + - Some features require git history + - Initialize git repo or use non-git features only + +3. **TypeScript compilation errors**: + + - Run `npm run mcp:build` to compile the server + - Check TypeScript configuration + +4. **Permission errors**: + - Ensure MCP server has read access to target repository + - Check file permissions + +### Debug Mode + +Enable debug logging by setting environment variable: + +```bash +DEBUG=detective-mcp npm run mcp:serve +``` + +### Getting Help + +1. Check server logs for error messages +2. Verify MCP server builds without errors +3. Test with `npm run mcp:inspect` for interactive debugging +4. Ensure target repository has proper git history and file structure + +## Architecture + +The MCP server is built as a thin wrapper around Detective's existing analysis services: + +``` +MCP Client (Claude, etc.) + ↓ +MCP Protocol (stdio transport) + ↓ +Detective MCP Server + ↓ +Detective Analysis Services + ↓ +Git Repository + TypeScript Analysis +``` + +Each tool is implemented as a type-safe wrapper that: + +1. Validates inputs using Zod schemas +2. Calls existing Detective services +3. Formats results for MCP consumption +4. Provides comprehensive error handling + +This architecture ensures the MCP server is maintainable, reliable, and leverages Detective's proven analysis capabilities. diff --git a/apps/backend/src/mcp/http-router.ts b/apps/backend/src/mcp/http-router.ts new file mode 100644 index 0000000..e377084 --- /dev/null +++ b/apps/backend/src/mcp/http-router.ts @@ -0,0 +1,60 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import express from 'express'; +import type { Request, Response } from 'express'; + +export function createMcpHttpRouter(createServer: () => McpServer) { + const router = express.Router(); + + // Stateless Streamable HTTP endpoints (no SSE notifications) + router.post('/', async (req: Request, res: Response) => { + try { + const server = createServer(); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + + res.on('close', () => { + transport.close(); + server.close(); + }); + + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + } catch (error: unknown) { + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: '2.0', + error: { code: -32603, message: 'Internal server error' }, + id: null, + }); + } + } + }); + + router.get('/', async (_req: Request, res: Response) => { + res.status(405).end( + JSON.stringify({ + jsonrpc: '2.0', + error: { code: -32000, message: 'Method not allowed.' }, + id: null, + }) + ); + }); + + router.delete('/', async (_req: Request, res: Response) => { + res.status(405).end( + JSON.stringify({ + jsonrpc: '2.0', + error: { code: -32000, message: 'Method not allowed.' }, + id: null, + }) + ); + }); + + router.get('/health', async (_req: Request, res: Response) => { + res.json({ ok: true, transport: 'streamable-http' }); + }); + + return router; +} diff --git a/apps/backend/src/mcp/server.ts b/apps/backend/src/mcp/server.ts new file mode 100644 index 0000000..3696251 --- /dev/null +++ b/apps/backend/src/mcp/server.ts @@ -0,0 +1,394 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { z } from 'zod'; + +import { loadConfig } from '../infrastructure/config'; +import { DETECTIVE_VERSION } from '../infrastructure/version'; +import { Options } from '../options/options'; +import { calcChangeCoupling } from '../services/change-coupling'; +import { calcCoupling } from '../services/coupling'; +import { inferFolders } from '../services/folders'; +import { + aggregateHotspots, + findHotspotFiles, + HotspotCriteria, + ComplexityMetric, +} from '../services/hotspot'; +import { updateLogCache, isStale } from '../services/log-cache'; +import { calcModuleInfo } from '../services/module-info'; +import { calcTeamAlignment } from '../services/team-alignment'; +import { + runTrendAnalysis, + formatTrendAnalysisForAPI, +} from '../services/trend-analysis'; + +type Limits = { + limitCommits: number | null; + limitMonths: number | null; +}; + +const limitsSchema = z.object({ + limitCommits: z.number().int().nullable().optional(), + limitMonths: z.number().int().nullable().optional(), +}); +const limitsSchemaShape = { + limitCommits: z.number().int().nullable().optional(), + limitMonths: z.number().int().nullable().optional(), +} as const; +type LimitsArgs = { + limitCommits?: number | null; + limitMonths?: number | null; +}; + +function toLimits(input: Partial>): Limits { + return { + limitCommits: + typeof input.limitCommits === 'number' ? input.limitCommits : null, + limitMonths: + typeof input.limitMonths === 'number' ? input.limitMonths : null, + }; +} + +export function createMcpServer(options: Options): McpServer { + const server = new McpServer({ + name: 'detective-backend', + version: DETECTIVE_VERSION, + }); + + // config.read + server.registerTool( + 'config.read', + { + title: 'Read Detective config', + description: 'Returns the Detective configuration loaded from file', + inputSchema: {}, + }, + async () => { + const cfg = loadConfig(options); + return { + content: [{ type: 'text', text: JSON.stringify(cfg) }], + }; + } + ); + + // config.write + server.registerTool( + 'config.write', + { + title: 'Write Detective config', + description: + 'Persists the Detective configuration to the configured file', + inputSchema: { + config: z.object({ + scopes: z.array(z.string()), + groups: z.array(z.string()), + entries: z.array(z.unknown()), + filter: z.object({ + files: z.array(z.string()), + logs: z.array(z.string()), + }), + aliases: z.record(z.string(), z.string()), + teams: z.record(z.string(), z.array(z.string())), + }), + }, + }, + async ({ config }: { config?: unknown }) => { + if (!config || typeof config !== 'object') { + throw new Error("'config' object is required"); + } + const path = await import('node:path'); + const fs = await import('node:fs/promises'); + const filePath = path.join(process.cwd(), options.config); + await fs.writeFile(filePath, JSON.stringify(config, null, 2), 'utf8'); + return { + content: [{ type: 'text', text: JSON.stringify({ ok: true }) }], + }; + } + ); + + // cache.status + server.registerTool( + 'cache.status', + { + title: 'Get log cache status', + description: 'Returns whether the log cache is stale', + inputSchema: {}, + }, + async () => { + const stale = isStale(); + return { + content: [{ type: 'text', text: JSON.stringify({ isStale: stale }) }], + }; + } + ); + + // cache.update + server.registerTool( + 'cache.update', + { + title: 'Update log cache', + description: 'Updates the git log cache', + inputSchema: {}, + }, + async () => { + await updateLogCache(); + return { + content: [{ type: 'text', text: JSON.stringify({ ok: true }) }], + }; + } + ); + + // modules.get + server.registerTool( + 'modules.get', + { + title: 'Get module info', + description: 'Calculates module information', + inputSchema: {}, + }, + async () => { + const result = calcModuleInfo(options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // folders.get + server.registerTool( + 'folders.get', + { + title: 'Get inferred folders', + description: 'Infers folder structure from configuration', + inputSchema: {}, + }, + async () => { + const result = inferFolders(options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // coupling.get + server.registerTool( + 'coupling.get', + { + title: 'Get coupling', + description: 'Calculates coupling matrix and cohesion', + inputSchema: {}, + }, + async () => { + const result = calcCoupling(options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // changeCoupling.get + server.registerTool( + 'changeCoupling.get', + { + title: 'Get change coupling', + description: 'Calculates change coupling for recent history', + inputSchema: limitsSchemaShape, + }, + async (input: LimitsArgs) => { + const result = await calcChangeCoupling(toLimits(input), options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // teamAlignment.get + server.registerTool( + 'teamAlignment.get', + { + title: 'Get team alignment', + description: 'Calculates team alignment metrics', + inputSchema: { ...limitsSchemaShape, byUser: z.boolean().optional() }, + }, + async (input: LimitsArgs & { byUser?: boolean }) => { + const { byUser = false, ...rest } = input as LimitsArgs & { + byUser?: boolean; + }; + const result = await calcTeamAlignment(byUser, toLimits(rest), options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // hotspots.find + server.registerTool( + 'hotspots.find', + { + title: 'Find hotspots', + description: 'Finds hotspot files based on criteria and limits', + inputSchema: { + ...limitsSchemaShape, + module: z.string().default(''), + minScore: z.number().default(-1), + metric: z.enum(['McCabe', 'Length']).default('McCabe'), + }, + }, + async ( + input: LimitsArgs & { + module?: string; + minScore?: number; + metric?: ComplexityMetric; + } + ) => { + const criteria: HotspotCriteria = { + module: input.module ?? '', + minScore: typeof input.minScore === 'number' ? input.minScore : -1, + metric: (input.metric as ComplexityMetric) ?? 'McCabe', + }; + const limits = toLimits(input); + const result = await findHotspotFiles(criteria, limits, options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // hotspots.aggregate + server.registerTool( + 'hotspots.aggregate', + { + title: 'Aggregate hotspots', + description: 'Aggregates hotspot statistics per module', + inputSchema: { + ...limitsSchemaShape, + minScore: z.number().default(50), + metric: z.enum(['McCabe', 'Length']).default('McCabe'), + }, + }, + async ( + input: LimitsArgs & { minScore?: number; metric?: ComplexityMetric } + ) => { + const criteria: HotspotCriteria = { + module: '', + minScore: typeof input.minScore === 'number' ? input.minScore : 50, + metric: (input.metric as ComplexityMetric) ?? 'McCabe', + }; + const limits = toLimits(input); + const result = await aggregateHotspots(criteria, limits, options); + return { content: [{ type: 'text', text: JSON.stringify(result) }] }; + } + ); + + // trendAnalysis.run + server.registerTool( + 'trendAnalysis.run', + { + title: 'Run trend analysis', + description: 'Runs trend analysis and returns formatted results', + inputSchema: { + maxCommits: z.number().int().default(50), + parallelWorkers: z.number().int().min(1).max(10).default(5), + fileExtensions: z + .array(z.string()) + .default(['.ts', '.js', '.tsx', '.jsx']), + }, + }, + async (args: { + maxCommits?: number; + parallelWorkers?: number; + fileExtensions?: string[]; + }) => { + const maxCommits = args.maxCommits ?? 50; + const parallelWorkers = Math.max( + 1, + Math.min(10, args.parallelWorkers ?? 5) + ); + const fileExtensions = args.fileExtensions ?? [ + '.ts', + '.js', + '.tsx', + '.jsx', + ]; + const result = await runTrendAnalysis(options, { + maxCommits, + fileExtensions, + parallelWorkers, + }); + const formatted = await formatTrendAnalysisForAPI( + result, + options.path, + fileExtensions + ); + return { content: [{ type: 'text', text: JSON.stringify(formatted) }] }; + } + ); + + // xray.get + server.registerTool( + 'xray.get', + { + title: 'Get X-Ray analysis', + description: 'Analyzes a file and returns X-Ray metrics', + inputSchema: { + file: z.string(), + includeSource: z.boolean().default(false), + }, + }, + async ({ + file, + includeSource, + }: { + file?: string; + includeSource?: boolean; + }) => { + if (!file) { + throw new Error("Parameter 'file' is required"); + } + const path = await import('node:path'); + const fs = await import('node:fs'); + const repoRoot = path.resolve(options.path); + const fullPath = path.resolve(repoRoot, file); + const relativeToRoot = path.relative(repoRoot, fullPath); + if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) { + throw new Error('File path must be within the repository root'); + } + if (!fs.existsSync(fullPath)) { + throw new Error(`File not found: ${file}`); + } + const { CodeAnalyzer } = await import( + '../services/trend-analysis/x-ray/code-analyzer' + ); + const analyzer = new CodeAnalyzer(fullPath); + const metrics = await analyzer.analyze(includeSource ?? false); + return { content: [{ type: 'text', text: JSON.stringify(metrics) }] }; + } + ); + + // xray.schema + server.registerTool( + 'xray.schema', + { + title: 'Get X-Ray schema', + description: 'Returns JSON schema and UI schema for X-Ray', + inputSchema: {}, + }, + async () => { + const { CodeAnalyzer } = await import( + '../services/trend-analysis/x-ray/code-analyzer' + ); + const { buildBaseXRaySchema } = await import( + '../services/trend-analysis/x-ray/x-ray.schema' + ); + const jsonSchema = CodeAnalyzer.buildJSONSchema() as Record< + string, + unknown + >; + const uiSchema = + (jsonSchema as { [key: string]: unknown })['x-ui'] ?? + CodeAnalyzer.buildUISchema(); + const base = buildBaseXRaySchema(); + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + version: base.version, + jsonSchema, + uiSchema, + }), + }, + ], + }; + } + ); + + return server; +} diff --git a/apps/backend/src/options/options.ts b/apps/backend/src/options/options.ts index 40906fa..6327425 100644 --- a/apps/backend/src/options/options.ts +++ b/apps/backend/src/options/options.ts @@ -5,6 +5,7 @@ export type Options = { port: number; demoMode: boolean; open: boolean; + trendAnalysis: boolean; }; export const defaultOptions: Options = { @@ -14,4 +15,5 @@ export const defaultOptions: Options = { port: 3334, demoMode: false, open: true, + trendAnalysis: false, }; diff --git a/apps/backend/src/options/parse-options.ts b/apps/backend/src/options/parse-options.ts index 5836059..478f338 100644 --- a/apps/backend/src/options/parse-options.ts +++ b/apps/backend/src/options/parse-options.ts @@ -16,6 +16,8 @@ export function parseOptions(args: string[]): Options { state = 'open'; } else if (arg === '--demo') { parsed.demoMode = true; + } else if (arg === '--trend-analysis') { + parsed.trendAnalysis = true; } else if (!parsed.sheriffDump) { parsed.sheriffDump = arg; } diff --git a/apps/backend/src/services/team-alignment.ts b/apps/backend/src/services/team-alignment.ts index ad204ad..eb9725f 100644 --- a/apps/backend/src/services/team-alignment.ts +++ b/apps/backend/src/services/team-alignment.ts @@ -29,6 +29,7 @@ export async function calcTeamAlignment( const result = initResult(displayModules, Object.keys(teams)); const actualTeams = new Set(); + const userKeyToDisplay: Record = {}; const parseOptions: ParseOptions = { limits, @@ -52,7 +53,14 @@ export async function calcTeamAlignment( userName = config.aliases?.[userName] || userName; - const key = calcKey(byUser, userName, userToTeam); + const emailLower = (entry.header.email || '').toLowerCase(); + const stableUserKey = byUser ? emailLower || userName : userName; + + if (!userKeyToDisplay[stableUserKey]) { + userKeyToDisplay[stableUserKey] = userName; + } + + const key = byUser ? stableUserKey : calcKey(false, userName, userToTeam); actualTeams.add(key); for (const change of entry.body) { @@ -71,7 +79,21 @@ export async function calcTeamAlignment( } }, parseOptions); - result.teams = Array.from(actualTeams).sort(); + if (byUser) { + for (const module of Object.keys(result.modules)) { + const changes = result.modules[module].changes; + const remapped: Record = {}; + for (const stableKey of Object.keys(changes)) { + const base = userKeyToDisplay[stableKey] || stableKey; + remapped[base] = (remapped[base] || 0) + changes[stableKey]; + } + result.modules[module].changes = remapped; + } + const mapped = Array.from(actualTeams).map((k) => userKeyToDisplay[k] || k); + result.teams = Array.from(new Set(mapped)).sort(); + } else { + result.teams = Array.from(actualTeams).sort(); + } return result; } diff --git a/apps/backend/src/services/trend-analysis/api-formatter.ts b/apps/backend/src/services/trend-analysis/api-formatter.ts new file mode 100644 index 0000000..2784afd --- /dev/null +++ b/apps/backend/src/services/trend-analysis/api-formatter.ts @@ -0,0 +1,32 @@ +import { DataFormatter } from './data-formatter'; +import { GitService } from './git-service'; +import { TrendAnalysisResult } from './trend-analysis.types'; + +const dataFormatter = new DataFormatter(); + +export async function formatTrendAnalysisForAPI( + result: TrendAnalysisResult, + projectPath: string = process.cwd(), + fileExtensions: string[] = ['.ts', '.js', '.tsx', '.jsx'] +) { + // Get ALL current files that exist in the latest commit + const gitService = new GitService(projectPath); + const currentFiles = await gitService.getCurrentFiles(fileExtensions); + + // Create entries for ALL current files (ground truth) + const files = currentFiles.map((filePath) => { + const trendData = result.fileMetrics.get(filePath); + return dataFormatter.formatFileTrendForAPI(filePath, trendData); + }); + + return { + files, + summary: { + totalProcessingTimeMs: result.totalProcessingTimeMs, + commitsAnalyzed: result.commitsAnalyzed, + filesAnalyzed: result.filesAnalyzed, + commitHashes: dataFormatter.formatCommitHashes(result.commitHashes), + timingMetrics: result.timingMetrics, + }, + }; +} diff --git a/apps/backend/src/services/trend-analysis/cache-manager.ts b/apps/backend/src/services/trend-analysis/cache-manager.ts new file mode 100644 index 0000000..0c8d981 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/cache-manager.ts @@ -0,0 +1,88 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { DETECTIVE_DIR, TREND_CACHE_FILE } from '../../infrastructure/paths'; +import { DETECTIVE_VERSION } from '../../infrastructure/version'; + +import { CachedCommitMetrics, CommitMetrics } from './trend-analysis.types'; + +export class CacheManager { + private projectPath: string; + private fileExtensions: string[]; + + constructor(projectPath: string, fileExtensions: string[]) { + this.projectPath = projectPath; + this.fileExtensions = fileExtensions; + } + + private getCacheFilePath(): string { + const detectiveDir = path.join(this.projectPath, DETECTIVE_DIR); + if (!fs.existsSync(detectiveDir)) { + fs.mkdirSync(detectiveDir, { recursive: true }); + } + return path.join(detectiveDir, TREND_CACHE_FILE); + } + + getCacheKey(commitHash: string): string { + return `${commitHash}:${this.fileExtensions.join( + ',' + )}:${DETECTIVE_VERSION}`; + } + + load(): Map { + const cacheFile = this.getCacheFilePath(); + if (!fs.existsSync(cacheFile)) { + return new Map(); + } + + try { + const cacheData = JSON.parse(fs.readFileSync(cacheFile, 'utf-8')); + const cache = new Map(); + + if (cacheData.commitCache && Array.isArray(cacheData.commitCache)) { + for (const [commitHash, cached] of cacheData.commitCache) { + if ( + cached.version === DETECTIVE_VERSION && + this.arraysEqual(cached.fileExtensions, this.fileExtensions) + ) { + cached.metrics = cached.metrics.map( + (m: CommitMetrics & { commitDate: string }) => ({ + ...m, + commitDate: new Date(m.commitDate), + }) + ); + cache.set(commitHash, cached); + } + } + } + + console.log(`[TREND-CACHE] Loaded ${cache.size} cached commits`); + return cache; + } catch (error) { + console.warn( + '[TREND-CACHE] Failed to load cache, starting fresh:', + error.message + ); + return new Map(); + } + } + + save(cache: Map): void { + try { + const cacheFile = this.getCacheFilePath(); + const cacheData = { + version: DETECTIVE_VERSION, + commitCache: Array.from(cache.entries()), + }; + + fs.writeFileSync(cacheFile, JSON.stringify(cacheData, null, 2), 'utf-8'); + console.log(`[TREND-CACHE] Saved ${cache.size} commits to cache`); + } catch (error) { + console.warn('[TREND-CACHE] Failed to save cache:', error.message); + } + } + + private arraysEqual(a: string[], b: string[]): boolean { + return a.length === b.length && a.every((val, index) => val === b[index]); + } +} diff --git a/apps/backend/src/services/trend-analysis/data-formatter.ts b/apps/backend/src/services/trend-analysis/data-formatter.ts new file mode 100644 index 0000000..c6dbbbb --- /dev/null +++ b/apps/backend/src/services/trend-analysis/data-formatter.ts @@ -0,0 +1,118 @@ +import { + CommitMetrics, + FileTrendData, + PROGRESS_CONSTANTS, +} from './trend-analysis.types'; + +export class DataFormatter { + formatCommitForAPI(commit: CommitMetrics) { + return { + commitHash: this.truncateCommitHash(commit.commitHash), + date: commit.commitDate.toISOString(), + author: commit.author, + message: commit.commitMessage.trim(), + linesAdded: commit.linesAdded, + linesRemoved: commit.linesRemoved, + totalLines: commit.totalLinesAtCommit, + complexity: commit.mccabeComplexityAtCommit, + }; + } + + formatTrendPointForAPI(trend: { + commitHash: string; + complexity?: number; + lines?: number; + date: Date; + }) { + const base = { + commit: this.truncateCommitHash(trend.commitHash), + date: trend.date.toISOString(), + }; + return 'complexity' in trend + ? { ...base, complexity: trend.complexity } + : { ...base, lines: trend.lines }; + } + + formatFileTrendForAPI(filePath: string, trendData?: FileTrendData) { + if (!trendData) { + return { + filePath, + changeFrequency: 0, + averageComplexity: 0, + averageSize: 0, + totalChanges: 0, + commits: [], + complexityTrend: [], + sizeTrend: [], + }; + } + + return { + filePath, + changeFrequency: trendData.changeFrequency, + averageComplexity: trendData.averageComplexity, + averageSize: trendData.averageSize, + totalChanges: trendData.totalChanges, + commits: trendData.commits.map((c) => this.formatCommitForAPI(c)), + complexityTrend: trendData.complexityTrend.map((t) => + this.formatTrendPointForAPI({ ...t, complexity: t.complexity }) + ), + sizeTrend: trendData.sizeTrend.map((t) => + this.formatTrendPointForAPI({ ...t, lines: t.lines }) + ), + }; + } + + formatIncrementalTrendData(fileMetrics: Map) { + const trendArray: ReturnType[] = []; + + for (const [filePath, commits] of fileMetrics.entries()) { + const latestCommit = commits[commits.length - 1]; + if (latestCommit) { + const averageComplexity = + Math.round( + (commits.reduce((sum, c) => sum + c.mccabeComplexityAtCommit, 0) / + commits.length) * + 10 + ) / 10; + const averageSize = Math.round( + commits.reduce((sum, c) => sum + c.totalLinesAtCommit, 0) / + commits.length + ); + const totalChanges = commits.reduce( + (sum, c) => sum + c.linesAdded + c.linesRemoved, + 0 + ); + + trendArray.push({ + filePath, + changeFrequency: commits.length, + averageComplexity, + averageSize, + totalChanges, + commits: commits.map((c) => this.formatCommitForAPI(c)), + complexityTrend: commits.map((c) => ({ + commit: this.truncateCommitHash(c.commitHash), + date: c.commitDate.toISOString(), + complexity: c.mccabeComplexityAtCommit, + })), + sizeTrend: commits.map((c) => ({ + commit: this.truncateCommitHash(c.commitHash), + date: c.commitDate.toISOString(), + lines: c.totalLinesAtCommit, + })), + }); + } + } + + return trendArray; + } + + truncateCommitHash(commitHash: string): string { + return commitHash.substring(0, PROGRESS_CONSTANTS.COMMIT_HASH_LENGTH); + } + + formatCommitHashes(hashes: string[]): string[] { + return hashes.map((h) => this.truncateCommitHash(h)); + } +} diff --git a/apps/backend/src/services/trend-analysis/git-service.ts b/apps/backend/src/services/trend-analysis/git-service.ts new file mode 100644 index 0000000..e7db110 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/git-service.ts @@ -0,0 +1,155 @@ +import { spawn } from 'child_process'; + +export interface CommitInfo { + hash: string; + date: Date; + author: string; + message: string; +} + +export interface FileChange { + path: string; + linesAdded: number; + linesRemoved: number; +} + +export class GitService { + private projectPath: string; + + constructor(projectPath: string) { + this.projectPath = projectPath; + } + + private async executeCommand(args: string[]): Promise { + return new Promise((resolve, reject) => { + const gitProcess = spawn('git', args, { + cwd: this.projectPath, + stdio: ['ignore', 'pipe', 'pipe'], + }); + + let stdout = ''; + let stderr = ''; + + gitProcess.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + gitProcess.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + gitProcess.on('close', (code) => { + if (code !== 0) { + reject(new Error(`Git command failed (${code}): ${stderr}`)); + return; + } + resolve(stdout); + }); + + gitProcess.on('error', (error) => { + reject(new Error(`Failed to spawn git process: ${error.message}`)); + }); + }); + } + + async getCommitList(maxCommits: number): Promise { + try { + const output = await this.executeCommand([ + 'log', + `--max-count=${maxCommits * 2}`, + '--pretty=format:%H|%ai|%an|%s', + '--no-merges', + ]); + + const commits: CommitInfo[] = []; + const lines = output + .trim() + .split('\n') + .filter((line) => line.trim()); + + for (const line of lines) { + const parts = line.split('|'); + if (parts.length >= 4) { + commits.push({ + hash: parts[0], + date: new Date(parts[1]), + author: parts[2], + message: parts.slice(3).join('|'), + }); + } + } + + return commits; + } catch (error) { + throw new Error(`Failed to get commit list: ${error.message}`); + } + } + + async getChangedFiles(commitHash: string): Promise { + try { + const output = await this.executeCommand([ + 'show', + '--numstat', + '--format=', + commitHash, + ]); + + const changes: FileChange[] = []; + const lines = output + .trim() + .split('\n') + .filter((line) => line.trim()); + + for (const line of lines) { + const parts = line.split('\t'); + if (parts.length >= 3) { + const added = parts[0] === '-' ? 0 : parseInt(parts[0], 10) || 0; + const removed = parts[1] === '-' ? 0 : parseInt(parts[1], 10) || 0; + const filePath = parts.slice(2).join('\t'); + + changes.push({ + path: filePath, + linesAdded: added, + linesRemoved: removed, + }); + } + } + + return changes; + } catch (error) { + return []; + } + } + + async getFileContentAtCommit( + commitHash: string, + filePath: string + ): Promise { + try { + const content = await this.executeCommand([ + 'show', + `${commitHash}:${filePath}`, + ]); + return content; + } catch (error) { + return null; + } + } + + async getCurrentFiles(fileExtensions: string[]): Promise { + try { + const output = await this.executeCommand(['ls-files']); + + const allFiles = output + .trim() + .split('\n') + .filter((line) => line.trim()); + + return allFiles.filter((filePath) => + fileExtensions.some((ext) => filePath.endsWith(ext)) + ); + } catch (error) { + throw new Error(`Failed to get current files: ${error.message}`); + } + } +} diff --git a/apps/backend/src/services/trend-analysis/index.ts b/apps/backend/src/services/trend-analysis/index.ts new file mode 100644 index 0000000..6e2c8e1 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/index.ts @@ -0,0 +1,13 @@ +// Refactored trend analysis components +export { TrendAnalyzer, runTrendAnalysis } from './trend-analyzer'; +export { CacheManager } from './cache-manager'; +export { GitService } from './git-service'; +export { MetricsCalculator } from './metrics-calculator'; +export { DataFormatter } from './data-formatter'; +export { ProgressReporter } from './progress-reporter'; + +// Keep compatibility with existing API +export { formatTrendAnalysisForAPI } from './api-formatter'; + +// Export types +export * from './trend-analysis.types'; diff --git a/apps/backend/src/services/trend-analysis/metrics-calculator.ts b/apps/backend/src/services/trend-analysis/metrics-calculator.ts new file mode 100644 index 0000000..e197ab4 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/metrics-calculator.ts @@ -0,0 +1,90 @@ +import { calcComplexityForCode } from '../../utils/complexity'; + +import { CommitInfo, FileChange } from './git-service'; +import { CommitMetrics, FileTrendData } from './trend-analysis.types'; + +export class MetricsCalculator { + calculateFileMetrics( + commit: CommitInfo, + fileChange: FileChange, + fileContent: string, + processingTime: number + ): CommitMetrics { + const totalLinesAtCommit = fileContent.split('\n').length; + const mccabeComplexityAtCommit = this.calculateComplexity( + fileChange.path, + fileContent + ); + + return { + commitHash: commit.hash, + commitDate: commit.date, + author: commit.author, + commitMessage: commit.message, + filePath: fileChange.path, + linesAdded: fileChange.linesAdded, + linesRemoved: fileChange.linesRemoved, + totalLinesAtCommit, + mccabeComplexityAtCommit, + processingTimeMs: processingTime, + }; + } + + private calculateComplexity(filePath: string, content: string): number { + if (filePath.endsWith('.ts') || filePath.endsWith('.js')) { + return calcComplexityForCode(content); + } + return 1; + } + + aggregateMetricsIntoTrends( + fileMetrics: Map + ): Map { + const trendData = new Map(); + + for (const [filePath, commits] of fileMetrics.entries()) { + commits.sort((a, b) => a.commitDate.getTime() - b.commitDate.getTime()); + + const complexityTrend = commits.map((c) => ({ + commitHash: c.commitHash, + complexity: c.mccabeComplexityAtCommit, + date: c.commitDate, + })); + + const sizeTrend = commits.map((c) => ({ + commitHash: c.commitHash, + lines: c.totalLinesAtCommit, + date: c.commitDate, + })); + + const averageComplexity = + commits.reduce((sum, c) => sum + c.mccabeComplexityAtCommit, 0) / + commits.length; + const averageSize = + commits.reduce((sum, c) => sum + c.totalLinesAtCommit, 0) / + commits.length; + const totalChanges = commits.reduce( + (sum, c) => sum + c.linesAdded + c.linesRemoved, + 0 + ); + + trendData.set(filePath, { + filePath, + commits, + changeFrequency: commits.length, + complexityTrend, + sizeTrend, + averageComplexity: Math.round(averageComplexity * 10) / 10, + averageSize: Math.round(averageSize), + totalChanges, + }); + } + + return trendData; + } + + calculateAverageFromArray(values: number[]): number { + if (values.length === 0) return 0; + return values.reduce((a, b) => a + b, 0) / values.length; + } +} diff --git a/apps/backend/src/services/trend-analysis/progress-reporter.ts b/apps/backend/src/services/trend-analysis/progress-reporter.ts new file mode 100644 index 0000000..319827a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/progress-reporter.ts @@ -0,0 +1,48 @@ +import { ProgressUpdate, PROGRESS_CONSTANTS } from './trend-analysis.types'; + +export class ProgressReporter { + private progressCallback?: (update: ProgressUpdate) => void; + + constructor(progressCallback?: (update: ProgressUpdate) => void) { + this.progressCallback = progressCallback; + } + + send( + type: ProgressUpdate['type'], + message: string, + progress: number, + commitsProcessed?: number, + totalCommits?: number, + filesAnalyzed?: number, + currentCommit?: string, + currentAuthor?: string, + processingTimeMs?: number, + data?: unknown + ) { + if (this.progressCallback) { + this.progressCallback({ + type, + message, + progress: Math.min(100, Math.max(0, progress)), + commitsProcessed, + totalCommits, + filesAnalyzed, + currentCommit, + currentAuthor, + processingTimeMs, + data, + }); + } + } + + calculateBatchProgress(current: number, total: number): number { + return ( + PROGRESS_CONSTANTS.INITIAL_PROGRESS + + Math.round((current / total) * PROGRESS_CONSTANTS.BATCH_PROGRESS_WEIGHT) + ); + } + + truncateCommitHash(commitHash: string): string { + return commitHash.substring(0, PROGRESS_CONSTANTS.COMMIT_HASH_LENGTH); + } +} diff --git a/apps/backend/src/services/trend-analysis/trend-analysis.types.ts b/apps/backend/src/services/trend-analysis/trend-analysis.types.ts new file mode 100644 index 0000000..f452088 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/trend-analysis.types.ts @@ -0,0 +1,126 @@ +/** + * Type definitions for trend analysis functionality + */ + +// Constants for progress calculation and display +export const PROGRESS_CONSTANTS = { + INITIAL_PROGRESS: 15, + BATCH_PROGRESS_WEIGHT: 80, + FINAL_PROGRESS: 95, + COMMIT_HASH_LENGTH: 8, +} as const; + +/** + * Cache entry for a single commit's file metrics + */ +export type CachedCommitMetrics = { + commitHash: string; + fileExtensions: string[]; + version: string; + timestamp: number; + metrics: CommitMetrics[]; +}; + +/** + * Complete cache structure for trend analysis + */ +export type TrendAnalysisCache = { + version: string; + commitCache: Map; +}; + +/** + * Metrics for a single commit affecting a specific file + */ +export type CommitMetrics = { + commitHash: string; + commitDate: Date; + author: string; + commitMessage: string; + filePath: string; + linesAdded: number; + linesRemoved: number; + totalLinesAtCommit: number; + mccabeComplexityAtCommit: number; + processingTimeMs: number; +}; + +/** + * Aggregated trend data for a file across multiple commits + */ +export type FileTrendData = { + filePath: string; + commits: CommitMetrics[]; + changeFrequency: number; + complexityTrend: Array<{ + commitHash: string; + complexity: number; + date: Date; + }>; + sizeTrend: Array<{ commitHash: string; lines: number; date: Date }>; + averageComplexity: number; + averageSize: number; + totalChanges: number; +}; + +/** + * Timing metrics for performance monitoring + */ +export type TimingMetrics = { + totalTime: number; + gitOperationsTime: number; + analysisTime: number; + commitListTime: number; + fileProcessingTime: number; + averageCommitTime: number; + averageFileTime: number; +}; + +/** + * Complete result of trend analysis + */ +export type TrendAnalysisResult = { + fileMetrics: Map; + totalProcessingTimeMs: number; + commitsAnalyzed: number; + filesAnalyzed: number; + commitHashes: string[]; + timingMetrics: TimingMetrics; +}; + +/** + * Progress update for streaming analysis results + */ +export type ProgressUpdate = { + type: 'progress' | 'commit_complete' | 'file_analyzed' | 'complete' | 'error'; + message: string; + progress: number; // 0-100 + commitsProcessed?: number; + totalCommits?: number; + filesAnalyzed?: number; + currentCommit?: string; + currentAuthor?: string; + processingTimeMs?: number; + data?: unknown; +}; + +/** + * Configuration options for trend analysis + */ +export type TrendAnalysisOptions = { + maxCommits?: number; + fileExtensions?: string[]; + progressCallback?: (update: ProgressUpdate) => void; + parallelWorkers?: number; // Number of parallel workers (default 5) +}; + +/** + * Default options for trend analysis + */ +export const DEFAULT_OPTIONS: Required< + Omit +> = { + maxCommits: 20, + fileExtensions: ['.ts', '.js', '.tsx', '.jsx'], + parallelWorkers: 5, +}; diff --git a/apps/backend/src/services/trend-analysis/trend-analyzer.ts b/apps/backend/src/services/trend-analysis/trend-analyzer.ts new file mode 100644 index 0000000..1677d19 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/trend-analyzer.ts @@ -0,0 +1,568 @@ +import { DETECTIVE_VERSION } from '../../infrastructure/version'; +import { Options } from '../../options/options'; + +import { CacheManager } from './cache-manager'; +import { DataFormatter } from './data-formatter'; +import { GitService, CommitInfo } from './git-service'; +import { MetricsCalculator } from './metrics-calculator'; +import { ProgressReporter } from './progress-reporter'; +import { + CommitMetrics, + TimingMetrics, + TrendAnalysisResult, + TrendAnalysisOptions, + DEFAULT_OPTIONS, + CachedCommitMetrics, +} from './trend-analysis.types'; + +interface TimingData { + gitOperationsTime: number; + analysisTime: number; + commitListTime: number; + fileProcessingTime: number; + commitTimes: number[]; + fileTimes: number[]; +} + +interface CommitTiming { + commitTime: number; + gitTime: number; + analysisTime: number; + fileTime: number; + fileTimings: number[]; +} + +export class TrendAnalyzer { + private options: TrendAnalysisOptions & + Required>; + private projectPath: string; + private fileMetrics = new Map(); + + private cacheManager: CacheManager; + private gitService: GitService; + private metricsCalculator: MetricsCalculator; + private dataFormatter: DataFormatter; + private progressReporter: ProgressReporter; + + constructor( + projectOptions: Options, + trendOptions: TrendAnalysisOptions = {} + ) { + this.options = { ...DEFAULT_OPTIONS, ...trendOptions }; + this.projectPath = projectOptions.path; + + // Initialize services + this.cacheManager = new CacheManager( + this.projectPath, + this.options.fileExtensions + ); + this.gitService = new GitService(this.projectPath); + this.metricsCalculator = new MetricsCalculator(); + this.dataFormatter = new DataFormatter(); + this.progressReporter = new ProgressReporter(this.options.progressCallback); + } + + async analyzeTrends(): Promise { + const startTime = Date.now(); + const timingData = this.initializeTimingData(); + let commitsAnalyzed = 0; + let filesAnalyzed = 0; + const commitHashes: string[] = []; + + this.progressReporter.send( + 'progress', + 'Starting git-based trend analysis...', + 0 + ); + + try { + // Load cache and get commits + const cache = this.cacheManager.load(); + const { commits, commitListTime } = await this.getCommitListWithTiming(); + timingData.commitListTime = commitListTime; + timingData.gitOperationsTime += commitListTime; + + this.progressReporter.send( + 'progress', + `Found ${commits.length} commits to analyze (${commitListTime}ms)`, + 5 + ); + + // Separate cached and new commits + const { cachedCommitsLoaded, newCommitsToProcess } = + await this.separateCachedCommits(commits, cache, commitHashes); + + this.progressReporter.send( + 'progress', + `Using ${cachedCommitsLoaded} cached commits, processing ${newCommitsToProcess.length} new commits`, + 10 + ); + + // Process new commits + if (newCommitsToProcess.length > 0) { + const processResult = await this.processNewCommits( + newCommitsToProcess, + cachedCommitsLoaded, + cache, + timingData + ); + commitsAnalyzed = processResult.commitsAnalyzed; + filesAnalyzed = processResult.filesAnalyzed; + commitHashes.push(...processResult.commitHashes); + + this.cacheManager.save(cache); + } else { + commitsAnalyzed = cachedCommitsLoaded; + this.progressReporter.send( + 'progress', + 'All commits found in cache, skipping processing', + 90 + ); + } + + // Calculate final trend data + this.progressReporter.send('progress', 'Calculating trend data...', 95); + } catch (error) { + this.progressReporter.send( + 'error', + `Analysis failed: ${error.message}`, + 0 + ); + throw error; + } + + return this.buildFinalResult( + startTime, + timingData, + commitsAnalyzed, + filesAnalyzed, + commitHashes + ); + } + + private initializeTimingData() { + return { + gitOperationsTime: 0, + analysisTime: 0, + commitListTime: 0, + fileProcessingTime: 0, + commitTimes: [] as number[], + fileTimes: [] as number[], + }; + } + + private async getCommitListWithTiming() { + const startTime = Date.now(); + const commits = await this.gitService.getCommitList( + this.options.maxCommits + ); + const commitListTime = Date.now() - startTime; + return { commits, commitListTime }; + } + + private async separateCachedCommits( + commits: CommitInfo[], + cache: Map, + commitHashes: string[] + ) { + let cachedCommitsLoaded = 0; + const newCommitsToProcess: CommitInfo[] = []; + const commitsToAnalyze = commits.slice(0, this.options.maxCommits); + + for (const commit of commitsToAnalyze) { + const cacheKey = this.cacheManager.getCacheKey(commit.hash); + if (cache.has(cacheKey)) { + const cached = cache.get(cacheKey); + if (!cached) continue; + this.loadCachedMetrics(cached.metrics); + cachedCommitsLoaded++; + commitHashes.push(commit.hash); + } else { + newCommitsToProcess.push(commit); + } + } + + return { cachedCommitsLoaded, newCommitsToProcess }; + } + + private loadCachedMetrics(metrics: CommitMetrics[]) { + metrics.forEach((metric) => { + if (!this.fileMetrics.has(metric.filePath)) { + this.fileMetrics.set(metric.filePath, []); + } + this.fileMetrics.get(metric.filePath)?.push(metric); + }); + } + + private async processNewCommits( + newCommitsToProcess: CommitInfo[], + cachedCommitsLoaded: number, + cache: Map, + timingData: TimingData + ) { + let commitsAnalyzed = cachedCommitsLoaded; + let filesAnalyzed = 0; + const commitHashes: string[] = []; + + const batchSize = this.options.parallelWorkers; + for (let i = 0; i < newCommitsToProcess.length; i += batchSize) { + const batch = newCommitsToProcess.slice(i, i + batchSize); + const batchResult = await this.processBatch( + batch, + cachedCommitsLoaded, + i, + newCommitsToProcess.length, + cache, + timingData + ); + + commitsAnalyzed += batchResult.successCount; + filesAnalyzed += batchResult.filesProcessed; + commitHashes.push(...batchResult.commitHashes); + + // Send incremental update + this.sendIncrementalUpdate( + i, + batch.length, + batchSize, + newCommitsToProcess.length, + cachedCommitsLoaded, + batchResult.batchDuration + ); + } + + return { commitsAnalyzed, filesAnalyzed, commitHashes }; + } + + private async processBatch( + batch: CommitInfo[], + cachedCommitsLoaded: number, + batchIndex: number, + totalNewCommits: number, + cache: Map, + timingData: TimingData + ) { + const batchStartTime = Date.now(); + const batchPromises = batch.map((commit, index) => + this.processCommit( + commit, + cachedCommitsLoaded + batchIndex + index + 1, + cachedCommitsLoaded + totalNewCommits + ) + ); + + const batchResults = await Promise.allSettled(batchPromises); + let successCount = 0; + let filesProcessed = 0; + const commitHashes: string[] = []; + + batchResults.forEach((result, index) => { + const commit = batch[index]; + if (result.status === 'fulfilled' && result.value) { + const { metrics, commitHash, timing } = result.value; + this.updateTimingData(timingData, timing); + this.cacheCommitResult(cache, commitHash, metrics); + this.addMetricsToFileMap(metrics); + + successCount++; + filesProcessed += metrics.length; + commitHashes.push(commitHash); + } else { + this.progressReporter.send( + 'error', + `Failed to process commit ${this.dataFormatter.truncateCommitHash( + commit.hash + )}: ${ + result.status === 'rejected' ? result.reason : 'Unknown error' + }`, + this.progressReporter.calculateBatchProgress( + cachedCommitsLoaded + batchIndex + index + 1, + cachedCommitsLoaded + totalNewCommits + ) + ); + } + }); + + return { + successCount, + filesProcessed, + commitHashes, + batchDuration: Date.now() - batchStartTime, + }; + } + + private updateTimingData(timingData: TimingData, timing: CommitTiming) { + timingData.commitTimes.push(timing.commitTime); + timingData.gitOperationsTime += timing.gitTime; + timingData.analysisTime += timing.analysisTime; + timingData.fileProcessingTime += timing.fileTime; + timingData.fileTimes.push(...timing.fileTimings); + } + + private cacheCommitResult( + cache: Map, + commitHash: string, + metrics: CommitMetrics[] + ) { + const cacheKey = this.cacheManager.getCacheKey(commitHash); + cache.set(cacheKey, { + commitHash, + fileExtensions: [...this.options.fileExtensions], + version: DETECTIVE_VERSION, + timestamp: Date.now(), + metrics: [...metrics], + }); + } + + private addMetricsToFileMap(metrics: CommitMetrics[]) { + metrics.forEach((metric) => { + if (!this.fileMetrics.has(metric.filePath)) { + this.fileMetrics.set(metric.filePath, []); + } + this.fileMetrics.get(metric.filePath)?.push(metric); + }); + } + + private sendIncrementalUpdate( + batchIndex: number, + batchSize: number, + totalBatchSize: number, + totalNewCommits: number, + cachedCommitsLoaded: number, + batchDuration: number + ) { + const progressPercent = this.progressReporter.calculateBatchProgress( + cachedCommitsLoaded + batchIndex + batchSize, + cachedCommitsLoaded + totalNewCommits + ); + + const trendArray = this.dataFormatter.formatIncrementalTrendData( + this.fileMetrics + ); + + this.progressReporter.send( + 'commit_complete', + `Processed batch ${ + Math.floor(batchIndex / totalBatchSize) + 1 + }/${Math.ceil( + totalNewCommits / totalBatchSize + )} in ${batchDuration}ms - ${this.fileMetrics.size} files with trends`, + progressPercent, + cachedCommitsLoaded + batchIndex + batchSize, + cachedCommitsLoaded + totalNewCommits, + this.fileMetrics.size, + undefined, + undefined, + batchDuration, + { + files: trendArray, + commitsAnalyzed: cachedCommitsLoaded + batchIndex + batchSize, + filesAnalyzed: this.fileMetrics.size, + totalProcessingTimeMs: batchDuration, + } + ); + } + + private async processCommit( + commit: CommitInfo, + commitNumber: number, + totalCommits: number + ): Promise<{ + metrics: CommitMetrics[]; + commitHash: string; + timing: { + commitTime: number; + gitTime: number; + analysisTime: number; + fileTime: number; + fileTimings: number[]; + }; + } | null> { + const commitStartTime = Date.now(); + let gitTime = 0; + let analysisTime = 0; + let fileTime = 0; + const fileTimings: number[] = []; + + try { + this.progressReporter.send( + 'progress', + `Processing commit ${commitNumber}/${totalCommits}: ${this.dataFormatter.truncateCommitHash( + commit.hash + )}`, + this.progressReporter.calculateBatchProgress( + commitNumber, + totalCommits + ), + commitNumber - 1, + totalCommits, + undefined, + commit.hash, + commit.author + ); + + // Get changed files + const gitStartTime = Date.now(); + const changedFiles = await this.gitService.getChangedFiles(commit.hash); + gitTime += Date.now() - gitStartTime; + + // Analyze files + const analysisStartTime = Date.now(); + const filePromises = changedFiles + .filter((fileChange) => this.shouldAnalyzeFile(fileChange.path)) + .map((fileChange) => this.analyzeFileAtCommit(commit, fileChange)); + + const fileResults = await Promise.allSettled(filePromises); + analysisTime += Date.now() - analysisStartTime; + + const commitMetrics: CommitMetrics[] = []; + fileResults.forEach((result) => { + if (result.status === 'fulfilled' && result.value) { + const { metrics, timing } = result.value; + commitMetrics.push(metrics); + fileTime += timing; + fileTimings.push(timing); + + this.progressReporter.send( + 'file_analyzed', + `${metrics.filePath}: ${metrics.totalLinesAtCommit} lines, complexity ${metrics.mccabeComplexityAtCommit} (${timing}ms)`, + this.progressReporter.calculateBatchProgress( + commitNumber, + totalCommits + ), + commitNumber, + totalCommits + ); + } + }); + + const commitTime = Date.now() - commitStartTime; + this.progressReporter.send( + 'commit_complete', + `Commit ${commit.hash.substring(0, 8)} completed in ${commitTime}ms (${ + commitMetrics.length + } files, git: ${gitTime}ms, analysis: ${analysisTime}ms)`, + this.progressReporter.calculateBatchProgress( + commitNumber, + totalCommits + ), + commitNumber, + totalCommits, + undefined, + commit.hash, + commit.author, + commitTime + ); + + return { + metrics: commitMetrics, + commitHash: commit.hash, + timing: { commitTime, gitTime, analysisTime, fileTime, fileTimings }, + }; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + this.progressReporter.send( + 'error', + `Error processing commit ${this.dataFormatter.truncateCommitHash( + commit.hash + )}: ${errorMessage}`, + this.progressReporter.calculateBatchProgress(commitNumber, totalCommits) + ); + return null; + } + } + + private shouldAnalyzeFile(filePath: string): boolean { + return this.options.fileExtensions.some((ext) => filePath.endsWith(ext)); + } + + private async analyzeFileAtCommit( + commit: CommitInfo, + fileChange: { path: string; linesAdded: number; linesRemoved: number } + ): Promise<{ metrics: CommitMetrics; timing: number } | null> { + const fileStartTime = Date.now(); + + try { + const fileContent = await this.gitService.getFileContentAtCommit( + commit.hash, + fileChange.path + ); + + if (!fileContent) { + return null; + } + + const processingTime = Date.now() - fileStartTime; + const metrics = this.metricsCalculator.calculateFileMetrics( + commit, + fileChange, + fileContent, + processingTime + ); + + return { metrics, timing: processingTime }; + } catch (error) { + return null; + } + } + + private buildFinalResult( + startTime: number, + timingData: TimingData, + commitsAnalyzed: number, + filesAnalyzed: number, + commitHashes: string[] + ): TrendAnalysisResult { + const totalTime = Date.now() - startTime; + const trendData = this.metricsCalculator.aggregateMetricsIntoTrends( + this.fileMetrics + ); + + const timingMetrics: TimingMetrics = { + totalTime, + gitOperationsTime: timingData.gitOperationsTime, + analysisTime: timingData.analysisTime, + commitListTime: timingData.commitListTime, + fileProcessingTime: timingData.fileProcessingTime, + averageCommitTime: this.metricsCalculator.calculateAverageFromArray( + timingData.commitTimes + ), + averageFileTime: this.metricsCalculator.calculateAverageFromArray( + timingData.fileTimes + ), + }; + + const result: TrendAnalysisResult = { + fileMetrics: trendData, + totalProcessingTimeMs: totalTime, + commitsAnalyzed, + filesAnalyzed, + commitHashes, + timingMetrics, + }; + + this.progressReporter.send( + 'complete', + `Analysis complete: ${commitsAnalyzed} commits, ${this.fileMetrics.size} files (Total: ${totalTime}ms, Git ops: ${timingData.gitOperationsTime}ms, Analysis: ${timingData.analysisTime}ms)`, + 100, + commitsAnalyzed, + commitsAnalyzed, + filesAnalyzed, + undefined, + undefined, + totalTime, + result + ); + + return result; + } +} + +export async function runTrendAnalysis( + projectOptions: Options, + trendOptions: TrendAnalysisOptions = {} +): Promise { + const analyzer = new TrendAnalyzer(projectOptions, trendOptions); + return analyzer.analyzeTrends(); +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/base-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/base-metrics-analyzer.ts new file mode 100644 index 0000000..69b49c2 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/base-metrics-analyzer.ts @@ -0,0 +1,35 @@ +import * as ts from 'typescript'; + +import { AnalyzerContext, type CodeMetrics } from './x-ray-metrics.types'; + +export abstract class BaseMetricsAnalyzer { + protected context: AnalyzerContext; + + constructor(context: AnalyzerContext) { + this.context = context; + } + + protected countLines(node: ts.Node): number { + const start = this.context.sourceFile.getLineAndCharacterOfPosition( + node.getStart() + ); + const end = this.context.sourceFile.getLineAndCharacterOfPosition( + node.getEnd() + ); + return end.line - start.line + 1; + } + + protected getMethodName(node: ts.FunctionLikeDeclaration): string { + if (ts.isFunctionDeclaration(node) && node.name) { + return node.name.text; + } + if (ts.isMethodDeclaration(node) && node.name) { + return node.name.getText(); + } + return 'anonymous'; + } + + // Each analyzer must implement its own analysis routine (always async) + // Returns a partial of CodeMetrics.metrics keyed by the analyzer's domain + abstract analyze(): Promise>; +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/class-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/class-metrics-analyzer.ts new file mode 100644 index 0000000..1a3ae2c --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/class-metrics-analyzer.ts @@ -0,0 +1,151 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { ClassComplexityMetric } from './metrics/class-complexity.metric'; +import { ClassDependenciesMetric } from './metrics/class-dependencies.metric'; +import { DuplicateCodeAnalyzer } from './metrics/duplicate-code-analyzer'; +import { ReasonsToChangeMetric } from './metrics/reasons-to-change.metric'; +import { UnusedMembersMetric } from './metrics/unused-members.metric'; +import { ClassMetrics, AnalyzerContext } from './x-ray-metrics.types'; + +export class ClassMetricsAnalyzer extends BaseMetricsAnalyzer { + private duplicateAnalyzer: DuplicateCodeAnalyzer; + private reasonsMetric: ReasonsToChangeMetric; + private dependencyMetric: ClassDependenciesMetric; + private unusedMetric: UnusedMembersMetric; + private complexityMetric: ClassComplexityMetric; + + constructor(context: AnalyzerContext) { + super(context); + this.duplicateAnalyzer = new DuplicateCodeAnalyzer(); + this.reasonsMetric = new ReasonsToChangeMetric(); + this.dependencyMetric = new ClassDependenciesMetric(); + this.unusedMetric = new UnusedMembersMetric(); + this.complexityMetric = new ClassComplexityMetric(); + } + + static getMeta() { + // Collect metadata from all metric analyzers + const complexityMeta = ClassComplexityMetric.getMetadata(); + const dependencyMeta = ClassDependenciesMetric.getMetadata(); + const duplicateMeta = DuplicateCodeAnalyzer.getMeta(); + const reasonsMeta = ReasonsToChangeMetric.getMetadata(); + const unusedMeta = UnusedMembersMetric.getMetadata(); + + // Compose the schema from all metrics + const composedSchema = z.object({ + ...complexityMeta.schema.shape, + ...dependencyMeta.schema.shape, + ...duplicateMeta.schema.shape, + ...reasonsMeta.schema.shape, + ...unusedMeta.schema.shape, + }); + + // Compose UI configuration + const allMetrics = [ + ...complexityMeta.ui.metrics, + ...dependencyMeta.ui.metrics, + ...reasonsMeta.ui.metrics, + ...unusedMeta.ui.metrics, + ]; + + const allSections = [ + ...(reasonsMeta.ui.sections ?? []), + ...(duplicateMeta.ui.sections ?? []), + ]; + + return { + schema: composedSchema, + ui: { + kind: 'tab', + id: 'classes', + title: 'Classes ({{count}})', + icon: 'class', + collection: 'metrics.classLevel', + hideIfEmpty: true, + itemTitle: '{{key}}', + metrics: allMetrics, + badges: complexityMeta.ui.badges, + sections: allSections, + }, + dictionaries: {}, + } as const; + } + + async analyze(): Promise<{ classLevel: Record }> { + const classMetrics: Record = {}; + const classes: ts.ClassDeclaration[] = []; + const collect = (n: ts.Node) => { + if (ts.isClassDeclaration(n)) classes.push(n); + ts.forEachChild(n, collect); + }; + collect(this.context.sourceFile); + + for (const node of classes) { + const className = node.name?.text || 'AnonymousClass'; + classMetrics[className] = await this.analyzeClass(node); + } + + return { classLevel: classMetrics }; + } + + async analyzeClass(node: ts.ClassDeclaration): Promise { + // Collect metrics + const [ + complexity, + dependencyAnalysis, + reasonsMetric, + unusedMembers, + duplicateAnalysis, + ] = await Promise.all([ + this.complexityMetric.analyzeAsync({ ...this.context, scopeNode: node }), + this.dependencyMetric.analyzeAsync({ ...this.context, scopeNode: node }), + this.reasonsMetric.analyzeAsync({ ...this.context, scopeNode: node }), + this.unusedMetric.analyzeAsync({ ...this.context, scopeNode: node }), + this.duplicateAnalyzer.analyze( + this.context.sourceFile.fileName, + this.context.sourceCode + ), + ]); + + const duplicateBlocks = this.duplicateAnalyzer.getDuplicateCountForClass( + duplicateAnalysis.duplicateInfo, + node, + this.context.sourceFile + ); + const duplicateDetails = this.duplicateAnalyzer.getDuplicateDetailsForClass( + duplicateAnalysis.duplicateInfo, + node, + this.context.sourceFile + ); + + // Build the complete metrics object + return { + // Member metrics + methods: complexity.methods, + fields: complexity.fields, + onlyGettersSetters: complexity.onlyGettersSetters, + isGodClass: complexity.isGodClass, + + // Complexity metrics + cyclomaticComplexity: complexity.cyclomaticComplexity, + fileComplexity: complexity.fileComplexity, + + // Dependency metrics + dependencies: dependencyAnalysis.count, + + // Reasons to change metrics + reasonsToChange: reasonsMetric.score, + reasonsCategories: reasonsMetric.reasonsCategories, + externalPackages: reasonsMetric.externalPackages, + layerCrossing: reasonsMetric.layerCrossing, + internalFiles: reasonsMetric.internalFiles, + + // Code quality metrics + unusedMembers: unusedMembers, + duplicateBlocks, + duplicateDetails, + }; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/code-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/code-analyzer.ts new file mode 100644 index 0000000..6df5ebd --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/code-analyzer.ts @@ -0,0 +1,76 @@ +import * as fs from 'fs'; + +import * as ts from 'typescript'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { ClassMetricsAnalyzer } from './class-metrics-analyzer'; +import { DataStructureMetricsAnalyzer } from './data-structure-metrics-analyzer'; +import { MethodMetricsAnalyzer } from './method-metrics-analyzer'; +import { OrganizationMetricsAnalyzer } from './organization-metrics-analyzer'; +import { TypeScriptMetricsAnalyzer } from './typescript-metrics-analyzer'; +import { CodeMetrics, AnalyzerContext } from './x-ray-metrics.types'; +import { + buildXRayUISchemaFromAnalyzers, + buildXRayJSONSchemaFromAnalyzers, +} from './x-ray.schema'; +export class CodeAnalyzer { + private context: AnalyzerContext; + private analyzers: BaseMetricsAnalyzer[] = []; + + static buildUISchema() { + return buildXRayUISchemaFromAnalyzers(); + } + static buildJSONSchema() { + return buildXRayJSONSchemaFromAnalyzers(); + } + + constructor(filePath: string) { + const sourceCode = fs.readFileSync(filePath, 'utf8'); + + const program = ts.createProgram([filePath], { + target: ts.ScriptTarget.Latest, + module: ts.ModuleKind.CommonJS, + strict: true, + allowJs: true, + }); + + const sourceFile = program.getSourceFile(filePath); + if (!sourceFile) { + throw new Error(`Unable to load source file: ${filePath}`); + } + const checker = program.getTypeChecker(); + + this.context = { + sourceFile, + checker, + program, + sourceCode, + }; + + const ctors: Array BaseMetricsAnalyzer> = + [ + MethodMetricsAnalyzer, + ClassMetricsAnalyzer, + DataStructureMetricsAnalyzer, + OrganizationMetricsAnalyzer, + TypeScriptMetricsAnalyzer, + ]; + this.analyzers = ctors.map((Ctor) => new Ctor(this.context)); + } + + async analyze(includeSource = false): Promise { + const parts = await Promise.all(this.analyzers.map((a) => a.analyze())); + const metrics = Object.assign({}, ...parts) as CodeMetrics['metrics']; + + const result: CodeMetrics = { + file: this.context.sourceFile.fileName, + metrics, + }; + + if (includeSource) { + result.sourceCode = this.context.sourceCode; + } + + return result; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/data-structure-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/data-structure-metrics-analyzer.ts new file mode 100644 index 0000000..6ac1807 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/data-structure-metrics-analyzer.ts @@ -0,0 +1,65 @@ +import { z } from 'zod'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { ArrayMixedMeaningsMetric } from './metrics/data-structure/array-mixed-meanings.metric'; +import { ComplexDataPassingMetric } from './metrics/data-structure/complex-data-passing.metric'; +import { MagicNumbersMetric } from './metrics/data-structure/magic-numbers.metric'; +import { NullChecksMetric } from './metrics/data-structure/null-checks.metric'; +import { PublicFieldsMetric } from './metrics/data-structure/public-fields.metric'; +import { + Dictionaries, + QualityGroupFragment, + MetricItem, +} from './ui-schema.types'; +import { DataStructureMetrics } from './x-ray-metrics.types'; + +export class DataStructureMetricsAnalyzer extends BaseMetricsAnalyzer { + static getMeta() { + const metaParts = [ + PublicFieldsMetric.getMetadata(), + MagicNumbersMetric.getMetadata(), + NullChecksMetric.getMetadata(), + ComplexDataPassingMetric.getMetadata(), + ArrayMixedMeaningsMetric.getMetadata(), + ]; + const schema = metaParts.reduce( + (acc, m) => acc.merge(m.schema), + z.object({}) + ); + const metrics: MetricItem[] = metaParts.flatMap( + (m) => m.ui.metrics as unknown as MetricItem[] + ); + return { + schema, + ui: { + kind: 'quality-group', + id: 'data-structure', + title: 'Data Structure', + path: 'metrics.dataStructure', + refLink: 'https://refactoring.guru/smells/data-clumps', + metrics, + } as QualityGroupFragment, + dictionaries: {} as Dictionaries, + } as const; + } + async analyze(): Promise<{ dataStructure: DataStructureMetrics }> { + const publicFields = new PublicFieldsMetric().analyze(this.context); + const magicNumbers = new MagicNumbersMetric().analyze(this.context); + const nullChecks = new NullChecksMetric().analyze(this.context); + const complexDataPassing = new ComplexDataPassingMetric().analyze( + this.context + ); + const arrayMixedMeanings = new ArrayMixedMeaningsMetric().analyze( + this.context + ); + return Promise.resolve({ + dataStructure: { + publicFields, + magicNumbers, + nullChecks, + complexDataPassing, + arrayMixedMeanings, + }, + }); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/method-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/method-metrics-analyzer.ts new file mode 100644 index 0000000..1758b4e --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/method-metrics-analyzer.ts @@ -0,0 +1,179 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { calcComplexityForNode } from '../../../utils/complexity'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { MethodResponsibilityMetric } from './metrics/method-responsibility-metric'; +import { NestedConditionsMetric } from './metrics/nested-conditions-metric'; +import { Dictionaries, TabFragment } from './ui-schema.types'; +import { MethodMetrics } from './x-ray-metrics.types'; + +export class MethodMetricsAnalyzer extends BaseMetricsAnalyzer { + static getMeta() { + const responsibilityMeta = MethodResponsibilityMetric.getMeta(); + const nestingMeta = NestedConditionsMetric.getMeta(); + + return { + schema: z + .object({ + lines: z.number().describe('Lines of code in the function/method.'), + parameters: z.number().describe('Number of parameters.'), + cyclomaticComplexity: z + .number() + .describe('Independent paths through the method.'), + hasComments: z + .boolean() + .describe('Whether the method has leading comments.'), + className: z.string().optional().describe('Owning class, if any.'), + responsibilitiesCategories: z + .array(z.string()) + .optional() + .describe('Categories detected for responsibilities.'), + }) + .merge(nestingMeta.schema) + .merge(responsibilityMeta.schema), + ui: { + kind: 'tab', + id: 'methods', + title: 'Methods ({{count}})', + icon: 'functions', + collection: 'metrics.methodLevel', + hideIfEmpty: true, + itemTitle: '{{key}}', + badges: [{ label: '{{value.className}}', if: 'value.className' }], + metrics: [ + { + path: 'lines', + label: 'Lines', + icon: 'format_list_numbered', + tooltip: 'Prefer small, cohesive methods.', + threshold: { warnGte: 30 }, + refLink: 'https://en.wikipedia.org/wiki/Source_lines_of_code', + }, + { + path: 'parameters', + label: 'Parameters', + icon: 'input', + tooltip: 'Consider DTO if many parameters.', + threshold: { warnGte: 4 }, + refLink: 'https://martinfowler.com/bliki/FunctionLength.html', + }, + { + path: 'cyclomaticComplexity', + label: 'Complexity', + icon: 'account_tree', + tooltip: 'Lower is better.', + threshold: { warnGte: 12 }, + refLink: 'https://en.wikipedia.org/wiki/Cyclomatic_complexity', + }, + ...nestingMeta.ui.metrics, + ...responsibilityMeta.ui.metrics, + ], + } as TabFragment, + dictionaries: { + categoryDescriptions: { + networking: 'Interacts with remote services.', + storage: 'Uses storage/persistence.', + dom: 'Manipulates DOM/globals.', + logging: 'Performs logging.', + internal: 'Uses internal modules/types.', + navigation: 'Router/navigation interactions.', + state: 'State management.', + 'state-effects': 'Reactive side-effects layer.', + forms: 'Angular forms.', + reactive: 'RxJS/observables.', + ui: 'UI libraries/components.', + 'change-detection': 'ChangeDetectorRef usage.', + zone: 'NgZone usage.', + 'angular-core': 'Angular core baseline usage (neutral).', + }, + categoryIcons: { + networking: 'cloud', + storage: 'save', + dom: 'web', + logging: 'bug_report', + internal: 'home_repair_service', + navigation: 'route', + state: 'hub', + 'state-effects': 'bolt', + forms: 'assignment', + reactive: 'sync', + ui: 'palette', + 'change-detection': 'refresh', + zone: 'donut_large', + 'angular-core': 'angular', + }, + } as Dictionaries, + } as const; + } + + async analyze(): Promise<{ methodLevel: Record }> { + const methodLevel = this.collectMethodMetrics(); + return Promise.resolve({ methodLevel }); + } + + private collectMethodMetrics(): Record { + const methodMetrics: Record = {}; + + const visitNode = (node: ts.Node, currentClassName?: string) => { + if (ts.isClassDeclaration(node)) { + const className = node.name?.text || 'AnonymousClass'; + ts.forEachChild(node, (child) => visitNode(child, className)); + return; + } + + if ( + ts.isFunctionDeclaration(node) || + ts.isMethodDeclaration(node) || + ts.isArrowFunction(node) || + ts.isFunctionExpression(node) + ) { + const methodName = this.getMethodName( + node as ts.FunctionLikeDeclaration + ); + const metrics = this.analyzeMethod(node as ts.FunctionLikeDeclaration); + metrics.className = currentClassName; + + const methodKey = currentClassName + ? `${currentClassName}.${methodName}` + : methodName; + methodMetrics[methodKey] = metrics; + return; + } + + ts.forEachChild(node, (child) => visitNode(child, currentClassName)); + }; + + visitNode(this.context.sourceFile); + return methodMetrics; + } + + analyzeMethod(node: ts.FunctionLikeDeclaration): MethodMetrics { + const responsibilityMetric = new MethodResponsibilityMetric(); + const nestedConditionsMetric = new NestedConditionsMetric(); + + const { responsibilities, categories, writesThis, mutatesParams } = + responsibilityMetric.analyze(this.context, node); + return { + lines: this.countLines(node), + parameters: node.parameters.length, + cyclomaticComplexity: calcComplexityForNode(node), + nestedConditions: nestedConditionsMetric.analyze(node), + hasComments: this.hasComments(node), + responsibilities, + className: undefined, // Will be set by analyze() method + responsibilitiesCategories: Array.from(categories), + writesThis, + mutatesParams, + }; + } + + private hasComments(node: ts.Node): boolean { + const commentRanges = ts.getLeadingCommentRanges( + this.context.sourceFile.text, + node.pos + ); + return commentRanges && commentRanges.length > 0; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.spec.ts new file mode 100644 index 0000000..3f41e82 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.spec.ts @@ -0,0 +1,90 @@ +import * as ts from 'typescript'; + +import { ClassComplexityMetric } from './class-complexity.metric'; + +describe('ClassComplexityMetric', () => { + function parseClass(code: string): { + classNode: ts.ClassDeclaration; + sourceFile: ts.SourceFile; + } { + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + let classNode: ts.ClassDeclaration | undefined; + + ts.forEachChild(sourceFile, (node) => { + if (ts.isClassDeclaration(node)) { + classNode = node; + } + }); + + if (!classNode) throw new Error('No class found'); + return { classNode, sourceFile }; + } + + describe('calculateClassComplexity', () => { + it('should calculate higher complexity for branching code', () => { + const code = ` +class TestClass { + method() { + if (true) return 1; + for (let i = 0; i < 10; i++) { + console.log(i); + } + } +}`; + const { classNode, sourceFile } = parseClass(code); + const metric = new ClassComplexityMetric(); + const result = metric.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.cyclomaticComplexity).toBeGreaterThan(1); + expect(result.fileComplexity).toBeGreaterThan(0); + }); + + it('should handle arrow function properties', () => { + const code = ` +class TestClass { + handleClick = () => { + if (this.isValid()) this.process(); + } +}`; + const { classNode, sourceFile } = parseClass(code); + const metric = new ClassComplexityMetric(); + const result = metric.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.cyclomaticComplexity).toBeGreaterThan(0); + expect(result.fileComplexity).toBeDefined(); + }); + + it('should return baseline complexity for empty class', () => { + const code = `class EmptyClass {}`; + const { classNode, sourceFile } = parseClass(code); + const metric = new ClassComplexityMetric(); + const result = metric.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.cyclomaticComplexity).toBe(1); + expect(result.fileComplexity).toBe(1); + }); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.ts new file mode 100644 index 0000000..5aa517e --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-complexity.metric.ts @@ -0,0 +1,195 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { calcComplexityForNode } from '../../../../utils/complexity'; +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { BaseMetric } from './metric.base'; +import { Metric } from './metric.decorator'; + +export interface ClassComplexityMetricResult { + methods: number; + fields: number; + onlyGettersSetters: boolean; + isGodClass: boolean; + cyclomaticComplexity: number; + fileComplexity: number; +} + +@Metric({ + schema: z.object({ + methods: z.number().describe('Number of methods and accessors.'), + fields: z.number().describe('Number of field/property declarations.'), + cyclomaticComplexity: z + .number() + .describe( + 'Cyclomatic complexity of the class (sum of branching constructs inside the class body).' + ), + fileComplexity: z + .number() + .describe('Cyclomatic complexity of the entire file.'), + isGodClass: z.boolean().describe('True if methods > 30 or fields > 20.'), + onlyGettersSetters: z + .boolean() + .describe('True if all methods are accessors.'), + }), + ui: { + metrics: [ + { + path: 'methods', + label: 'Methods', + icon: 'functions', + refLink: 'https://refactoring.guru/smells/large-class', + }, + { + path: 'fields', + label: 'Fields', + icon: 'storage', + refLink: 'https://refactoring.guru/smells/large-class', + }, + { + path: 'cyclomaticComplexity', + label: 'Class Complexity', + icon: 'account_tree', + threshold: { warnGte: 25 }, + tooltip: 'Aggregate branching complexity within the class.', + refLink: 'https://en.wikipedia.org/wiki/Cyclomatic_complexity', + }, + { + path: 'fileComplexity', + label: 'File Complexity', + icon: 'schema', + threshold: { warnGte: 30 }, + tooltip: 'Cyclomatic complexity measured at file scope.', + refLink: 'https://en.wikipedia.org/wiki/Cyclomatic_complexity', + }, + ], + badges: [ + { label: 'God Class', class: 'warning', if: 'value.isGodClass' }, + { label: 'Data Class', class: 'info', if: 'value.onlyGettersSetters' }, + ], + }, +}) +export class ClassComplexityMetric extends BaseMetric { + analyze(context: AnalyzerContext): ClassComplexityMetricResult { + const classNode = + context.scopeNode && ts.isClassDeclaration(context.scopeNode) + ? context.scopeNode + : undefined; + + const { methods, fields, onlyGettersSetters } = classNode + ? this.analyzeClassMembers(classNode) + : { methods: 0, fields: 0, onlyGettersSetters: false }; + + const cyclomaticComplexity = classNode + ? this.calculateClassComplexity(classNode, context.sourceFile) + : calcComplexityForNode(context.sourceFile); + + const fileComplexity = calcComplexityForNode(context.sourceFile); + + const isGodClass = methods > 30 || fields > 20; + + return { + methods, + fields, + onlyGettersSetters, + isGodClass, + cyclomaticComplexity, + fileComplexity, + }; + } + + private calculateClassComplexity( + node: ts.ClassDeclaration, + sourceFile: ts.SourceFile + ): number { + const functionLikeNodes = this.collectFunctionLikeNodes(node); + + let classComplexity: number; + if (functionLikeNodes.length > 0) { + const total = functionLikeNodes.reduce( + (sum, fn) => sum + calcComplexityForNode(fn), + 0 + ); + classComplexity = Math.max(1, total - (functionLikeNodes.length - 1)); + } else { + classComplexity = calcComplexityForNode(node); + } + + const fileComplexity = calcComplexityForNode(sourceFile); + if (this.isOnlyClassInFile(sourceFile)) { + classComplexity = fileComplexity; + } + return classComplexity; + } + + private collectFunctionLikeNodes( + node: ts.ClassDeclaration + ): ts.FunctionLikeDeclaration[] { + const functionLikeNodes: ts.FunctionLikeDeclaration[] = []; + node.members.forEach((member) => { + if ( + ts.isMethodDeclaration(member) || + ts.isGetAccessorDeclaration(member) || + ts.isSetAccessorDeclaration(member) + ) { + functionLikeNodes.push(member); + } else if (ts.isPropertyDeclaration(member)) { + const init = member.initializer; + if ( + init && + (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) + ) { + functionLikeNodes.push(init); + } + } + }); + return functionLikeNodes; + } + + private isOnlyClassInFile(sourceFile: ts.SourceFile): boolean { + let classCount = 0; + const visit = (node: ts.Node) => { + if (ts.isClassDeclaration(node)) { + classCount++; + } + ts.forEachChild(node, visit); + }; + visit(sourceFile); + return classCount === 1; + } + + private analyzeClassMembers(node: ts.ClassDeclaration): { + methods: number; + fields: number; + onlyGettersSetters: boolean; + } { + let methods = 0; + let fields = 0; + let onlyGettersSetters = true; + + node.members.forEach((member) => { + if ( + ts.isMethodDeclaration(member) || + ts.isGetAccessorDeclaration(member) || + ts.isSetAccessorDeclaration(member) + ) { + methods++; + if ( + ts.isMethodDeclaration(member) && + !this.isGetterOrSetter(member.name?.getText() || '') + ) { + onlyGettersSetters = false; + } + } else if (ts.isPropertyDeclaration(member)) { + fields++; + } + }); + + return { methods, fields, onlyGettersSetters }; + } + + private isGetterOrSetter(name: string): boolean { + return name.startsWith('get') || name.startsWith('set'); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.spec.ts new file mode 100644 index 0000000..730ea17 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.spec.ts @@ -0,0 +1,99 @@ +import * as ts from 'typescript'; + +import { ClassDependenciesMetric } from './class-dependencies.metric'; +import { parseClass } from './test-utils'; + +describe('ClassDependenciesMetric', () => { + describe('analyze', () => { + it('should collect dependencies from constructor', () => { + const code = ` +class TestClass { + constructor( + private userService: UserService, + private logger: Logger + ) {} +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new ClassDependenciesMetric(); + const result = analyzer.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.count).toBe(2); + expect(result.types.has('UserService')).toBe(true); + expect(result.types.has('Logger')).toBe(true); + }); + + it('should collect service-like properties', () => { + const code = ` +class TestClass { + private readonly dataService: DataService; + private userRepository: UserRepository; +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new ClassDependenciesMetric(); + const result = analyzer.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.count).toBe(2); + expect(result.types.has('DataService')).toBe(true); + expect(result.types.has('UserRepository')).toBe(true); + }); + + it('should return empty for class without dependencies', () => { + const code = ` +class EmptyClass { + private name: string; + private count: number; +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new ClassDependenciesMetric(); + const result = analyzer.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.count).toBe(0); + expect(result.types.size).toBe(0); + }); + + it('should collect dependencies from inject() calls', () => { + const code = ` +class TestClass { + private http = inject(HttpClient); + private userService = inject(UserService); + statusStore = inject(StatusStore); +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new ClassDependenciesMetric(); + const result = analyzer.analyze({ + sourceFile, + program: ts.createProgram([], {}), + checker: ts.createProgram([], {}).getTypeChecker(), + sourceCode: code, + scopeNode: classNode, + }); + + expect(result.count).toBe(3); + expect(result.types.has('HttpClient')).toBe(true); + expect(result.types.has('UserService')).toBe(true); + expect(result.types.has('StatusStore')).toBe(true); + }); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.ts new file mode 100644 index 0000000..7c2567c --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/class-dependencies.metric.ts @@ -0,0 +1,142 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { BaseMetric } from './metric.base'; +import { Metric } from './metric.decorator'; + +export interface ClassDependenciesMetricResult { + count: number; + types: Set; +} + +@Metric({ + schema: z.object({ + dependencies: z + .number() + .describe('Unique dependency types in constructor/fields.'), + }), + ui: { + metrics: [ + { + path: 'dependencies', + label: 'Dependencies', + icon: 'link', + threshold: { warnGte: 6 }, + tooltip: 'High count can signal tight coupling.', + refLink: 'https://martinfowler.com/articles/dipInTheWild.html', + }, + ], + }, +}) +export class ClassDependenciesMetric extends BaseMetric { + analyze(context: AnalyzerContext): ClassDependenciesMetricResult { + const classNode = + context.scopeNode && ts.isClassDeclaration(context.scopeNode) + ? context.scopeNode + : undefined; + if (!classNode) { + return { count: 0, types: new Set() }; + } + + const types = this.collectDependencies(classNode); + return { count: types.size, types }; + } + + private collectDependencies(node: ts.ClassDeclaration): Set { + const dependencies = new Set(); + node.members.forEach((member) => { + if (ts.isConstructorDeclaration(member)) { + this.collectConstructorDependencies(member, dependencies); + } else if (ts.isPropertyDeclaration(member)) { + this.collectPropertyDependencies(member, dependencies); + } + }); + return dependencies; + } + + private collectConstructorDependencies( + constructor: ts.ConstructorDeclaration, + dependencies: Set + ): void { + constructor.parameters.forEach((param) => { + if (param.type) { + if ( + ts.isTypeReferenceNode(param.type) && + ts.isIdentifier(param.type.typeName) + ) { + dependencies.add(param.type.typeName.text); + } + } + }); + } + + private collectPropertyDependencies( + property: ts.PropertyDeclaration, + dependencies: Set + ): void { + if (property.initializer && ts.isCallExpression(property.initializer)) { + const injectDependency = this.extractInjectDependency( + property.initializer + ); + if (injectDependency) { + dependencies.add(injectDependency); + return; + } + } + + if (!property.type) return; + const hasReadonlyModifier = property.modifiers?.some( + (mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword + ); + if (hasReadonlyModifier || this.looksLikeService(property)) { + if ( + ts.isTypeReferenceNode(property.type) && + ts.isIdentifier(property.type.typeName) + ) { + dependencies.add(property.type.typeName.text); + } + } + } + + private extractInjectDependency( + callExpression: ts.CallExpression + ): string | null { + if ( + ts.isIdentifier(callExpression.expression) && + callExpression.expression.text === 'inject' + ) { + const firstArg = callExpression.arguments[0]; + if (firstArg && ts.isIdentifier(firstArg)) { + return firstArg.text; + } + } + return null; + } + + private looksLikeService(property: ts.PropertyDeclaration): boolean { + if (!property.type) return false; + let typeText = ''; + if ( + ts.isTypeReferenceNode(property.type) && + ts.isIdentifier(property.type.typeName) + ) { + typeText = property.type.typeName.text; + } else { + return false; + } + const servicePatterns = [ + 'Service', + 'Repository', + 'Store', + 'Client', + 'Manager', + 'Provider', + 'Factory', + 'Helper', + 'Utility', + ]; + return servicePatterns.some((pattern) => typeText.includes(pattern)); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.spec.ts new file mode 100644 index 0000000..b45b6b3 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.spec.ts @@ -0,0 +1,46 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { ArrayMixedMeaningsMetric } from './array-mixed-meanings.metric'; + +describe('ArrayMixedMeaningsMetric', () => { + it('counts arrays mixing heterogeneous element kinds', () => { + const code = ` + const a = [1, { x: 1 }, true]; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ArrayMixedMeaningsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); + + it('does not count homogeneous arrays', () => { + const code = ` + type Option = { + id: string; + label: string; + }; + const metricOptions: Option[] = [ + { id: 'Length', label: 'File Length' }, + { id: 'McCabe', label: 'Cyclomatic Complexity' }, + ]; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ArrayMixedMeaningsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.ts new file mode 100644 index 0000000..0dbb683 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/array-mixed-meanings.metric.ts @@ -0,0 +1,62 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + arrayMixedMeanings: z + .number() + .describe( + 'Counts arrays that mix heterogeneous element kinds (objects, scalars, booleans, etc.). High values suggest the array is overloaded with multiple responsibilities; consider typed objects, records, or tuples with explicit structure.' + ), + }), + ui: { + metrics: [ + { + path: 'arrayMixedMeanings', + label: 'Array Mixed Meanings', + icon: 'view_array', + threshold: { warnGte: 1 }, + tooltip: + 'Arrays containing mixed element kinds (e.g., numbers + objects) often hide multiple responsibilities. Prefer a structured type (object/record) or a tuple.', + refLink: 'https://refactoring.guru/smells/primitive-obsession', + }, + ], + }, +}) +export class ArrayMixedMeaningsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + + const categorize = (expr: ts.Expression): string => { + if (ts.isArrayLiteralExpression(expr)) return 'array'; + if (ts.isObjectLiteralExpression(expr)) return 'object'; + if (ts.isLiteralExpression(expr)) return 'literal'; + switch (expr.kind) { + case ts.SyntaxKind.TrueKeyword: + case ts.SyntaxKind.FalseKeyword: + case ts.SyntaxKind.NullKeyword: + return 'literal'; + default: + break; + } + if (ts.isIdentifier(expr) && expr.text === 'undefined') return 'literal'; + return 'other'; + }; + + const walk = (node: ts.Node) => { + if (ts.isArrayLiteralExpression(node)) { + const categories = new Set(); + node.elements.forEach((el) => categories.add(categorize(el))); + if (categories.size >= 2) count++; + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.spec.ts new file mode 100644 index 0000000..c61830d --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.spec.ts @@ -0,0 +1,63 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { ComplexDataPassingMetric } from './complex-data-passing.metric'; + +describe('ComplexDataPassingMetric', () => { + it('counts large object literals passed to calls/constructors', () => { + const code = ` + function f() {} + f({ a: 1, b: 2, c: 3, d: 4 }); + new Date({ a: 1, b: 2, c: 3, d: 4 }); + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexDataPassingMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('counts large destructures and inline object types in params', () => { + const code = ` + function g({a,b,c,d}) {} + function h(p: {a: number, b: number, c: number, d: number}) {} + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexDataPassingMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('ignores decorator arguments', () => { + const code = ` + function Dec(): any { return () => {} } + @Dec() + class C { + @Dec() + method() {} + } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexDataPassingMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.ts new file mode 100644 index 0000000..cd59af3 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/complex-data-passing.metric.ts @@ -0,0 +1,86 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + complexDataPassing: z + .number() + .describe( + 'Counts "fat" data passed through APIs (large object literals, large destructures, or large inline object types). High values indicate potential Data Clumps; consider introducing value objects/DTOs or narrowing parameters.' + ), + }), + ui: { + metrics: [ + { + path: 'complexDataPassing', + label: 'Complex Data Passing', + icon: 'group_work', + threshold: { warnGte: 1 }, + tooltip: + 'Detects large object literals/destructures in calls/signatures. Consider extracting value objects or reducing parameter surface.', + refLink: 'https://refactoring.guru/smells/data-clumps', + }, + ], + }, +}) +export class ComplexDataPassingMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const LARGE_THRESHOLD = 4; + const isLargeObjectLiteral = (expr: ts.Expression): boolean => + ts.isObjectLiteralExpression(expr) && + expr.properties.length >= LARGE_THRESHOLD; + const isInsideDecorator = (n: ts.Node): boolean => { + let cur: ts.Node | undefined = n; + while (cur) { + if (ts.isDecorator(cur)) return true; + cur = cur.parent; + } + return false; + }; + const isLargeDestructure = (param: ts.ParameterDeclaration): boolean => + ts.isObjectBindingPattern(param.name) && + param.name.elements.length >= LARGE_THRESHOLD; + const isLargeInlineObjType = (param: ts.ParameterDeclaration): boolean => + !!param.type && + ts.isTypeLiteralNode(param.type) && + param.type.members.filter((m) => ts.isPropertySignature(m)).length >= + LARGE_THRESHOLD; + + const walk = (node: ts.Node) => { + if (ts.isCallExpression(node)) { + if (!isInsideDecorator(node)) { + node.arguments?.forEach((arg) => { + if (isLargeObjectLiteral(arg)) count++; + }); + } + } else if (ts.isNewExpression(node)) { + if (!isInsideDecorator(node)) { + node.arguments?.forEach((arg) => { + if (isLargeObjectLiteral(arg)) count++; + }); + } + } + + if ( + ts.isFunctionDeclaration(node) || + ts.isMethodDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isArrowFunction(node) || + ts.isConstructorDeclaration(node) + ) { + node.parameters.forEach((param) => { + if (isLargeDestructure(param) || isLargeInlineObjType(param)) count++; + }); + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.spec.ts new file mode 100644 index 0000000..394786c --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.spec.ts @@ -0,0 +1,39 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { MagicNumbersMetric } from './magic-numbers.metric'; + +describe('MagicNumbersMetric', () => { + it('counts numeric literals not in the allowed set', () => { + const code = ` + const a = 3, b = 5, c = -1, d = 0, e = 2; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MagicNumbersMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('does not count allowed literals (-1, 0, 1, 2)', () => { + const code = ` + const a = -1, b = 0, c = 1, d = 2; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MagicNumbersMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.ts new file mode 100644 index 0000000..3c42d15 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/magic-numbers.metric.ts @@ -0,0 +1,41 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + magicNumbers: z + .number() + .describe('Numeric literals other than -1, 0, 1, 2.'), + }), + ui: { + metrics: [ + { + path: 'magicNumbers', + label: 'Magic Numbers', + icon: 'numbers', + threshold: { warnGte: 1 }, + tooltip: 'Use named constants.', + refLink: 'https://en.wikipedia.org/wiki/Magic_number_(programming)', + }, + ], + }, +}) +export class MagicNumbersMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const allowed: ReadonlySet = new Set([-1, 0, 1, 2]); + const walk = (node: ts.Node) => { + if (ts.isNumericLiteral(node)) { + const value = parseFloat(node.text); + if (!allowed.has(value)) count++; + } + ts.forEachChild(node, walk); + }; + walk(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.spec.ts new file mode 100644 index 0000000..f1a41ee --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.spec.ts @@ -0,0 +1,42 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { NullChecksMetric } from './null-checks.metric'; + +describe('NullChecksMetric', () => { + it('counts non-strict equality checks against null/undefined', () => { + const code = ` + if (x == null) {} + if (y != undefined) {} + if (z === null) {} + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new NullChecksMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('ignores strict nullish comparisons', () => { + const code = ` + if (a === null) {} + if (b !== undefined) {} + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new NullChecksMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.ts new file mode 100644 index 0000000..77dd06a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/null-checks.metric.ts @@ -0,0 +1,46 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + nullChecks: z + .number() + .describe('Non-strict equality checks against null/undefined.'), + }), + ui: { + metrics: [ + { + path: 'nullChecks', + label: 'Null Checks', + icon: 'not_equal', + threshold: { warnGte: 1 }, + tooltip: + 'Non-strict (==/!=) null checks are error-prone. Prefer ===/!== or explicit nullish handling.', + refLink: + 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Equality', + }, + ], + }, +}) +export class NullChecksMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const isNullish = (s: string): boolean => s === 'null' || s === 'undefined'; + const walk = (node: ts.Node) => { + if (ts.isBinaryExpression(node)) { + const left = node.left.getText(); + const operator = node.operatorToken.getText(); + const right = node.right.getText(); + const isNonStrictEq = operator === '==' || operator === '!='; + if (isNonStrictEq && (isNullish(left) || isNullish(right))) count++; + } + ts.forEachChild(node, walk); + }; + walk(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.spec.ts new file mode 100644 index 0000000..6724d80 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.spec.ts @@ -0,0 +1,53 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { PublicFieldsMetric } from './public-fields.metric'; + +describe('PublicFieldsMetric', () => { + it('counts public fields excluding Angular signals', () => { + const code = ` + import { signal, computed } from '@angular/core'; + class C { + x = 1; // public + y: string; // public + a = signal(1); // Angular signal initializer + b: Signal; + c: WritableSignal; + d: ReadonlySignal; + e: ComputedSignal; + constructor() { this.y = 'ok'; this.b = signal(2) as any; } + } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new PublicFieldsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('does not count private/protected fields', () => { + const code = ` + class C { + private x = 1; + protected y = 2; + public z = 3; + } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new PublicFieldsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.ts new file mode 100644 index 0000000..5c9506c --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/data-structure/public-fields.metric.ts @@ -0,0 +1,100 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + publicFields: z + .number() + .describe('Count of public fields (excluding Angular signals).'), + }), + ui: { + metrics: [ + { + path: 'publicFields', + label: 'Public Fields', + icon: 'visibility', + threshold: { warnGte: 1 }, + tooltip: 'Breaks encapsulation. Excludes Angular signals.', + refLink: 'https://refactoring.guru/smells/data-class', + }, + ], + }, +}) +export class PublicFieldsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + + const isPublic = (node: ts.PropertyDeclaration): boolean => { + return ( + !node.modifiers || + node.modifiers.some( + (mod) => mod.kind === ts.SyntaxKind.PublicKeyword + ) || + !node.modifiers.some( + (mod) => + mod.kind === ts.SyntaxKind.PrivateKeyword || + mod.kind === ts.SyntaxKind.ProtectedKeyword + ) + ); + }; + + const isAngularSignalType = (t: ts.TypeNode | undefined): boolean => { + if (!t) return false; + if (ts.isTypeReferenceNode(t)) { + const getName = (tn: ts.EntityName): string => + ts.isIdentifier(tn) ? tn.text : tn.right.text; + const name = getName(t.typeName); + if ( + name === 'Signal' || + name === 'WritableSignal' || + name === 'ComputedSignal' || + name === 'ReadonlySignal' + ) + return true; + if (name.endsWith('Signal')) return true; + } + return false; + }; + + const isAngularSignalInitializer = ( + init: ts.Expression | undefined + ): boolean => { + if (!init) return false; + if (ts.isCallExpression(init)) { + const callee = init.expression; + const getId = (expr: ts.Expression): string | undefined => { + if (ts.isIdentifier(expr)) return expr.text; + if (ts.isPropertyAccessExpression(expr)) return expr.name.text; + return undefined; + }; + const name = getId(callee); + if (!name) return false; + return name === 'signal' || name === 'computed' || name === 'effect'; + } + return false; + }; + + const walk = (node: ts.Node) => { + if (ts.isClassDeclaration(node)) { + node.members.forEach((member) => { + if ( + ts.isPropertyDeclaration(member) && + isPublic(member) && + !isAngularSignalType(member.type) && + !isAngularSignalInitializer(member.initializer) + ) { + count++; + } + }); + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.spec.ts new file mode 100644 index 0000000..95efa11 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.spec.ts @@ -0,0 +1,58 @@ +import { DependencyMetric } from './dependency.metric'; +import { parseClass, createContext } from './test-utils'; + +describe('DependencyMetric', () => { + describe('analyze', () => { + it('should collect dependencies from constructor', () => { + const code = ` +class TestClass { + constructor( + private userService: UserService, + private logger: Logger + ) {} +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new DependencyMetric(); + const ctx = createContext(sourceFile); + const result = analyzer.analyze({ ...ctx, scopeNode: classNode }); + + expect(result.count).toBe(2); + expect(result.types.has('UserService')).toBe(true); + expect(result.types.has('Logger')).toBe(true); + }); + + it('should collect service-like properties', () => { + const code = ` +class TestClass { + private readonly dataService: DataService; + private userRepository: UserRepository; +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new DependencyMetric(); + const ctx = createContext(sourceFile); + const result = analyzer.analyze({ ...ctx, scopeNode: classNode }); + + expect(result.count).toBe(2); + expect(result.types.has('DataService')).toBe(true); + expect(result.types.has('UserRepository')).toBe(true); + }); + + it('should return empty for class without dependencies', () => { + const code = ` +class EmptyClass { + private name: string; + private count: number; +} +`; + const { classNode, sourceFile } = parseClass(code); + const analyzer = new DependencyMetric(); + const ctx = createContext(sourceFile); + const result = analyzer.analyze({ ...ctx, scopeNode: classNode }); + + expect(result.count).toBe(0); + expect(result.types.size).toBe(0); + }); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.ts new file mode 100644 index 0000000..2351564 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/dependency.metric.ts @@ -0,0 +1,117 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { BaseMetric } from './metric.base'; +import { Metric } from './metric.decorator'; + +export interface DependencyMetricResult { + count: number; + types: Set; +} + +@Metric({ + schema: z.object({ + dependencies: z + .number() + .describe('Unique dependency types in constructor/fields.'), + }), + ui: { + metrics: [ + { + path: 'dependencies', + label: 'Dependencies', + icon: 'link', + threshold: { warnGte: 6 }, + tooltip: 'High count can signal tight coupling.', + refLink: 'https://martinfowler.com/articles/dipInTheWild.html', + }, + ], + }, +}) +export class DependencyMetric extends BaseMetric { + analyze(context: AnalyzerContext): DependencyMetricResult { + const classNode = + context.scopeNode && ts.isClassDeclaration(context.scopeNode) + ? context.scopeNode + : undefined; + if (!classNode) { + return { count: 0, types: new Set() }; + } + + const types = this.collectDependencies(classNode); + return { count: types.size, types }; + } + + private collectDependencies(node: ts.ClassDeclaration): Set { + const dependencies = new Set(); + node.members.forEach((member) => { + if (ts.isConstructorDeclaration(member)) { + this.collectConstructorDependencies(member, dependencies); + } else if (ts.isPropertyDeclaration(member)) { + this.collectPropertyDependencies(member, dependencies); + } + }); + return dependencies; + } + + private collectConstructorDependencies( + constructor: ts.ConstructorDeclaration, + dependencies: Set + ): void { + constructor.parameters.forEach((param) => { + if (param.type) { + if ( + ts.isTypeReferenceNode(param.type) && + ts.isIdentifier(param.type.typeName) + ) { + dependencies.add(param.type.typeName.text); + } + } + }); + } + + private collectPropertyDependencies( + property: ts.PropertyDeclaration, + dependencies: Set + ): void { + if (!property.type) return; + const hasReadonlyModifier = property.modifiers?.some( + (mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword + ); + if (hasReadonlyModifier || this.looksLikeService(property)) { + if ( + ts.isTypeReferenceNode(property.type) && + ts.isIdentifier(property.type.typeName) + ) { + dependencies.add(property.type.typeName.text); + } + } + } + + private looksLikeService(property: ts.PropertyDeclaration): boolean { + if (!property.type) return false; + let typeText = ''; + if ( + ts.isTypeReferenceNode(property.type) && + ts.isIdentifier(property.type.typeName) + ) { + typeText = property.type.typeName.text; + } else { + return false; + } + const servicePatterns = [ + 'Service', + 'Repository', + 'Store', + 'Client', + 'Manager', + 'Provider', + 'Factory', + 'Helper', + 'Utility', + ]; + return servicePatterns.some((pattern) => typeText.includes(pattern)); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.spec.ts new file mode 100644 index 0000000..39024aa --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.spec.ts @@ -0,0 +1,66 @@ +import { DuplicateCodeAnalyzer } from './duplicate-code-analyzer'; + +describe('DuplicateCodeAnalyzer', () => { + let analyzer: DuplicateCodeAnalyzer; + + beforeEach(() => { + analyzer = new DuplicateCodeAnalyzer(); + }); + + describe('analyze', () => { + it('should analyze code and return duplicate info map', async () => { + const code = ` + function processData(data: any): void { + console.log('Processing data'); + console.log('Step 1'); + console.log('Step 2'); + console.log('Done'); + } + + function handleData(data: any): void { + console.log('Processing data'); + console.log('Step 1'); + console.log('Step 2'); + console.log('Done'); + } + `; + + const result = await analyzer.analyze('test.ts', code); + expect(result.duplicateInfo).toBeDefined(); + expect(result.duplicateInfo instanceof Map).toBe(true); + }); + + it('should classify severity correctly', async () => { + // Create a 13-line duplicate for MEDIUM severity + const lines = Array(13).fill(' console.log("line");').join('\n'); + const code = ` + function method1() { + ${lines} + } + function method2() { + ${lines} + } + `; + + const result = await analyzer.analyze('test.ts', code); + const severities = Array.from(result.duplicateInfo.values()).map( + (d) => d.severity + ); + expect(severities).toContain('MEDIUM'); + }); + + it('should return empty result for unique code', async () => { + const code = ` + function add(a: number, b: number): number { + return a + b; + } + function multiply(x: number, y: number): number { + return x * y; + } + `; + + const result = await analyzer.analyze('test.ts', code); + expect(result.duplicateInfo.size).toBe(0); + }); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.ts new file mode 100644 index 0000000..181773e --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/duplicate-code-analyzer.ts @@ -0,0 +1,199 @@ +import { + Detector, + MemoryStore, + getDefaultOptions, + type IClone, + type IMapFrame, +} from '@jscpd/core'; +import { Tokenizer } from '@jscpd/tokenizer'; +import * as ts from 'typescript'; +import { z } from 'zod'; + +export interface DuplicateDetails { + location: string; + count: number; + severity: 'LOW' | 'MEDIUM' | 'HIGH'; +} + +export interface DuplicateAnalysisResult { + duplicateInfo: Map; +} + +/** + * Analyzes duplicate code blocks within a file. + * Detects code clones and calculates severity based on the size of duplicates. + */ +export class DuplicateCodeAnalyzer { + private static readonly MIN_LINES = 3; + private static readonly SEVERITY_THRESHOLDS = { + HIGH: 30, + MEDIUM: 12, + }; + + static getMeta() { + return { + schema: z.object({ + duplicateBlocks: z + .number() + .describe('Number of duplicate blocks within this class.'), + duplicateDetails: z + .array( + z.object({ + location: z.string(), + count: z.number(), + severity: z.enum(['LOW', 'MEDIUM', 'HIGH']), + }) + ) + .optional(), + }), + ui: { + sections: [ + { + type: 'duplicates', + title: 'Duplicate Code', + path: 'duplicateDetails', + }, + ], + }, + } as const; + } + + async analyze( + fileName: string, + sourceCode: string + ): Promise { + const duplicateInfo = await this.computeDuplicateInfo(fileName, sourceCode); + return { duplicateInfo }; + } + + getDuplicateCountForClass( + duplicateInfo: Map, + classNode: ts.ClassDeclaration, + sourceFile: ts.SourceFile + ): number { + const { startLine, endLine } = this.getNodeLineRange(classNode, sourceFile); + let duplicateCount = 0; + + duplicateInfo.forEach((details, location) => { + const [dupStart, dupEnd] = this.parseLineRange(location); + + // Check if duplicate overlaps with class + if (dupStart >= startLine && dupEnd <= endLine) { + duplicateCount += details.count; + } + }); + + return duplicateCount; + } + + getDuplicateDetailsForClass( + duplicateInfo: Map, + classNode: ts.ClassDeclaration, + sourceFile: ts.SourceFile + ): DuplicateDetails[] { + const { startLine, endLine } = this.getNodeLineRange(classNode, sourceFile); + const details: DuplicateDetails[] = []; + + duplicateInfo.forEach((info, location) => { + const [dupStart, dupEnd] = this.parseLineRange(location); + if (dupStart >= startLine && dupEnd <= endLine) { + details.push(info); + } + }); + + return details; + } + + private getNodeLineRange( + node: ts.Node, + sourceFile: ts.SourceFile + ): { startLine: number; endLine: number } { + const start = sourceFile.getLineAndCharacterOfPosition(node.getStart()); + const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()); + return { + startLine: start.line + 1, + endLine: end.line + 1, + }; + } + + private async computeDuplicateInfo( + fileName: string, + sourceCode: string + ): Promise> { + const tokenizer = new Tokenizer(); + const store = new MemoryStore(); + const options = getDefaultOptions(); + options.minLines = options.minLines ?? DuplicateCodeAnalyzer.MIN_LINES; + + const detector = new Detector(tokenizer, store, [], options); + const format = 'typescript'; + + let clones: IClone[] = []; + try { + clones = await detector.detect(fileName, sourceCode, format); + } catch (_err) { + // If detection fails, return empty result + clones = []; + } + + return this.processClones(clones); + } + + private processClones(clones: IClone[]): Map { + const map = new Map(); + + for (const clone of clones) { + const aStart = clone.duplicationA.start.line; + const aEnd = clone.duplicationA.end.line; + const bStart = clone.duplicationB.start.line; + const bEnd = clone.duplicationB.end.line; + + const keyA = `${aStart}-${aEnd}`; + const keyB = `${bStart}-${bEnd}`; + + const sevA = this.computeSeverity(aEnd - aStart + 1); + const sevB = this.computeSeverity(bEnd - bStart + 1); + + const existingA = map.get(keyA); + const existingB = map.get(keyB); + + map.set(keyA, { + location: keyA, + count: (existingA?.count ?? 0) + 1, + severity: existingA ? this.maxSeverity(existingA.severity, sevA) : sevA, + }); + + map.set(keyB, { + location: keyB, + count: (existingB?.count ?? 0) + 1, + severity: existingB ? this.maxSeverity(existingB.severity, sevB) : sevB, + }); + } + + return map; + } + + private computeSeverity(lines: number): 'LOW' | 'MEDIUM' | 'HIGH' { + if (lines >= DuplicateCodeAnalyzer.SEVERITY_THRESHOLDS.HIGH) return 'HIGH'; + if (lines >= DuplicateCodeAnalyzer.SEVERITY_THRESHOLDS.MEDIUM) + return 'MEDIUM'; + return 'LOW'; + } + + private maxSeverity( + a: 'LOW' | 'MEDIUM' | 'HIGH', + b: 'LOW' | 'MEDIUM' | 'HIGH' + ): 'LOW' | 'MEDIUM' | 'HIGH' { + const order: Record<'LOW' | 'MEDIUM' | 'HIGH', number> = { + LOW: 0, + MEDIUM: 1, + HIGH: 2, + }; + return order[a] >= order[b] ? a : b; + } + + private parseLineRange(location: string): [number, number] { + const parts = location.split('-').map(Number); + return [parts[0], parts[1]]; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.spec.ts new file mode 100644 index 0000000..7882a46 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.spec.ts @@ -0,0 +1,57 @@ +import { MethodResponsibilityMetric } from './method-responsibility-metric'; +import { getFirstMethodFrom, createContext, parseClass } from './test-utils'; + +describe('MethodResponsibilityMetric', () => { + // Helpers imported from test-utils + + it('computes responsibilities, categories, and flags for a simple method', () => { + const code = ` + class TestClass { + simpleMethod(param) { + const x = 1; + console.log(x); + return x + 2; + } + } + `; + + const { sourceFile } = parseClass(code); + const ctx = createContext(sourceFile); + const method = getFirstMethodFrom(ctx.sourceFile); + const metric = new MethodResponsibilityMetric(); + const result = metric.analyze(ctx, method); + + expect(result.responsibilities).toBeGreaterThanOrEqual(1); + expect(result.categories.has('logging')).toBe(true); + expect(result.writesThis).toBe(false); + expect(result.mutatesParams).toBe(false); + }); + + it('detects writes to this, param mutations, and global categories', () => { + const code = ` + class UserService { + updateUser(user) { + console.log('Updating user:', user.id); + this.status = 'updating'; + user = { id: user.id }; + fetch('/api/users/' + user.id, { method: 'PUT' }); + localStorage.setItem('lastUser', user.id); + this.users.push(user); + } + } + `; + + const { sourceFile } = parseClass(code); + const ctx = createContext(sourceFile); + const method = getFirstMethodFrom(ctx.sourceFile); + const metric = new MethodResponsibilityMetric(); + const result = metric.analyze(ctx, method); + + expect(result.writesThis).toBe(true); + expect(result.mutatesParams).toBe(true); + expect(result.categories.has('logging')).toBe(true); + expect(result.categories.has('networking')).toBe(true); + expect(result.categories.has('storage')).toBe(true); + expect(result.responsibilities).toBeGreaterThanOrEqual(3); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.ts new file mode 100644 index 0000000..690c208 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/method-responsibility-metric.ts @@ -0,0 +1,362 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +interface ResponsibilityAnalysisResult { + responsibilities: number; + categories: Set; + writesThis: boolean; + mutatesParams: boolean; +} + +export class MethodResponsibilityMetric { + static getMeta() { + return { + schema: z.object({ + responsibilities: z + .number() + .describe( + 'Distinct responsibility categories detected in the method.' + ), + writesThis: z + .boolean() + .optional() + .describe('True if method writes to this.* or mutates this state.'), + mutatesParams: z + .boolean() + .optional() + .describe('True if method mutates its parameters.'), + }), + ui: { + metrics: [ + { + path: 'responsibilities', + label: 'Responsibilities', + icon: 'category', + threshold: { warnGte: 4 }, + tooltip: + 'Higher means the method does many distinct kinds of work.', + refLink: 'https://martinfowler.com/bliki/FunctionLength.html', + }, + { + path: 'writesThis', + label: 'Writes this.*', + icon: 'edit', + tooltip: 'Method mutates object state (this.*).', + }, + { + path: 'mutatesParams', + label: 'Mutates params', + icon: 'sync_problem', + tooltip: 'Method reassigns or mutates its parameters.', + }, + ], + }, + } as const; + } + private static readonly MUTATING_METHODS = new Set([ + 'push', + 'pop', + 'splice', + 'shift', + 'unshift', + 'sort', + 'reverse', + 'copyWithin', + 'fill', + 'set', + 'add', + 'delete', + 'clear', + ]); + + private static readonly ASSIGNMENT_OPERATORS = new Set([ + ts.SyntaxKind.EqualsToken, + ts.SyntaxKind.PlusEqualsToken, + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.AsteriskAsteriskEqualsToken, + ts.SyntaxKind.SlashEqualsToken, + ts.SyntaxKind.PercentEqualsToken, + ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + ts.SyntaxKind.AmpersandEqualsToken, + ts.SyntaxKind.BarEqualsToken, + ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.BarBarEqualsToken, + ts.SyntaxKind.AmpersandAmpersandEqualsToken, + ts.SyntaxKind.QuestionQuestionEqualsToken, + ]); + + analyze( + context: AnalyzerContext, + node: ts.FunctionLikeDeclaration + ): ResponsibilityAnalysisResult { + const categories = new Set(); + let writesThis = false; + let mutatesParams = false; + + const parameterNames = + MethodResponsibilityMetric.extractParameterNames(node); + + const visit = (n: ts.Node): void => { + const bin = this.checkBinary(n, parameterNames); + if (bin.writesThis) writesThis = true; + if (bin.mutatesParams) mutatesParams = true; + + if (this.checkUnaryWritesThis(n)) writesThis = true; + + if (ts.isCallExpression(n)) { + if (this.handleCall(n, context, categories)) writesThis = true; + } + + ts.forEachChild(n, visit); + }; + + ts.forEachChild(node, visit); + + return { + responsibilities: categories.size, + categories, + writesThis, + mutatesParams, + }; + } + + private static extractParameterNames( + node: ts.FunctionLikeDeclaration + ): Set { + return new Set( + node.parameters.map((p) => (ts.isIdentifier(p.name) ? p.name.text : '')) + ); + } + + private checkBinary( + node: ts.Node, + parameterNames: Set + ): { writesThis: boolean; mutatesParams: boolean } { + if (!ts.isBinaryExpression(node)) + return { writesThis: false, mutatesParams: false }; + const isAssignment = MethodResponsibilityMetric.ASSIGNMENT_OPERATORS.has( + node.operatorToken.kind + ); + if (!isAssignment) return { writesThis: false, mutatesParams: false }; + return { + writesThis: this.isThisPropertyAssignment(node.left), + mutatesParams: this.isParameterMutation(node.left, parameterNames), + }; + } + + private checkUnaryWritesThis(node: ts.Node): boolean { + if (!ts.isPrefixUnaryExpression(node) && !ts.isPostfixUnaryExpression(node)) + return false; + const operator = ts.isPrefixUnaryExpression(node) + ? node.operator + : node.operator; + const operand = ts.isPrefixUnaryExpression(node) + ? node.operand + : node.operand; + const isIncDec = + operator === ts.SyntaxKind.PlusPlusToken || + operator === ts.SyntaxKind.MinusMinusToken; + if (!isIncDec) return false; + return this.isThisPropertyAccess(operand); + } + + private handleCall( + node: ts.CallExpression, + context: AnalyzerContext, + categories: Set + ): boolean { + const expression = node.expression; + this.categorizeGlobalUsage(expression.getText(), categories); + const symbol = context.checker.getSymbolAtLocation(expression); + if (symbol) this.categorizeByDeclarations(symbol, categories); + if ( + ts.isPropertyAccessExpression(expression) || + ts.isElementAccessExpression(expression) + ) { + this.categorizeByReceiverType(expression.expression, context, categories); + } + return this.isMutatingCallOnThisProperty(expression); + } + + private isThisPropertyAssignment(node: ts.Expression): boolean { + return ( + ts.isPropertyAccessExpression(node) && + node.expression.kind === ts.SyntaxKind.ThisKeyword + ); + } + + private isParameterMutation( + node: ts.Expression, + parameterNames: Set + ): boolean { + return ts.isIdentifier(node) && parameterNames.has(node.text); + } + + private isThisPropertyAccess(node: ts.Expression): boolean { + return ( + ts.isPropertyAccessExpression(node) && + node.expression.kind === ts.SyntaxKind.ThisKeyword + ); + } + + private isMutatingCallOnThisProperty(expression: ts.Expression): boolean { + if (ts.isPropertyAccessExpression(expression)) { + return this.isMutatingPropertyCall(expression); + } + + if (ts.isElementAccessExpression(expression)) { + return this.isMutatingElementCall(expression); + } + + return false; + } + + private isMutatingPropertyCall( + expression: ts.PropertyAccessExpression + ): boolean { + const methodName = expression.name.getText(); + const receiver = expression.expression; + + return ( + MethodResponsibilityMetric.MUTATING_METHODS.has(methodName) && + ts.isPropertyAccessExpression(receiver) && + receiver.expression.kind === ts.SyntaxKind.ThisKeyword + ); + } + + private isMutatingElementCall( + expression: ts.ElementAccessExpression + ): boolean { + const receiver = expression.expression; + const argument = expression.argumentExpression; + + if (!ts.isStringLiteral(argument)) return false; + + const methodName = argument.text; + return ( + MethodResponsibilityMetric.MUTATING_METHODS.has(methodName) && + ts.isPropertyAccessExpression(receiver) && + receiver.expression.kind === ts.SyntaxKind.ThisKeyword + ); + } + + // Removed branching: global categorization always happens in handleCall + + private categorizeGlobalUsage(text: string, categories: Set): void { + if (text.startsWith('console')) categories.add('logging'); + if (text.startsWith('fetch')) categories.add('networking'); + if (text.startsWith('localStorage') || text.startsWith('sessionStorage')) { + categories.add('storage'); + } + if (text.startsWith('document') || text.startsWith('window')) { + categories.add('dom'); + } + if ( + text.startsWith('setTimeout') || + text.startsWith('setInterval') || + text.startsWith('Date') + ) { + categories.add('time'); + } + } + + private categorizeByDeclarations( + symbol: ts.Symbol, + categories: Set + ): void { + const declarations = symbol.getDeclarations() || []; + for (const declaration of declarations) { + const sourceFile = declaration.getSourceFile().fileName; + if (!sourceFile.includes('node_modules')) categories.add('internal'); + } + } + + private categorizeByReceiverType( + receiver: ts.Expression, + context: AnalyzerContext, + categories: Set + ): void { + const type = context.checker.getTypeAtLocation(receiver); + const typeSymbol = type.getSymbol(); + const typeName = typeSymbol?.getName(); + const declarations = typeSymbol?.getDeclarations() || []; + const sourceFiles = declarations.map((d) => d.getSourceFile().fileName); + + this.categorizeByTypeName(typeName, sourceFiles, categories); + } + + private static readonly TYPE_CATEGORY_RULES: ReadonlyArray<{ + category: string; + typeNames?: readonly string[]; + sourceIncludesAll?: readonly string[]; + sourceIncludesAny?: readonly string[]; + }> = [ + { + category: 'networking', + typeNames: ['HttpClient'], + sourceIncludesAny: ['@angular/common/http'], + }, + { + category: 'navigation', + typeNames: ['Router'], + sourceIncludesAny: ['@angular/router'], + }, + { + category: 'change-detection', + typeNames: ['ChangeDetectorRef'], + sourceIncludesAll: ['@angular/core', 'change_detector'], + }, + { + category: 'zone', + typeNames: ['NgZone'], + sourceIncludesAll: ['@angular/core', 'zone'], + }, + { + category: 'dom', + typeNames: ['Renderer2'], + sourceIncludesAll: ['@angular/core', 'renderer'], + }, + { + category: 'dom', + typeNames: ['Document', 'Window'], + sourceIncludesAny: ['lib.dom.d.ts'], + }, + { + category: 'state', + typeNames: ['Store'], + sourceIncludesAny: ['@ngrx/store'], + }, + { category: 'state-effects', sourceIncludesAny: ['@ngrx/effects'] }, + { + category: 'forms', + typeNames: ['FormBuilder', 'FormControl'], + sourceIncludesAny: ['@angular/forms'], + }, + ]; + + private categorizeByTypeName( + typeName: string | undefined, + sourceFiles: string[], + categories: Set + ): void { + for (const rule of MethodResponsibilityMetric.TYPE_CATEGORY_RULES) { + const nameMatch = rule.typeNames?.includes(typeName ?? '') ?? false; + const anyMatch = + rule.sourceIncludesAny?.some((s) => + sourceFiles.some((f) => f.includes(s)) + ) ?? false; + const allMatch = + rule.sourceIncludesAll?.every((s) => + sourceFiles.some((f) => f.includes(s)) + ) ?? false; + if (nameMatch || anyMatch || allMatch) { + categories.add(rule.category); + } + } + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.base.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.base.ts new file mode 100644 index 0000000..83f660a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.base.ts @@ -0,0 +1,27 @@ +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { getMetricMetadata } from './metric.decorator'; +import { IMetric, MetricMetadata } from './metric.interface'; + +export abstract class BaseMetric implements IMetric { + abstract analyze(context: AnalyzerContext): T; + analyzeAsync(context: AnalyzerContext): Promise { + return Promise.resolve(this.analyze(context)); + } + + getMetadata(): MetricMetadata { + const metadata = getMetricMetadata(this.constructor); + if (!metadata) { + throw new Error(`${this.constructor.name} is missing @Metric decorator`); + } + return metadata; + } + + static getMetadata(): MetricMetadata { + const metadata = getMetricMetadata(this); + if (!metadata) { + throw new Error(`${this.name} is missing @Metric decorator`); + } + return metadata; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.decorator.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.decorator.ts new file mode 100644 index 0000000..f508e1b --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.decorator.ts @@ -0,0 +1,26 @@ +import 'reflect-metadata'; +import { MetricMetadata } from './metric.interface'; + +const METRIC_METADATA_KEY = Symbol('metric:metadata'); + +export function Metric(metadata: MetricMetadata): ClassDecorator { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return function (target: any) { + Reflect.defineMetadata(METRIC_METADATA_KEY, metadata, target); + + target.prototype.getMetadata = function () { + return getMetricMetadata(this.constructor); + }; + + target.getMetadata = function () { + return getMetricMetadata(target); + }; + + return target; + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getMetricMetadata(target: any): MetricMetadata | undefined { + return Reflect.getMetadata(METRIC_METADATA_KEY, target); +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.interface.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.interface.ts new file mode 100644 index 0000000..5c71684 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/metric.interface.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; + +import { MetricItem, BadgeItem, Section } from '../ui-schema.types'; +import { AnalyzerContext } from '../x-ray-metrics.types'; + +export interface MetricMetadata { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + schema: z.ZodObject; + ui: { + metrics: MetricItem[]; + badges?: BadgeItem[]; + sections?: Section[]; + }; +} + +export interface IMetric { + analyze(context: AnalyzerContext): T; + analyzeAsync(context: AnalyzerContext): Promise; + getMetadata(): MetricMetadata; +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.spec.ts new file mode 100644 index 0000000..221a5dc --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.spec.ts @@ -0,0 +1,71 @@ +import { NestedConditionsMetric } from './nested-conditions-metric'; +import { parseClass } from './test-utils'; + +describe('NestedConditionsMetric', () => { + it('should return 0 for method with no conditionals', () => { + const classCode = ` + class SimpleClass { + simpleMethod() { + const x = 1; + return x + 2; + } + } + `; + + const { method } = parseClass(classCode); + const metric = new NestedConditionsMetric(); + const nestedCount = metric.analyze(method); + + expect(nestedCount).toBe(0); + }); + + it('should return 5 for deeply nested conditional method', () => { + const classCode = ` + class ComplexClass { + complexMethod(condition1, condition2) { + if (condition1) { + while (condition2) { + for (let i = 0; i < 10; i++) { + if (i % 2 === 0) { + switch (i) { + case 0: + return 'zero'; + case 2: + return 'two'; + default: + break; + } + } + } + } + } + return 'done'; + } + } + `; + + const { method } = parseClass(classCode); + const metric = new NestedConditionsMetric(); + const nestedCount = metric.analyze(method); + + expect(nestedCount).toBe(5); + }); + + it('should return 2 for method with nested ternary operators', () => { + const classCode = ` + class TernaryClass { + ternaryMethod(a, b, c) { + return a ? + (b ? 'both true' : 'only a true') : + (c ? 'only c true' : 'all false'); + } + } + `; + + const { method } = parseClass(classCode); + const metric = new NestedConditionsMetric(); + const nestedCount = metric.analyze(method); + + expect(nestedCount).toBe(2); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.ts new file mode 100644 index 0000000..93c034a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/nested-conditions-metric.ts @@ -0,0 +1,65 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +export class NestedConditionsMetric { + static getMeta() { + return { + schema: z.object({ + nestedConditions: z + .number() + .describe('Maximum nesting depth of conditionals within the method.'), + }), + ui: { + metrics: [ + { + path: 'nestedConditions', + label: 'Nesting', + icon: 'layers', + threshold: { warnGte: 3 }, + tooltip: 'Reduce nesting for readability and maintainability.', + refLink: 'https://refactoring.guru/smells/deeply-nested-code', + }, + ], + }, + } as const; + } + /** + * Counts the maximum nesting depth of conditional statements in a TypeScript node. + * Tracks if/while/for/switch statements and returns the deepest level found. + */ + analyze(node: ts.Node): number { + let maxDepth = 0; + let currentDepth = 0; + + const visit = (node: ts.Node) => { + const isConditional = this.isConditionalStatement(node); + + if (isConditional) { + currentDepth++; + maxDepth = Math.max(maxDepth, currentDepth); + } + + ts.forEachChild(node, visit); + + if (isConditional) { + currentDepth--; + } + }; + + visit(node); + return maxDepth; + } + + private isConditionalStatement(node: ts.Node): boolean { + return ( + node.kind === ts.SyntaxKind.IfStatement || + node.kind === ts.SyntaxKind.WhileStatement || + node.kind === ts.SyntaxKind.ForStatement || + node.kind === ts.SyntaxKind.ForInStatement || + node.kind === ts.SyntaxKind.ForOfStatement || + node.kind === ts.SyntaxKind.SwitchStatement || + node.kind === ts.SyntaxKind.ConditionalExpression || + node.kind === ts.SyntaxKind.DoStatement + ); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.spec.ts new file mode 100644 index 0000000..a803944 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.spec.ts @@ -0,0 +1,63 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { ComplexAlgorithmsMetric } from './complex-algorithms.metric'; + +describe('ComplexAlgorithmsMetric', () => { + it('counts 1 when a method has cyclomatic complexity ≥ 15 or nesting depth ≥ 4', () => { + const code = ` + function complex(a, b, c, d) { + if (a) { + for (let i = 0; i < 3; i++) { + while (b) { + if (c) { + switch (d) { + case 1: if (a && b) { return 1; } break; + case 2: if (a || c) { return 2; } break; + default: if (!d) { return 3; } + } + } + } + } + } + return 0; + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexAlgorithmsMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(1); + }); + + it('counts 0 for simple functions below thresholds', () => { + const code = ` + function simple(x) { + if (x) { + return 1; + } + return 0; + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexAlgorithmsMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.ts new file mode 100644 index 0000000..59a4f03 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/complex-algorithms.metric.ts @@ -0,0 +1,56 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { calcComplexityForNode } from '../../../../../utils/complexity'; +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; +import { NestedConditionsMetric } from '../nested-conditions-metric'; + +@Metric({ + schema: z.object({ + complexAlgorithms: z + .number() + .describe( + 'Counts functions/methods that are likely hard to understand or change based on two heuristics: cyclomatic complexity ≥ 15 or maximum nesting depth ≥ 4. High control-flow complexity and deep nesting reduce readability and maintainability; consider refactoring with early returns, extraction, or simplifying conditions.' + ), + }), + ui: { + metrics: [ + { + path: 'complexAlgorithms', + label: 'Complex Algorithms', + icon: 'account_tree', + threshold: { warnGte: 1 }, + refLink: 'https://en.wikipedia.org/wiki/Cyclomatic_complexity', + }, + ], + }, +}) +export class ComplexAlgorithmsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let complexMethods = 0; + + const cyclomaticComplexity = (node: ts.Node): number => + calcComplexityForNode(node); + + const nestedConditionsMetric = new NestedConditionsMetric(); + + const walk = (node: ts.Node) => { + if ( + ts.isMethodDeclaration(node) || + ts.isFunctionDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isArrowFunction(node) + ) { + const cpx = cyclomaticComplexity(node); + const depth = nestedConditionsMetric.analyze(node); + if (cpx >= 15 || depth >= 4) complexMethods++; + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return complexMethods; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.spec.ts new file mode 100644 index 0000000..29b2a0a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.spec.ts @@ -0,0 +1,55 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { FeatureEnvyMetric } from './feature-envy.metric'; + +describe('FeatureEnvyMetric', () => { + it('counts 1 for a method that accesses foreign params properties 3+ times and more than this', () => { + const code = ` + class OrderService { + process(user, order) { + const id = user.id; + const total = order.total; + const status = order.status; + return id + total + status; + } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new FeatureEnvyMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(1); + }); + + it('counts 0 when foreign property accesses are below threshold or not greater than this', () => { + const code = ` + class OrderService { + process(user) { + this.doSomething(); + return user.id; + } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new FeatureEnvyMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.ts new file mode 100644 index 0000000..2f631ec --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/feature-envy.metric.ts @@ -0,0 +1,79 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + featureEnvy: z + .number() + .describe( + 'Counts methods that access properties on parameters (foreign data) significantly more than their own state. Triggered when a method reads fields on its parameters at least 3 times and more often than it accesses `this`. This smell suggests the behavior likely belongs on the collaborator type; consider moving the method, extracting an object, or introducing better abstractions.' + ), + }), + ui: { + metrics: [ + { + path: 'featureEnvy', + label: 'Feature Envy', + icon: 'swap_calls', + threshold: { warnGte: 1 }, + refLink: 'https://refactoring.guru/smells/feature-envy', + }, + ], + }, +}) +export class FeatureEnvyMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let methodsWithFeatureEnvy = 0; + + const isFeatureEnvy = (fn: ts.FunctionLikeDeclarationBase): boolean => { + if (!fn.body) return false; + + const paramNames: ReadonlySet = new Set( + (fn.parameters ?? []) + .map((p) => (ts.isIdentifier(p.name) ? p.name.text : undefined)) + .filter((n): n is string => !!n) + ); + + let thisAccesses = 0; + let foreignParamPropertyAccesses = 0; + + const visit = (n: ts.Node) => { + if (ts.isPropertyAccessExpression(n)) { + const expr = n.expression; + if (expr.kind === ts.SyntaxKind.ThisKeyword) { + thisAccesses++; + } else if (ts.isIdentifier(expr) && paramNames.has(expr.text)) { + foreignParamPropertyAccesses++; + } + } + ts.forEachChild(n, visit); + }; + + ts.forEachChild(fn.body, visit); + + return ( + foreignParamPropertyAccesses >= 3 && + foreignParamPropertyAccesses > thisAccesses + ); + }; + + const walk = (node: ts.Node) => { + if ( + ts.isMethodDeclaration(node) || + ts.isFunctionDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isArrowFunction(node) + ) { + if (isFeatureEnvy(node)) methodsWithFeatureEnvy++; + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return methodsWithFeatureEnvy; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.spec.ts new file mode 100644 index 0000000..25134f5 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.spec.ts @@ -0,0 +1,78 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { MiddleManMetric } from './middle-man.metric'; + +describe('MiddleManMetric', () => { + it('counts 1 when a class has ≥3 methods and ≥60% are single-statement delegations', () => { + const code = ` + class Service { + // internal helpers as fields so they don't count as methods + fetchInternal = () => 1; + persist = (item) => item; + + // delegating to this (recognized) + doFetch() { return this.fetchInternal(); } + + // delegating to parameter (recognized) + execute(task) { return task.run(); } + + // delegating to this again (recognized) + save(item) { return this.persist(item); } + + // non-delegating (multiple statements) + compute(x) { const y = x + 1; return y * 2; } + + // non-delegating (not a call expression) + identity(v) { return v; } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MiddleManMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(1); + }); + + it('counts 0 when delegating ratio is below 60% or methods < 3', () => { + const code = ` + class ApiClient {} + + class Service { + constructor(private api: ApiClient) {} + + // delegating + getOne(id) { return this.api.getOne(id); } + + // delegating + run(task) { return task.run(); } + + // non-delegating + calc(a) { const b = a * 2; return b - 1; } + + // non-delegating + noop() { /* nothing */ } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MiddleManMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.ts new file mode 100644 index 0000000..254b5ab --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/middle-man.metric.ts @@ -0,0 +1,80 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + middleManClasses: z + .number() + .describe( + 'Counts classes where the majority of methods are one-liners that simply delegate to another object (e.g., `this.dep.method(...)` or a parameter call). Triggered when a class has at least 3 methods and ≥60% are single-statement delegations. This smell suggests low cohesion and unnecessary indirection; consider moving behavior to the real owner or collapsing pass-throughs.' + ), + }), + ui: { + metrics: [ + { + path: 'middleManClasses', + label: 'Middle Man', + icon: 'compare_arrows', + threshold: { warnGte: 1 }, + refLink: 'https://refactoring.guru/smells/middle-man', + }, + ], + }, +}) +export class MiddleManMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let middleManClasses = 0; + + const isDelegatingCall = ( + expr: ts.Expression, + params: ReadonlySet + ): boolean => { + if (!ts.isCallExpression(expr)) return false; + const callee = expr.expression; + if (!ts.isPropertyAccessExpression(callee)) return false; + const target = callee.expression; + if (target.kind === ts.SyntaxKind.ThisKeyword) return true; + return ts.isIdentifier(target) && params.has(target.text); + }; + + const isDelegatingMethod = (method: ts.MethodDeclaration): boolean => { + if (!method.body) return false; + const params = new Set( + method.parameters + .map((p) => (ts.isIdentifier(p.name) ? p.name.text : undefined)) + .filter((n): n is string => !!n) + ); + const stmts = method.body.statements; + if (stmts.length !== 1) return false; + const only = stmts[0]; + if (ts.isReturnStatement(only) && only.expression) { + return isDelegatingCall(only.expression, params); + } + if (ts.isExpressionStatement(only)) { + return isDelegatingCall(only.expression, params); + } + return false; + }; + + const walk = (node: ts.Node) => { + if (ts.isClassDeclaration(node)) { + const methods: ts.MethodDeclaration[] = node.members.filter( + (m): m is ts.MethodDeclaration => + ts.isMethodDeclaration(m) && !!m.body + ); + if (methods.length === 0) return; + const delegating = methods.filter((m) => isDelegatingMethod(m)).length; + const ratio = delegating / methods.length; + if (methods.length >= 3 && ratio >= 0.6) middleManClasses++; + } + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return middleManClasses; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.spec.ts new file mode 100644 index 0000000..4288b33 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.spec.ts @@ -0,0 +1,66 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { TemporalCouplingMetric } from './temporal-coupling.metric'; + +describe('TemporalCouplingMetric', () => { + it('counts 1 when a field is written in one method and read in a condition in another (order dependence)', () => { + const code = ` + class Service { + flag = false; + + initialize() { + this.flag = true; + } + + execute() { + if (this.flag) { + return 'go'; + } + return 'stop'; + } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TemporalCouplingMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(1); + }); + + it('counts 0 when reads in conditions happen only within the same method that writes (no cross-method order dependence)', () => { + const code = ` + class Service { + ready = false; + + process() { + this.ready = !this.ready; + if (this.ready) { + return 'ok'; + } + return 'wait'; + } + } + `; + + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TemporalCouplingMetric(); + const count = metric.analyze(ctx); + + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.ts new file mode 100644 index 0000000..855e8be --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/organization/temporal-coupling.metric.ts @@ -0,0 +1,154 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + temporalCoupling: z + .number() + .describe( + 'Counts fields that are written in one method and later read inside a conditional in a different method. This indicates an implicit ordering dependency between methods. If one method must run before another for correct behavior, the class may be exposing temporal coupling. Consider encapsulating the sequence, deriving values on demand, or reducing hidden state dependencies.' + ), + }), + ui: { + metrics: [ + { + path: 'temporalCoupling', + label: 'Temporal Coupling', + icon: 'timeline', + threshold: { warnGte: 1 }, + refLink: + 'https://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/', + }, + ], + }, +}) +export class TemporalCouplingMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + type MethodKey = string; + let coupledFields = 0; + + const collectFieldWrites = ( + body: ts.Block | ts.ConciseBody, + writer: (field: string) => void + ): void => { + const recordWrite = (n: ts.Node) => { + if ( + ts.isBinaryExpression(n) && + (n.operatorToken.kind === ts.SyntaxKind.EqualsToken || + n.operatorToken.kind === ts.SyntaxKind.PlusEqualsToken || + n.operatorToken.kind === ts.SyntaxKind.MinusEqualsToken || + n.operatorToken.kind === ts.SyntaxKind.AsteriskEqualsToken || + n.operatorToken.kind === ts.SyntaxKind.SlashEqualsToken) + ) { + if ( + ts.isPropertyAccessExpression(n.left) && + n.left.expression.kind === ts.SyntaxKind.ThisKeyword + ) { + writer(n.left.name.getText()); + } + } else if ( + ts.isPrefixUnaryExpression(n) || + ts.isPostfixUnaryExpression(n) + ) { + const operand = ts.isPrefixUnaryExpression(n) ? n.operand : n.operand; + if ( + ts.isPropertyAccessExpression(operand) && + operand.expression.kind === ts.SyntaxKind.ThisKeyword + ) { + writer(operand.name.getText()); + } + } + ts.forEachChild(n, recordWrite); + }; + if (ts.isBlock(body)) ts.forEachChild(body, recordWrite); + else recordWrite(body); + }; + + const collectFieldReadsInConditions = ( + body: ts.Block | ts.ConciseBody, + reader: (field: string) => void + ): void => { + const scan = (n: ts.Node) => { + if ( + ts.isIfStatement(n) || + ts.isWhileStatement(n) || + ts.isDoStatement(n) || + ts.isForStatement(n) || + ts.isConditionalExpression(n) || + ts.isCaseClause(n) + ) { + let condition: ts.Expression | undefined; + if (ts.isIfStatement(n)) condition = n.expression; + else if (ts.isWhileStatement(n)) condition = n.expression; + else if (ts.isDoStatement(n)) condition = n.expression; + else if (ts.isForStatement(n)) condition = n.condition ?? undefined; + else if (ts.isConditionalExpression(n)) condition = n.condition; + else if (ts.isCaseClause(n)) condition = n.expression; + if (condition) { + const findFieldName = (m: ts.Node) => { + if ( + ts.isPropertyAccessExpression(m) && + m.expression.kind === ts.SyntaxKind.ThisKeyword + ) { + reader(m.name.getText()); + } + ts.forEachChild(m, findFieldName); + }; + findFieldName(condition); + } + } + ts.forEachChild(n, scan); + }; + if (ts.isBlock(body)) ts.forEachChild(body, scan); + else scan(body); + }; + + const walk = (node: ts.Node) => { + if (!ts.isClassDeclaration(node)) { + ts.forEachChild(node, walk); + return; + } + + const className = node.name?.text || 'AnonymousClass'; + const writes: Map> = new Map(); + const readsInConds: Map> = new Map(); + + const record = ( + map: Map>, + key: string, + methodKey: MethodKey + ) => { + const s = map.get(key) ?? new Set(); + s.add(methodKey); + map.set(key, s); + }; + + node.members.forEach((m) => { + if (!ts.isMethodDeclaration(m) || !m.body) return; + const methodName = m.name ? m.name.getText() : 'anonymous'; + const methodKey = `${className}.${methodName}`; + + collectFieldWrites(m.body, (field) => record(writes, field, methodKey)); + collectFieldReadsInConditions(m.body, (field) => + record(readsInConds, field, methodKey) + ); + }); + + writes.forEach((writers, field) => { + const readers = readsInConds.get(field); + if (!readers || readers.size === 0) return; + const disjoint = Array.from(readers).some((r) => !writers.has(r)); + if (disjoint) coupledFields++; + }); + + ts.forEachChild(node, walk); + }; + + walk(context.sourceFile); + return coupledFields; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.spec.ts new file mode 100644 index 0000000..f9a669b --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.spec.ts @@ -0,0 +1,91 @@ +import * as ts from 'typescript'; + +import { ReasonsToChangeMetric } from './reasons-to-change.metric'; +import { createContext, parseClass } from './test-utils'; + +describe('ReasonsToChangeMetric', () => { + let metric: ReasonsToChangeMetric; + + beforeEach(() => { + metric = new ReasonsToChangeMetric(); + }); + + it('analyzes real code and aggregates method/dep categories, packages, and files', () => { + const code = ` + export class Sample { + items = [] as number[]; + + doThings() { + console.log('log'); + fetch('/api'); + document.title = 'x'; + this.items.push(1); + } + } + `; + + const { classNode, sourceFile } = parseClass(code); + const context = createContext(sourceFile); + const result = metric.analyze({ ...context, scopeNode: classNode }); + + // Categories should include both method-derived and dependency-derived ones + expect(result.reasonsCategories).toEqual( + expect.arrayContaining([ + 'networking', // from fetch() + 'dom', // from document usage + ]) + ); + + // Layer crossing: UI/DOM mixed with state/IO + expect(result.layerCrossing).toBe(true); + + // Score remains within 1..10 and should be > 1 given multiple categories + expect(result.score).toBeGreaterThanOrEqual(3); + expect(result.score).toBeLessThanOrEqual(10); + }); + + it('returns the minimal result when no class is present in scope', () => { + const sf = ts.createSourceFile( + 'no-class.ts', + 'export const x = 42;', + ts.ScriptTarget.Latest, + true + ); + const context = createContext(sf); + const result = metric.analyze({ ...context, scopeNode: undefined }); + + expect(result.score).toBe(1); + expect(result.reasonsCategories).toEqual([]); + expect(result.externalPackages).toEqual([]); + expect(result.internalFiles).toEqual([]); + expect(result.layerCrossing).toBe(false); + }); + + it('detects layer crossing in an Angular-like class mixing DOM with IO', () => { + const code = ` + // Local stub with the Angular name to trigger type-based networking + class HttpClient { get(url: string): void {} } + + export class AngularLikeComponent { + http = inject(HttpClient); + + render() { + document.title = 'cmp'; + document.getElementById('root'); + this.http.get('/api/data'); + } + } + `; + + const { classNode, sourceFile } = parseClass(code); + const context = createContext(sourceFile); + const result = metric.analyze({ ...context, scopeNode: classNode }); + + expect(result.reasonsCategories).toEqual( + expect.arrayContaining(['dom', 'networking']) + ); + expect(result.layerCrossing).toBe(true); + expect(result.score).toBeGreaterThanOrEqual(3); + expect(result.score).toBeLessThanOrEqual(10); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.ts new file mode 100644 index 0000000..2289d8b --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/reasons-to-change.metric.ts @@ -0,0 +1,431 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { MethodResponsibilityMetric } from './method-responsibility-metric'; +import { BaseMetric } from './metric.base'; +import { Metric } from './metric.decorator'; + +export interface ReasonsToChangeMetricResult { + score: number; + reasonsCategories: string[]; + externalPackages: string[]; + internalFiles: string[]; + layerCrossing: boolean; +} + +@Metric({ + schema: z.object({ + reasonsToChange: z + .number() + .describe( + 'How many different reasons this class might need to change. Higher means lower cohesion. We add: the variety of work its methods do, the variety of things it depends on, a small bump if it uses many external packages, and another if it mixes UI with state or I/O. Result is kept on a 1–10 scale.' + ), + reasonsCategories: z + .array(z.string()) + .optional() + .describe('Distinct categories contributing to reasons-to-change.'), + externalPackages: z + .array(z.string()) + .optional() + .describe('External package roots used.'), + layerCrossing: z + .boolean() + .optional() + .describe('True if class mixes UI with IO/state.'), + internalFiles: z + .array(z.string()) + .optional() + .describe('Internal files referenced by types used by this class.'), + }), + ui: { + metrics: [ + { + path: 'reasonsToChange', + label: 'Reasons To Change', + icon: 'change_circle', + threshold: { warnGte: 6 }, + tooltip: + 'Estimate of how many reasons this class could change: method variety, dependency variety, external packages, and mixing UI with state/I/O. Higher is worse (1–10). (Formula: method casts + dep casts + ext pkgs/3 (max 2) + layer mix; clamped 1–10).', + refLink: 'https://reflectoring.io/single-responsibility-principle', + }, + ], + sections: [ + { + type: 'chips', + title: 'Reasons To Change - Categories', + path: 'reasonsCategories', + iconMapRef: 'categoryIcons', + tooltipMapRef: 'categoryDescriptions', + }, + { + type: 'list', + title: 'External Packages Used', + path: 'externalPackages', + icon: 'inventory_2', + }, + { + type: 'list', + title: 'Internal Sources', + path: 'internalFiles', + icon: 'description', + format: 'fileName', + }, + ], + }, +}) +export class ReasonsToChangeMetric extends BaseMetric { + private static readonly NEUTRAL_CATEGORIES = new Set([ + 'angular-core', + ]); + + analyze(context: AnalyzerContext): ReasonsToChangeMetricResult { + const classNode = + context.scopeNode && ts.isClassDeclaration(context.scopeNode) + ? context.scopeNode + : undefined; + + if (!classNode) { + return { + score: 1, + reasonsCategories: [], + externalPackages: [], + internalFiles: [], + layerCrossing: false, + }; + } + + const className = classNode.name?.text || 'AnonymousClass'; + const methodCategories = this.collectMethodCategories(context, className); + const dependencyCategories = this.collectDependencyCategories( + context, + classNode + ); + const externalPackages = this.collectExternalPackages(context, classNode); + const internalFiles = this.collectInternalFiles(context, classNode); + + const filteredMethodCats = this.filterNeutralCategories(methodCategories); + const filteredDepCats = this.filterNeutralCategories(dependencyCategories); + + const C = filteredMethodCats.size; + const D = filteredDepCats.size; + const I = Math.min(2, Math.floor(externalPackages.size / 3)); + + const unionForLayer = new Set([ + ...filteredMethodCats, + ...filteredDepCats, + ]); + const layerCrossing = this.detectLayerCrossing(unionForLayer); + const L = layerCrossing ? 1 : 0; + + const score = Math.max(1, Math.min(10, C + D + I + L)); + + const allCategories = new Set([ + ...methodCategories, + ...dependencyCategories, + ]); + const filteredCategories = this.filterNeutralCategories(allCategories); + + return { + score, + reasonsCategories: Array.from(filteredCategories).sort(), + externalPackages: Array.from(externalPackages).sort(), + internalFiles: Array.from(internalFiles).sort(), + layerCrossing, + }; + } + + private filterNeutralCategories(categories: Set): Set { + return new Set( + Array.from(categories).filter( + (c) => !ReasonsToChangeMetric.NEUTRAL_CATEGORIES.has(c) + ) + ); + } + + private collectMethodCategories( + context: AnalyzerContext, + className: string + ): Set { + const categories = new Set(); + const file = context.sourceFile; + + const visit = (n: ts.Node, currentClass?: string) => { + if (ts.isClassDeclaration(n)) { + const cls = n.name?.text || 'AnonymousClass'; + ts.forEachChild(n, (child) => visit(child, cls)); + } else if ( + (ts.isMethodDeclaration(n) || + ts.isFunctionDeclaration(n) || + ts.isArrowFunction(n) || + ts.isFunctionExpression(n)) && + currentClass === className + ) { + const res = new MethodResponsibilityMetric().analyze( + context, + n as ts.FunctionLikeDeclaration + ); + for (const c of res.categories) { + categories.add(c); + } + } + }; + + ts.forEachChild(file, (child) => visit(child)); + return categories; + } + + private collectDependencyCategories( + context: AnalyzerContext, + node: ts.ClassDeclaration + ): Set { + const categories = new Set(); + const checker = context.checker; + + const classifyType = (t: ts.Type | undefined) => { + if (!t) return; + const sym = t.getSymbol(); + const rawName = sym?.getName(); + const typeName = rawName?.startsWith('typeof ') + ? rawName.slice('typeof '.length) + : rawName; + const decls = sym?.getDeclarations() || []; + const srcs = decls.map((d) => d.getSourceFile().fileName); + + if ( + typeName === 'HttpClient' || + srcs.some((s) => s.includes('@angular/common/http')) + ) { + categories.add('networking'); + } + if ( + typeName === 'Router' || + srcs.some((s) => s.includes('@angular/router')) + ) { + categories.add('navigation'); + } + if (typeName === 'Store' || srcs.some((s) => s.includes('@ngrx/store'))) { + categories.add('state'); + } + if (srcs.some((s) => s.includes('@ngrx/effects'))) { + categories.add('state-effects'); + } + if ( + typeName === 'FormBuilder' || + typeName === 'FormControl' || + srcs.some((s) => s.includes('@angular/forms')) + ) { + categories.add('forms'); + } + if (srcs.some((s) => s.includes('@angular/material'))) { + categories.add('ui'); + } + if ( + typeName === 'Document' || + typeName === 'Window' || + srcs.some((s) => s.includes('lib.dom.d.ts')) + ) { + categories.add('dom'); + } + }; + + node.members.forEach((m) => { + if (ts.isConstructorDeclaration(m)) { + m.parameters.forEach((p) => + classifyType( + p.type + ? checker.getTypeFromTypeNode(p.type) + : checker.getTypeAtLocation(p) + ) + ); + } else if (ts.isPropertyDeclaration(m)) { + if (m.type) { + classifyType(checker.getTypeFromTypeNode(m.type)); + } else if (m.initializer) { + if (ts.isCallExpression(m.initializer)) { + const callExpr = m.initializer; + const callee = callExpr.expression; + const isInjectFn = + ts.isIdentifier(callee) && callee.text === 'inject'; + if (isInjectFn && callExpr.arguments.length > 0) { + const arg = callExpr.arguments[0]; + let categorized = false; + try { + const before = categories.size; + const injectedType = checker.getTypeAtLocation(arg); + classifyType(injectedType); + categorized = categories.size > before; + } catch { + categorized = false; + } + if (!categorized) { + // Fallback to token name-based classification when type resolution fails + let tokenName: string | undefined; + if (ts.isIdentifier(arg)) tokenName = arg.text; + else if (ts.isPropertyAccessExpression(arg)) + tokenName = arg.name.text; + if (tokenName === 'HttpClient') categories.add('networking'); + if (tokenName === 'Router') categories.add('navigation'); + if (tokenName === 'Store') categories.add('state'); + if (tokenName === 'FormBuilder' || tokenName === 'FormControl') + categories.add('forms'); + } + // We handled inject() explicitly; skip further type inference on initializer + return; + } + } + // Try to infer from initializer expression (e.g., http = new HttpClient()) + try { + classifyType(checker.getTypeAtLocation(m.initializer)); + } catch { + // ignore if checker cannot resolve in this test context + } + } + } + }); + + return categories; + } + + private collectExternalPackages( + context: AnalyzerContext, + node: ts.ClassDeclaration + ): Set { + const packages = new Set(); + const checker = context.checker; + + const pushDeclaration = (decl: ts.Declaration | undefined) => { + if (!decl) return; + const file = decl.getSourceFile().fileName; + const pkg = this.getPackageRootFromPath(file); + if (pkg) { + packages.add(pkg); + } + }; + + node.members.forEach((m) => { + if (ts.isConstructorDeclaration(m)) { + m.parameters.forEach((p) => { + const t = p.type + ? checker.getTypeFromTypeNode(p.type) + : checker.getTypeAtLocation(p); + const sym = t.getSymbol(); + const decl = sym?.getDeclarations()?.[0]; + pushDeclaration(decl); + }); + } else if (ts.isPropertyDeclaration(m)) { + let t: ts.Type | undefined; + if (m.type) { + t = checker.getTypeFromTypeNode(m.type); + } else if (m.initializer) { + if ( + ts.isCallExpression(m.initializer) && + ts.isIdentifier(m.initializer.expression) && + m.initializer.expression.text === 'inject' && + m.initializer.arguments.length > 0 + ) { + try { + t = checker.getTypeAtLocation(m.initializer.arguments[0]); + } catch { + t = undefined; + } + } else { + try { + t = checker.getTypeAtLocation(m.initializer); + } catch { + t = undefined; + } + } + } + if (t) { + const sym = t.getSymbol(); + const decl = sym?.getDeclarations()?.[0]; + pushDeclaration(decl); + } + } + }); + + return packages; + } + + private getPackageRootFromPath(filePath: string): string | undefined { + const nm = '/node_modules/'; + const idx = filePath.lastIndexOf(nm); + if (idx === -1) return undefined; + const after = filePath.slice(idx + nm.length); + const parts = after.split(/[\\/]/).filter(Boolean); + if (!parts.length) return undefined; + if (parts[0].startsWith('@') && parts.length >= 2) { + return `${parts[0]}/${parts[1]}`; + } + return parts[0]; + } + + private detectLayerCrossing(categories: Set): boolean { + const hasUI = categories.has('ui') || categories.has('dom'); + const hasIO = categories.has('networking') || categories.has('storage'); + const hasState = categories.has('state') || categories.has('state-effects'); + return hasUI && (hasIO || hasState); + } + + private collectInternalFiles( + context: AnalyzerContext, + node: ts.ClassDeclaration + ): Set { + const files = new Set(); + const checker = context.checker; + + const pushDeclaration = (decl: ts.Declaration | undefined) => { + if (!decl) return; + const file = decl.getSourceFile().fileName; + if (!file.includes('/node_modules/')) { + files.add(file); + } + }; + + node.members.forEach((m) => { + if (ts.isConstructorDeclaration(m)) { + m.parameters.forEach((p) => { + const t = p.type + ? checker.getTypeFromTypeNode(p.type) + : checker.getTypeAtLocation(p); + const sym = t.getSymbol(); + const decl = sym?.getDeclarations()?.[0]; + pushDeclaration(decl); + }); + } else if (ts.isPropertyDeclaration(m)) { + let t: ts.Type | undefined; + if (m.type) { + t = checker.getTypeFromTypeNode(m.type); + } else if (m.initializer) { + if ( + ts.isCallExpression(m.initializer) && + ts.isIdentifier(m.initializer.expression) && + m.initializer.expression.text === 'inject' && + m.initializer.arguments.length > 0 + ) { + try { + t = checker.getTypeAtLocation(m.initializer.arguments[0]); + } catch { + t = undefined; + } + } else { + try { + t = checker.getTypeAtLocation(m.initializer); + } catch { + t = undefined; + } + } + } + if (t) { + const sym = t.getSymbol(); + const decl = sym?.getDeclarations()?.[0]; + pushDeclaration(decl); + } + } + }); + + return files; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/test-utils.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/test-utils.ts new file mode 100644 index 0000000..78bdd2f --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/test-utils.ts @@ -0,0 +1,91 @@ +import * as ts from 'typescript'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +export function parseClass(code: string): { + classNode: ts.ClassDeclaration; + method: ts.MethodDeclaration; + sourceFile: ts.SourceFile; +} { + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + let classNode: ts.ClassDeclaration | undefined; + + ts.forEachChild(sourceFile, (node) => { + if (ts.isClassDeclaration(node)) { + classNode = node; + } + }); + + if (!classNode) throw new Error('No class found'); + const method = classNode.members[0] as ts.MethodDeclaration; + return { classNode, method, sourceFile }; +} + +export function createContext(sourceFile: ts.SourceFile): AnalyzerContext { + const fileName = sourceFile.fileName; + const options: ts.CompilerOptions = { + target: ts.ScriptTarget.ES2018, + module: ts.ModuleKind.CommonJS, + lib: ['lib.es2018.d.ts', 'lib.dom.d.ts'], + }; + + const host = ts.createCompilerHost(options); + const origGetSourceFile = host.getSourceFile.bind(host); + const origReadFile = host.readFile?.bind(host); + const origFileExists = host.fileExists?.bind(host); + + host.getSourceFile = (f, languageVersion) => { + if (f === fileName) { + return ts.createSourceFile( + f, + sourceFile.getFullText(), + languageVersion, + true + ); + } + return origGetSourceFile(f, languageVersion); + }; + host.readFile = (f) => + f === fileName ? sourceFile.getFullText() : origReadFile?.(f); + host.fileExists = (f) => + f === fileName ? true : origFileExists?.(f) ?? false; + host.writeFile = () => undefined; + + const program = ts.createProgram([fileName], options, host); + const sf = + program.getSourceFile(fileName) ?? + ts.createSourceFile( + fileName, + sourceFile.getFullText(), + ts.ScriptTarget.Latest, + true + ); + const checker = program.getTypeChecker(); + + return { + sourceFile: sf, + sourceCode: sf.getFullText(), + checker, + program, + }; +} + +export function getFirstMethodFrom(sf: ts.SourceFile): ts.MethodDeclaration { + let found: ts.MethodDeclaration | undefined; + ts.forEachChild(sf, (node) => { + if ( + ts.isClassDeclaration(node) && + node.members.length > 0 && + ts.isMethodDeclaration(node.members[0]) + ) { + found = node.members[0] as ts.MethodDeclaration; + } + }); + if (!found) throw new Error('No method found'); + return found; +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.spec.ts new file mode 100644 index 0000000..0b34681 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.spec.ts @@ -0,0 +1,39 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { AnyTypesMetric } from './any-types.metric'; + +describe('AnyTypesMetric', () => { + it('counts any type occurrences', () => { + const code = ` + function f(x: any) { return x; } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new AnyTypesMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); + + it('does not count when no any is used', () => { + const code = ` + function f(x: string) { return x; } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new AnyTypesMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.ts new file mode 100644 index 0000000..1281de3 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/any-types.metric.ts @@ -0,0 +1,37 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + anyTypes: z.number().describe('Occurrences of any types.'), + }), + ui: { + metrics: [ + { + path: 'anyTypes', + label: 'Any Types', + icon: 'warning', + threshold: { warnGte: 1 }, + tooltip: 'Reduces type safety.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any', + }, + ], + }, +}) +export class AnyTypesMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const visit = (node: ts.Node) => { + if (ts.isTypeNode(node) && node.kind === ts.SyntaxKind.AnyKeyword) + count++; + ts.forEachChild(node, visit); + }; + visit(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.spec.ts new file mode 100644 index 0000000..75db9a4 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.spec.ts @@ -0,0 +1,39 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { ComplexUnionsMetric } from './complex-unions.metric'; + +describe('ComplexUnionsMetric', () => { + it('counts unions with more than 4 members', () => { + const code = ` + type U = 'a' | 'b' | 'c' | 'd' | 'e'; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexUnionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); + + it('does not count unions with 4 or fewer members', () => { + const code = ` + type U = 'a' | 'b' | 'c' | 'd'; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new ComplexUnionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.ts new file mode 100644 index 0000000..10a6d5a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/complex-unions.metric.ts @@ -0,0 +1,36 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + complexUnions: z.number().describe('Union types with > 4 members.'), + }), + ui: { + metrics: [ + { + path: 'complexUnions', + label: 'Complex Unions', + icon: 'category', + threshold: { warnGte: 1 }, + tooltip: 'Large unions increase complexity.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types', + }, + ], + }, +}) +export class ComplexUnionsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const visit = (node: ts.Node) => { + if (ts.isUnionTypeNode(node) && node.types.length > 4) count++; + ts.forEachChild(node, visit); + }; + visit(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.spec.ts new file mode 100644 index 0000000..938e8fd --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.spec.ts @@ -0,0 +1,43 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { MissingTypeGuardsMetric } from './missing-type-guards.metric'; + +describe('MissingTypeGuardsMetric', () => { + it('counts 1 for guard-like function lacking a type predicate but narrowing param', () => { + const code = ` + function isUser(x) { + return typeof x === 'object' && 'id' in x; + } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MissingTypeGuardsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); + + it('does not count proper type predicate guards', () => { + const code = ` + function isUser(x: any): x is { id: number } { + return typeof x === 'object' && 'id' in x; + } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new MissingTypeGuardsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.ts new file mode 100644 index 0000000..8448081 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/missing-type-guards.metric.ts @@ -0,0 +1,133 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + missingTypeGuards: z + .number() + .describe('Heuristic for missing user-defined type guards.'), + }), + ui: { + metrics: [ + { + path: 'missingTypeGuards', + label: 'Missing Type Guards', + icon: 'shield_moon', + threshold: { warnGte: 1 }, + tooltip: 'Prefer narrowing with guards.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/2/narrowing.html', + }, + ], + }, +}) +export class MissingTypeGuardsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + + const isBooleanKeyword = (t: ts.TypeNode | undefined): boolean => + !!t && t.kind === ts.SyntaxKind.BooleanKeyword; + const isTypePredicate = (t: ts.TypeNode | undefined): boolean => + !!t && ts.isTypePredicateNode(t); + const startsWithGuardName = (name: string | undefined): boolean => { + if (!name) return false; + const lower = name.toLowerCase(); + return ( + lower.startsWith('is') || + lower.startsWith('has') || + lower.startsWith('assert') + ); + }; + + const getCallableName = ( + node: ts.FunctionLikeDeclaration | ts.ArrowFunction + ): string | undefined => { + if ( + (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && + node.name + ) { + return node.name.getText(); + } + const parent = node.parent; + if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) { + return parent.name.text; + } + return undefined; + }; + + const hasParamNarrowingChecks = ( + fn: ts.FunctionLikeDeclarationBase | ts.ArrowFunction, + paramName: string + ): boolean => { + let seen = false; + const check = (node: ts.Node) => { + if (seen) return; + if (ts.isTypeOfExpression(node)) { + if ( + ts.isIdentifier(node.expression) && + node.expression.text === paramName + ) + seen = true; + } else if ( + ts.isBinaryExpression(node) && + node.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword + ) { + if (ts.isIdentifier(node.left) && node.left.text === paramName) + seen = true; + } else if ( + ts.isBinaryExpression(node) && + node.operatorToken.kind === ts.SyntaxKind.InKeyword + ) { + if (ts.isIdentifier(node.right) && node.right.text === paramName) + seen = true; + } + ts.forEachChild(node, check); + }; + if (fn.body) ts.forEachChild(fn.body, check); + return seen; + }; + + const visit = (n: ts.Node) => { + if ( + ts.isFunctionDeclaration(n) || + ts.isMethodDeclaration(n) || + ts.isFunctionExpression(n) || + ts.isArrowFunction(n) + ) { + const name = getCallableName( + n as ts.FunctionLikeDeclaration | ts.ArrowFunction + ); + const sig = n as ts.SignatureDeclarationBase; + const rType = sig.type; + const params = sig.parameters ?? []; + + if ( + startsWithGuardName(name) && + params.length === 1 && + !isTypePredicate(rType) && + (isBooleanKeyword(rType) || !rType) + ) { + const p = params[0]; + const pName = ts.isIdentifier(p.name) ? p.name.text : undefined; + if ( + pName && + hasParamNarrowingChecks( + n as ts.FunctionLikeDeclarationBase | ts.ArrowFunction, + pName + ) + ) { + count++; + } + } + } + ts.forEachChild(n, visit); + }; + + visit(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.spec.ts new file mode 100644 index 0000000..1c0bbb7 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.spec.ts @@ -0,0 +1,40 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { TypeAssertionsMetric } from './type-assertions.metric'; + +describe('TypeAssertionsMetric', () => { + it('counts type assertions (as / )', () => { + const code = ` + const a = (42 as unknown) as number; + const b = (42 as any); + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TypeAssertionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(4); + }); + + it('does not count when no assertions present', () => { + const code = ` + const a = 42; + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TypeAssertionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.ts new file mode 100644 index 0000000..ae2addc --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-assertions.metric.ts @@ -0,0 +1,37 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + typeAssertions: z.number().describe('Type assertions (as / expr).'), + }), + ui: { + metrics: [ + { + path: 'typeAssertions', + label: 'Type Assertions', + icon: 'build', + threshold: { warnGte: 3 }, + tooltip: 'Can hide errors.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions', + }, + ], + }, +}) +export class TypeAssertionsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const visit = (node: ts.Node) => { + if (ts.isTypeAssertionExpression(node) || ts.isAsExpression(node)) + count++; + ts.forEachChild(node, visit); + }; + visit(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.spec.ts new file mode 100644 index 0000000..849f58e --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.spec.ts @@ -0,0 +1,42 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { TypeDuplicationMetric } from './type-duplication.metric'; + +describe('TypeDuplicationMetric', () => { + it('counts duplicated type shapes', () => { + const code = ` + interface A { id: number; name: string } + type B = { name: string; id: number } + interface C { id: number; name: string } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TypeDuplicationMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(2); + }); + + it('does not count when shapes are unique', () => { + const code = ` + interface A { id: number; name: string } + interface B { id: number; title: string } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new TypeDuplicationMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.ts new file mode 100644 index 0000000..2031741 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/type-duplication.metric.ts @@ -0,0 +1,85 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + typeDuplication: z + .number() + .describe('Duplicate type shapes across codebase (heuristic).'), + }), + ui: { + metrics: [ + { + path: 'typeDuplication', + label: 'Type Duplication', + icon: 'content_copy', + threshold: { warnGte: 1 }, + tooltip: 'Prefer reusable utility types.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/utility-types.html', + }, + ], + }, +}) +export class TypeDuplicationMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + const shapeCounts = new Map(); + + const recordShape = (members: readonly ts.TypeElement[]) => { + const props: string[] = []; + for (const m of members) { + if (!ts.isPropertySignature(m)) continue; + const nameNode = m.name; + const name = ts.isIdentifier(nameNode) + ? nameNode.text + : ts.isStringLiteral(nameNode) + ? nameNode.text + : nameNode.getText(); + const optional = m.questionToken ? '?' : ''; + const typeText = m.type + ? m.type.getText().replace(/\s+/g, ' ').trim() + : 'any'; + props.push(`${name}${optional}:${typeText}`); + } + if (!props.length) return; + props.sort(); + const signature = props.join(';'); + shapeCounts.set(signature, (shapeCounts.get(signature) ?? 0) + 1); + }; + + const visit = (node: ts.Node) => { + if (ts.isInterfaceDeclaration(node)) { + recordShape(node.members); + } else if ( + ts.isTypeAliasDeclaration(node) && + ts.isTypeLiteralNode(node.type) + ) { + recordShape(node.type.members); + } else if ( + ts.isParameter(node) && + node.type && + ts.isTypeLiteralNode(node.type) + ) { + recordShape(node.type.members); + } else if ( + ts.isVariableDeclaration(node) && + node.type && + ts.isTypeLiteralNode(node.type) + ) { + recordShape(node.type.members); + } + ts.forEachChild(node, visit); + }; + visit(context.sourceFile); + + let duplicates = 0; + shapeCounts.forEach((cnt) => { + if (cnt > 1) duplicates += cnt - 1; + }); + return duplicates; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.spec.ts new file mode 100644 index 0000000..154043a --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.spec.ts @@ -0,0 +1,39 @@ +import * as ts from 'typescript'; + +import { createContext } from '../test-utils'; + +import { WeakTypeDefinitionsMetric } from './weak-type-definitions.metric'; + +describe('WeakTypeDefinitionsMetric', () => { + it('counts interfaces where all members are optional', () => { + const code = ` + interface W { a?: number; b?: string } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new WeakTypeDefinitionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(1); + }); + + it('does not count when at least one member is required', () => { + const code = ` + interface S { a?: number; b: string } + `; + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const ctx = createContext(sourceFile); + const metric = new WeakTypeDefinitionsMetric(); + const count = metric.analyze(ctx); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.ts new file mode 100644 index 0000000..4b82440 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/typescript/weak-type-definitions.metric.ts @@ -0,0 +1,44 @@ +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../../x-ray-metrics.types'; +import { BaseMetric } from '../metric.base'; +import { Metric } from '../metric.decorator'; + +@Metric({ + schema: z.object({ + weakTypeDefinitions: z + .number() + .describe('Interfaces with all-optional members.'), + }), + ui: { + metrics: [ + { + path: 'weakTypeDefinitions', + label: 'Weak Type Definitions', + icon: 'assignment_late', + threshold: { warnGte: 1 }, + tooltip: 'All-optional interfaces are weak.', + refLink: + 'https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html', + }, + ], + }, +}) +export class WeakTypeDefinitionsMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + let count = 0; + const visit = (node: ts.Node) => { + if (ts.isInterfaceDeclaration(node)) { + const optionalProps = node.members.filter( + (member) => ts.isPropertySignature(member) && member.questionToken + ).length; + if (optionalProps === node.members.length && node.members.length > 0) + count++; + } + ts.forEachChild(node, visit); + }; + visit(context.sourceFile); + return count; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.spec.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.spec.ts new file mode 100644 index 0000000..2f4405d --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.spec.ts @@ -0,0 +1,122 @@ +import ts from 'typescript'; + +import { UnusedMembersMetric } from './unused-members.metric'; + +describe('UnusedMembersMetric', () => { + const metric = new UnusedMembersMetric(); + + function buildContext(code: string) { + const sourceFile = ts.createSourceFile( + 'test.ts', + code, + ts.ScriptTarget.Latest, + true + ); + const classNode = sourceFile.statements.find(ts.isClassDeclaration); + if (!classNode) throw new Error('No class found'); + const program = ts.createProgram([], {}); + const checker = program.getTypeChecker(); + return { sourceFile, classNode, program, checker }; + } + + it('detects various unused private members', () => { + const code = ` + export class Test { + private unusedMethod() {} + private unusedProperty: string; + #unusedSuperPrivate() {} + #unusedSuperPrivateField: number; + + constructor(private unusedService: any, public publicService: any) {} + + public publicMethod() {} // should be ignored + } + `; + const { sourceFile, classNode, program, checker } = buildContext(code); + const count = metric.analyze({ + sourceFile, + program, + checker, + sourceCode: code, + scopeNode: classNode, + }); + expect(count).toBe(5); + }); + + it('detects unused members with usage patterns', () => { + const code = ` + class Test { + private used1() {} + private used2: string; + private unused1() {} + private unused2: string; + #usedSuper() {} + #unusedSuper() {} + + method() { + this.used1(); + console.log(this.used2); + this.#usedSuper(); + } + } + `; + const { sourceFile, classNode, program, checker } = buildContext(code); + const count = metric.analyze({ + sourceFile, + program, + checker, + sourceCode: code, + scopeNode: classNode, + }); + expect(count).toBe(3); + }); + + it('detects unused constructor parameter properties', () => { + const code = ` + class Test { + constructor(private unused: any) {} + } + `; + const { sourceFile, classNode, program, checker } = buildContext(code); + const count = metric.analyze({ + sourceFile, + program, + checker, + sourceCode: code, + scopeNode: classNode, + }); + expect(count).toBe(1); + }); + + it('finds no unused members when all are used or public', () => { + const code = ` + class Test { + private used1() {} + private used2: string; + #usedSuper() {} + + constructor(private service: any, public config: any) {} + + public publicMethod() {} + publicField: string; + + method() { + this.used1(); + this.used2 = 'value'; + this.#usedSuper(); + this.service.call(); + this.config.get(); + } + } + `; + const { sourceFile, classNode, program, checker } = buildContext(code); + const count = metric.analyze({ + sourceFile, + program, + checker, + sourceCode: code, + scopeNode: classNode, + }); + expect(count).toBe(0); + }); +}); diff --git a/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.ts b/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.ts new file mode 100644 index 0000000..4bef7b7 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.ts @@ -0,0 +1,159 @@ +import { + createWrappedNode, + ClassDeclaration as MorphClassDeclaration, + Node as MorphNode, + SyntaxKind, + ts as tsMorph, +} from 'ts-morph'; +import * as ts from 'typescript'; +import { z } from 'zod'; + +import { AnalyzerContext } from '../x-ray-metrics.types'; + +import { BaseMetric } from './metric.base'; +import { Metric } from './metric.decorator'; + +@Metric({ + schema: z.object({ + unusedMembers: z + .number() + .describe( + 'Count of private and super private (#) members (including constructor parameter properties) unused within the class.' + ), + }), + ui: { + metrics: [ + { + path: 'unusedMembers', + label: 'Unused', + icon: 'remove_circle_outline', + threshold: { warnGte: 1 }, + tooltip: + 'Private and super private (#) members (including constructor parameter properties) unused within the class.', + refLink: 'https://refactoring.guru/smells/dead-code', + }, + ], + }, +}) +export class UnusedMembersMetric extends BaseMetric { + analyze(context: AnalyzerContext): number { + const targetNode = context.scopeNode; + if (!targetNode || !ts.isClassDeclaration(targetNode)) { + return 0; + } + + const classDecl = createWrappedNode( + targetNode as unknown as tsMorph.Node + ).asKindOrThrow(SyntaxKind.ClassDeclaration); + + const privateMembers = this.getPrivateMembers(classDecl); + const usedMembers = this.getUsedMemberNames(classDecl); + + const unusedMembers = privateMembers + .filter((member) => !usedMembers.has(member.name)) + .map((member) => member.name); + + return unusedMembers.length; + } + + private getPrivateMembers(classDecl: MorphClassDeclaration): Array<{ + name: string; + isPrivate: boolean; + isSuperPrivate: boolean; + }> { + const members: Array<{ + name: string; + isPrivate: boolean; + isSuperPrivate: boolean; + }> = []; + + classDecl.getConstructors().forEach((constructor) => { + constructor.getParameters().forEach((param) => { + if (param.hasModifier('private')) { + members.push({ + name: param.getName(), + isPrivate: true, + isSuperPrivate: false, + }); + } + }); + }); + + const privateMethods = classDecl + .getMethods() + .filter((m) => m.hasModifier('private')); + const privateProperties = classDecl + .getProperties() + .filter((p) => p.hasModifier('private')); + const privateGetters = classDecl + .getGetAccessors() + .filter((a) => a.hasModifier('private')); + const privateSetters = classDecl + .getSetAccessors() + .filter((a) => a.hasModifier('private')); + + [ + ...privateMethods, + ...privateProperties, + ...privateGetters, + ...privateSetters, + ].forEach((member) => { + members.push({ + name: member.getName(), + isPrivate: true, + isSuperPrivate: false, + }); + }); + + [ + ...classDecl.getMethods(), + ...classDecl.getProperties(), + ...classDecl.getGetAccessors(), + ...classDecl.getSetAccessors(), + ].forEach((member) => { + const name = member.getName(); + if (name.startsWith('#')) { + members.push({ + name, + isPrivate: true, + isSuperPrivate: true, + }); + } + }); + + return members; + } + + private getUsedMemberNames(classDecl: MorphClassDeclaration): Set { + const usedMembers = new Set(); + + classDecl.forEachDescendant((node) => { + if (MorphNode.isPropertyAccessExpression(node)) { + const expression = node.getExpression(); + if (MorphNode.isThisExpression(expression)) { + usedMembers.add(node.getName()); + } + } + + if (MorphNode.isElementAccessExpression(node)) { + const expression = node.getExpression(); + const argument = node.getArgumentExpression(); + if ( + MorphNode.isThisExpression(expression) && + MorphNode.isStringLiteral(argument) + ) { + usedMembers.add(argument.getLiteralValue()); + } + } + + if (MorphNode.isCallExpression(node)) { + const expression = node.getExpression(); + if (MorphNode.isIdentifier(expression)) { + usedMembers.add(expression.getText()); + } + } + }); + + return usedMembers; + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/organization-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/organization-metrics-analyzer.ts new file mode 100644 index 0000000..4dd12c3 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/organization-metrics-analyzer.ts @@ -0,0 +1,59 @@ +import { z } from 'zod'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { ComplexAlgorithmsMetric } from './metrics/organization/complex-algorithms.metric'; +import { FeatureEnvyMetric } from './metrics/organization/feature-envy.metric'; +import { MiddleManMetric } from './metrics/organization/middle-man.metric'; +import { TemporalCouplingMetric } from './metrics/organization/temporal-coupling.metric'; +import { + Dictionaries, + QualityGroupFragment, + MetricItem, +} from './ui-schema.types'; +import { OrganizationMetrics } from './x-ray-metrics.types'; + +export class OrganizationMetricsAnalyzer extends BaseMetricsAnalyzer { + static getMeta() { + const metaParts = [ + FeatureEnvyMetric.getMetadata(), + MiddleManMetric.getMetadata(), + ComplexAlgorithmsMetric.getMetadata(), + TemporalCouplingMetric.getMetadata(), + ]; + const schema = metaParts.reduce( + (acc, m) => acc.merge(m.schema), + z.object({}) + ); + const metrics: MetricItem[] = metaParts.flatMap( + (m) => m.ui.metrics as unknown as MetricItem[] + ); + return { + schema, + ui: { + kind: 'quality-group', + id: 'organization', + title: 'Code Organization', + path: 'metrics.organization', + refLink: 'https://refactoring.guru/smells/feature-envy', + metrics, + } as QualityGroupFragment, + dictionaries: {} as Dictionaries, + } as const; + } + async analyze(): Promise<{ organization: OrganizationMetrics }> { + const featureEnvy = new FeatureEnvyMetric().analyze(this.context); + const middleManClasses = new MiddleManMetric().analyze(this.context); + const complexAlgorithms = new ComplexAlgorithmsMetric().analyze( + this.context + ); + const temporalCoupling = new TemporalCouplingMetric().analyze(this.context); + return Promise.resolve({ + organization: { + featureEnvy, + middleManClasses, + complexAlgorithms, + temporalCoupling, + }, + }); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/typescript-metrics-analyzer.ts b/apps/backend/src/services/trend-analysis/x-ray/typescript-metrics-analyzer.ts new file mode 100644 index 0000000..fc9861b --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/typescript-metrics-analyzer.ts @@ -0,0 +1,66 @@ +import { z } from 'zod'; + +import { BaseMetricsAnalyzer } from './base-metrics-analyzer'; +import { AnyTypesMetric } from './metrics/typescript/any-types.metric'; +import { ComplexUnionsMetric } from './metrics/typescript/complex-unions.metric'; +import { MissingTypeGuardsMetric } from './metrics/typescript/missing-type-guards.metric'; +import { TypeAssertionsMetric } from './metrics/typescript/type-assertions.metric'; +import { TypeDuplicationMetric } from './metrics/typescript/type-duplication.metric'; +import { WeakTypeDefinitionsMetric } from './metrics/typescript/weak-type-definitions.metric'; +import { TypeScriptMetrics } from './x-ray-metrics.types'; + +export class TypeScriptMetricsAnalyzer extends BaseMetricsAnalyzer { + static getMeta() { + const metaParts = [ + AnyTypesMetric.getMetadata(), + TypeAssertionsMetric.getMetadata(), + ComplexUnionsMetric.getMetadata(), + MissingTypeGuardsMetric.getMetadata(), + WeakTypeDefinitionsMetric.getMetadata(), + TypeDuplicationMetric.getMetadata(), + ]; + const schema = metaParts.reduce( + (acc, m) => acc.merge(m.schema), + z.object({}) + ); + const metrics: import('./ui-schema.types').MetricItem[] = metaParts.flatMap( + (m) => m.ui.metrics as unknown as import('./ui-schema.types').MetricItem[] + ); + return { + schema, + ui: { + kind: 'quality-group', + id: 'typescript', + title: 'TypeScript Quality', + path: 'metrics.typescript', + refLink: + 'https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html', + metrics, + }, + dictionaries: {}, + } as const; + } + async analyze(): Promise<{ typescript: TypeScriptMetrics }> { + const anyTypes = new AnyTypesMetric().analyze(this.context); + const typeAssertions = new TypeAssertionsMetric().analyze(this.context); + const complexUnions = new ComplexUnionsMetric().analyze(this.context); + const missingTypeGuards = new MissingTypeGuardsMetric().analyze( + this.context + ); + const weakTypeDefinitions = new WeakTypeDefinitionsMetric().analyze( + this.context + ); + const typeDuplication = new TypeDuplicationMetric().analyze(this.context); + + return Promise.resolve({ + typescript: { + anyTypes, + typeAssertions, + complexUnions, + missingTypeGuards, + weakTypeDefinitions, + typeDuplication, + }, + }); + } +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/ui-schema.types.ts b/apps/backend/src/services/trend-analysis/x-ray/ui-schema.types.ts new file mode 100644 index 0000000..8156f70 --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/ui-schema.types.ts @@ -0,0 +1,85 @@ +export type Threshold = { + warnGte?: number; + warnGt?: number; + warnLte?: number; + warnLt?: number; +}; + +export interface MetricItem { + path: string; + label: string; + icon?: string; + tooltip?: string; + threshold?: Threshold; + refLink?: string; +} + +export interface BadgeItem { + label: string; + class?: 'warning' | 'info' | 'success' | 'neutral'; + if?: string; +} + +export interface ChipsSection { + type: 'chips'; + title: string; + path: string; + iconMapRef?: string; + tooltipMapRef?: string; +} + +export interface ListSection { + type: 'list'; + title: string; + path: string; + icon?: string; + format?: 'fileName' | 'none'; +} + +export interface DuplicatesSection { + type: 'duplicates'; + title: string; + path: string; +} + +export type Section = ChipsSection | ListSection | DuplicatesSection; + +export interface TabFragment { + kind?: 'tab'; + id: string; + title: string; + icon?: string; + collection?: string; + itemTitle?: string; + badges?: BadgeItem[]; + metrics?: MetricItem[]; + sections?: Section[]; + hideIfEmpty?: boolean; +} + +export interface QualityGroupFragment { + kind?: 'quality-group'; + id: string; + title: string; + path: string; + refLink?: string; + metrics: MetricItem[]; +} + +export interface QualityTab { + id: 'code-quality'; + title: string; + icon?: string; + groups: QualityGroupFragment[]; +} + +export interface Dictionaries { + [dictName: string]: Record; +} + +export interface UISchema { + version: number; + tabs: Array; + dictionaries?: Dictionaries; + docsUrl?: string; +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/x-ray-metrics.types.ts b/apps/backend/src/services/trend-analysis/x-ray/x-ray-metrics.types.ts new file mode 100644 index 0000000..4dbcffa --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/x-ray-metrics.types.ts @@ -0,0 +1,80 @@ +import * as ts from 'typescript'; + +export interface MethodMetrics { + lines: number; + parameters: number; + cyclomaticComplexity: number; + nestedConditions: number; + hasComments: boolean; + responsibilities: number; + className?: string; + responsibilitiesCategories?: string[]; + writesThis?: boolean; + mutatesParams?: boolean; +} + +export interface ClassMetrics { + methods: number; + fields: number; + dependencies: number; + reasonsToChange: number; + cyclomaticComplexity: number; + fileComplexity: number; + reasonsCategories?: string[]; + externalPackages?: string[]; + layerCrossing?: boolean; + internalFiles?: string[]; + isGodClass: boolean; + onlyGettersSetters: boolean; + unusedMembers: number; + duplicateBlocks: number; + duplicateDetails?: Array<{ + location: string; + count: number; + severity: 'LOW' | 'MEDIUM' | 'HIGH'; + }>; +} + +export interface DataStructureMetrics { + publicFields: number; + magicNumbers: number; + nullChecks: number; + complexDataPassing: number; + arrayMixedMeanings: number; +} + +export interface OrganizationMetrics { + featureEnvy: number; + middleManClasses: number; + complexAlgorithms: number; + temporalCoupling: number; +} + +export interface TypeScriptMetrics { + anyTypes: number; + typeAssertions: number; + complexUnions: number; + missingTypeGuards: number; + weakTypeDefinitions: number; + typeDuplication: number; +} + +export interface CodeMetrics { + file: string; + metrics: { + methodLevel: Record; + classLevel: Record; + dataStructure: DataStructureMetrics; + organization: OrganizationMetrics; + typescript: TypeScriptMetrics; + }; + sourceCode?: string; +} + +export interface AnalyzerContext { + sourceFile: ts.SourceFile; + checker: ts.TypeChecker; + program: ts.Program; + sourceCode: string; + scopeNode?: ts.Node; +} diff --git a/apps/backend/src/services/trend-analysis/x-ray/x-ray.schema.ts b/apps/backend/src/services/trend-analysis/x-ray/x-ray.schema.ts new file mode 100644 index 0000000..93d117e --- /dev/null +++ b/apps/backend/src/services/trend-analysis/x-ray/x-ray.schema.ts @@ -0,0 +1,164 @@ +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; + +import { ClassMetricsAnalyzer } from './class-metrics-analyzer'; +import { DataStructureMetricsAnalyzer } from './data-structure-metrics-analyzer'; +import { MethodMetricsAnalyzer } from './method-metrics-analyzer'; +import { OrganizationMetricsAnalyzer } from './organization-metrics-analyzer'; +import { TypeScriptMetricsAnalyzer } from './typescript-metrics-analyzer'; +import { + UISchema, + TabFragment, + QualityTab, + QualityGroupFragment, + BadgeItem, + MetricItem, + Section, +} from './ui-schema.types'; + +// Note: Metric Zod schemas live in analyzers; avoid duplication here. + +type AnalyzerDictionaries = Record>; + +type ReadonlyTabFragment = Omit< + TabFragment, + 'badges' | 'metrics' | 'sections' +> & { + badges?: ReadonlyArray; + metrics?: ReadonlyArray; + sections?: ReadonlyArray
; +}; + +type ReadonlyQualityGroupFragment = Omit & { + metrics: ReadonlyArray; +}; + +type AnalyzerMeta = { + schema: z.ZodTypeAny; + ui: ReadonlyTabFragment | ReadonlyQualityGroupFragment; + dictionaries?: AnalyzerDictionaries; +}; +type AnalyzerClass = { getMeta(): AnalyzerMeta }; + +const ANALYZERS: AnalyzerClass[] = [ + MethodMetricsAnalyzer, + ClassMetricsAnalyzer, + DataStructureMetricsAnalyzer, + OrganizationMetricsAnalyzer, + TypeScriptMetricsAnalyzer, +]; + +export type XRaySchemaResponse = { + version: number; + jsonSchema: unknown; + uiSchema: UISchema; +}; + +export function buildBaseXRaySchema(): XRaySchemaResponse { + const jsonSchema = buildXRayJSONSchemaFromAnalyzers(); + const uiSchema: UISchema = { version: 1, tabs: [] }; + return { version: 1, jsonSchema, uiSchema }; +} + +// Compose UI schema from analyzer-provided fragments +export function buildXRayUISchemaFromAnalyzers(): UISchema { + const metas = ANALYZERS.map((a) => a.getMeta()); + const tabs: TabFragment[] = metas + .filter((m): m is AnalyzerMeta & { ui: ReadonlyTabFragment } => { + const ui = m.ui as Partial; + return ( + typeof ui === 'object' && + ui !== null && + typeof ui.collection === 'string' + ); + }) + .map((m) => { + const ui = m.ui; + return { + ...ui, + badges: ui.badges ? [...ui.badges] : undefined, + metrics: ui.metrics ? [...ui.metrics] : undefined, + sections: ui.sections ? [...ui.sections] : undefined, + } satisfies TabFragment; + }); + const groups: QualityGroupFragment[] = metas + .filter((m): m is AnalyzerMeta & { ui: ReadonlyQualityGroupFragment } => { + const ui = m.ui as Partial< + ReadonlyQualityGroupFragment & ReadonlyTabFragment + >; + return ( + typeof ui === 'object' && + ui !== null && + typeof ui.path === 'string' && + (ui as Partial).collection === undefined + ); + }) + .map((m) => { + const ui = m.ui; + return { + ...ui, + metrics: [...ui.metrics], + } satisfies QualityGroupFragment; + }); + + const qualityTab: QualityTab = { + id: 'code-quality', + title: 'Code Quality', + icon: 'code', + groups, + }; + + // Merge analyzer-provided dictionaries to avoid duplication + const dictionariesList: AnalyzerDictionaries[] = metas + .map((m) => m.dictionaries) + .filter((d): d is AnalyzerDictionaries => !!d); + + const dictionaries = dictionariesList.reduce( + (acc: Record>, dict) => { + for (const [dictName, map] of Object.entries(dict)) { + acc[dictName] = { ...(acc[dictName] || {}), ...map }; + } + return acc; + }, + {} as Record> + ); + + return { + version: 1, + tabs: [...tabs, qualityTab], + dictionaries, + }; +} + +// Compose JSON schema by combining analyzer Zod schemas into the full result shape +export function buildXRayJSONSchemaFromAnalyzers() { + const metas = ANALYZERS.map((a) => a.getMeta()); + // Build metrics object by inferring keys from UI fragments + const metricsShape: Record = {}; + metas.forEach((m) => { + const ui = m.ui; + if ('collection' in ui && typeof ui.collection === 'string') { + const key = ui.collection.split('.').pop() as string; + metricsShape[key] = z.record(m.schema); + } else if ('path' in ui && typeof ui.path === 'string') { + const key = ui.path.split('.').pop() as string; + metricsShape[key] = m.schema; + } + }); + + const Metrics = z.object(metricsShape); + + const XRay = z.object({ + file: z.string(), + metrics: Metrics, + sourceCode: z.string().optional(), + }); + + const json = zodToJsonSchema(XRay, { + target: 'jsonSchema7', + $refStrategy: 'none', + }) as Record & { ['x-ui']?: UISchema }; + // Embed UI config into JSON Schema to avoid separate schemas/types on the frontend + json['x-ui'] = buildXRayUISchemaFromAnalyzers(); + return json; +} diff --git a/apps/backend/src/utils/complexity.ts b/apps/backend/src/utils/complexity.ts index bd34ad9..cf95fa3 100644 --- a/apps/backend/src/utils/complexity.ts +++ b/apps/backend/src/utils/complexity.ts @@ -7,38 +7,39 @@ export function calcCyclomaticComplexity(fileName: string): number { return calcComplexityForCode(code); } -function calcComplexityForCode(sourceCode: string): number { +export function calcComplexityForCode(sourceCode: string): number { const sourceFile = ts.createSourceFile( 'temp.ts', sourceCode, ts.ScriptTarget.Latest, true ); + return calcComplexityForNode(sourceFile); +} +// Calculates cyclomatic complexity for a specific AST node +// Mirrors the logic used for method-level complexity across analyzers +export function calcComplexityForNode(node: ts.Node): number { let complexity = 1; - function visit(node: ts.Node) { - switch (node.kind) { + const visit = (n: ts.Node) => { + switch (n.kind) { case ts.SyntaxKind.IfStatement: - case ts.SyntaxKind.ConditionalExpression: + case ts.SyntaxKind.WhileStatement: + case ts.SyntaxKind.DoStatement: case ts.SyntaxKind.ForStatement: case ts.SyntaxKind.ForInStatement: case ts.SyntaxKind.ForOfStatement: - case ts.SyntaxKind.WhileStatement: - case ts.SyntaxKind.DoStatement: - case ts.SyntaxKind.CaseClause: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.MethodDeclaration: - case ts.SyntaxKind.Constructor: - case ts.SyntaxKind.ArrowFunction: - case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.SwitchStatement: case ts.SyntaxKind.CatchClause: + case ts.SyntaxKind.ConditionalExpression: + case ts.SyntaxKind.CaseClause: complexity++; break; } - ts.forEachChild(node, visit); - } + ts.forEachChild(n, visit); + }; - visit(sourceFile); + visit(node); return complexity; } diff --git a/apps/frontend/project.json b/apps/frontend/project.json index 9d17fc0..e5944c2 100644 --- a/apps/frontend/project.json +++ b/apps/frontend/project.json @@ -48,7 +48,8 @@ "serve": { "executor": "@angular-devkit/build-angular:dev-server", "options": { - "proxyConfig": "apps/frontend/proxy.conf.json" + "proxyConfig": "apps/frontend/proxy.conf.json", + "port": 4300 }, "configurations": { "production": { @@ -80,7 +81,7 @@ "executor": "@nx/web:file-server", "options": { "buildTarget": "frontend:build", - "port": 4200, + "port": 4300, "staticFilePath": "dist/apps/frontend/browser", "spa": true } diff --git a/apps/frontend/src/app/app.routes.ts b/apps/frontend/src/app/app.routes.ts index 51f15e9..a96cdd3 100644 --- a/apps/frontend/src/app/app.routes.ts +++ b/apps/frontend/src/app/app.routes.ts @@ -2,7 +2,9 @@ import { Routes } from '@angular/router'; import { CouplingComponent } from './features/coupling/coupling.component'; import { HotspotComponent } from './features/hotspot/hotspot.component'; +import { HotspotCityComponent } from './features/hotspot-city/hotspot-city.component'; import { TeamAlignmentComponent } from './features/team-alignment/team-alignment.component'; +import { TrendAnalysisComponent } from './features/trend-analysis/trend-analysis.component'; import { GraphTypeData } from './model/graph-type'; import { AboutComponent } from './shell/about/about.component'; import { ensureCache } from './shell/cache.guard'; @@ -36,6 +38,10 @@ export const routes: Routes = [ path: 'hotspots', component: HotspotComponent, }, + { + path: 'hotspot-city', + component: HotspotCityComponent, + }, { path: 'change-coupling', component: CouplingComponent, @@ -43,6 +49,10 @@ export const routes: Routes = [ type: 'changes', } as GraphTypeData, }, + { + path: 'trend-analysis', + component: TrendAnalysisComponent, + }, ], }, ]; diff --git a/apps/frontend/src/app/data/trend-analysis.store.ts b/apps/frontend/src/app/data/trend-analysis.store.ts new file mode 100644 index 0000000..9739e3c --- /dev/null +++ b/apps/frontend/src/app/data/trend-analysis.store.ts @@ -0,0 +1,266 @@ +import { HttpClient } from '@angular/common/http'; +import { computed, inject } from '@angular/core'; +import { + patchState, + signalStore, + withComputed, + withMethods, + withState, +} from '@ngrx/signals'; +import { rxMethod } from '@ngrx/signals/rxjs-interop'; +import { pipe, switchMap, catchError, of, Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { + TrendAnalysisResult, + TrendAnalysisProgress, + FileTrend, +} from '../model/trend-analysis-result'; + +interface TrendAnalysisState { + result: TrendAnalysisResult | null; + progress: TrendAnalysisProgress | null; + isAnalyzing: boolean; + error: string | null; +} + +const initialState: TrendAnalysisState = { + result: null, + progress: null, + isAnalyzing: false, + error: null, +}; + +export const TrendAnalysisStore = signalStore( + { providedIn: 'root', protectedState: false }, + withState(initialState), + withComputed((store) => ({ + // Computed selectors for derived state + hasResult: computed(() => !!store.result()?.files?.length), + totalFiles: computed(() => store.result()?.files?.length || 0), + filesAnalyzed: computed(() => store.progress()?.filesAnalyzed || 0), + commitsProcessed: computed(() => store.progress()?.commitsProcessed || 0), + isComplete: computed( + () => + store.progress()?.type === 'complete' || + store.progress()?.type === 'final_result' + ), + })), + withMethods((store, http = inject(HttpClient)) => ({ + // Load trend analysis (one-time fetch) + loadTrendAnalysis: rxMethod( + pipe( + tap(() => patchState(store, { isAnalyzing: true, error: null })), + switchMap((maxCommits) => { + const url = `/api/trend-analysis?maxCommits=${maxCommits}`; + return http.get(url).pipe( + tap((result) => { + patchState(store, { + result, + isAnalyzing: false, + progress: null, + }); + }), + catchError((error) => { + patchState(store, { + error: error.message, + isAnalyzing: false, + }); + return of(null); + }) + ); + }) + ) + ), + + // Start streaming analysis with custom Observable wrapper + startStreamingAnalysis: rxMethod( + pipe( + tap(() => + patchState(store, { + isAnalyzing: true, + progress: null, + result: null, + error: null, + }) + ), + switchMap((maxCommits) => { + return createSSEObservable( + `/api/trend-analysis/stream?maxCommits=${maxCommits}` + ).pipe( + tap((progress) => { + console.log( + 'SSE received event:', + progress.type, + progress.message + ); + patchState(store, { progress }); + + if (progress.type === 'initial_files' && progress.data) { + console.log( + 'SSE: Processing initial_files event with', + progress.data.files?.length || 0, + 'files' + ); + patchState(store, { result: progress.data }); + } else if (progress.type === 'commit_complete' && progress.data) { + console.log( + 'SSE: Processing commit_complete event - incremental update' + ); + const currentResult = store.result(); + if (currentResult) { + const updatedResult = updateIncrementalData( + currentResult, + progress.data + ); + patchState(store, { result: updatedResult }); + } + } else if (progress.type === 'final_result' && progress.data) { + console.log('SSE: Processing final_result event with data'); + patchState(store, { + result: progress.data, + isAnalyzing: false, + }); + } else if (progress.type === 'error') { + console.log('SSE: Processing error event'); + patchState(store, { + error: progress.message || 'Analysis failed', + isAnalyzing: false, + }); + } + }), + catchError((error) => { + patchState(store, { + error: error.message, + isAnalyzing: false, + }); + return of(null); + }) + ); + }) + ) + ), + + // Stop analysis + stopAnalysis: () => { + patchState(store, { + isAnalyzing: false, + progress: null, + }); + }, + + // Clear result + clearResult: () => { + patchState(store, { + result: null, + progress: null, + error: null, + }); + }, + })) +); + +// Custom Observable wrapper for SSE handling +function createSSEObservable(url: string): Observable { + return new Observable((subscriber) => { + const eventSource = new EventSource(url); + + eventSource.onmessage = (event) => { + try { + const progress: TrendAnalysisProgress = JSON.parse(event.data); + subscriber.next(progress); + + // Close the connection and complete the observable on final result or error + if (progress.type === 'final_result' || progress.type === 'error') { + eventSource.close(); + subscriber.complete(); + } + } catch (error) { + console.error('Error parsing SSE data:', error); + subscriber.error(error); + } + }; + + eventSource.onerror = (error) => { + console.error('SSE connection error:', error); + eventSource.close(); + subscriber.error(error); + }; + + // Cleanup function + return () => { + eventSource.close(); + }; + }); +} + +// Helper function for incremental data updates +function updateIncrementalData( + currentResult: TrendAnalysisResult, + incrementalData: Partial & { + commitsAnalyzed?: number; + filesAnalyzed?: number; + totalProcessingTimeMs?: number; + } +): TrendAnalysisResult { + if (!incrementalData) return currentResult; + + console.log( + 'Updating incremental data - processing trend updates for', + incrementalData.files?.length || 0, + 'files' + ); + + const updatedFiles = updateFilesWithTrendData( + currentResult.files, + incrementalData.files || [] + ); + const updatedSummary = updateSummaryData( + currentResult.summary, + incrementalData + ); + + const updatedResult = { + files: updatedFiles, + summary: updatedSummary, + }; + + console.log( + 'Updated result with incremental data - files with trends:', + incrementalData.files?.length || 0 + ); + return updatedResult; +} + +function updateFilesWithTrendData( + currentFiles: FileTrend[], + fileTrends: FileTrend[] +) { + return currentFiles.map((file) => { + const trendData = fileTrends.find( + (trend) => trend.filePath === file.filePath + ); + return trendData || file; // Use trend data if available, otherwise keep original + }); +} + +function updateSummaryData( + currentSummary: TrendAnalysisResult['summary'], + incrementalData: Partial & { + commitsAnalyzed?: number; + filesAnalyzed?: number; + totalProcessingTimeMs?: number; + } +) { + return { + totalProcessingTimeMs: + incrementalData.totalProcessingTimeMs || + currentSummary?.totalProcessingTimeMs || + 0, + commitsAnalyzed: + incrementalData.commitsAnalyzed || currentSummary?.commitsAnalyzed || 0, + filesAnalyzed: + incrementalData.filesAnalyzed || currentSummary?.filesAnalyzed || 0, + commitHashes: currentSummary?.commitHashes || [], + }; +} diff --git a/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.html b/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.html new file mode 100644 index 0000000..558073c --- /dev/null +++ b/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.html @@ -0,0 +1 @@ +
diff --git a/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.ts b/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.ts new file mode 100644 index 0000000..f1d8d8c --- /dev/null +++ b/apps/frontend/src/app/features/coupling/chord-coupling-renderer.component.ts @@ -0,0 +1,255 @@ +import { CommonModule } from '@angular/common'; +import { + Component, + ElementRef, + input, + OnDestroy, + viewChild, + afterNextRender, + effect, +} from '@angular/core'; +import * as echarts from 'echarts'; + +import { CouplingResult } from '../../model/coupling-result'; +import { GraphType } from '../../model/graph-type'; + +@Component({ + selector: 'app-chord-coupling-renderer', + standalone: true, + imports: [CommonModule], + templateUrl: './chord-coupling-renderer.component.html', + styles: [ + ` + :host { + display: block; + } + `, + ` + .chord-container { + width: 100%; + height: 75vh; + min-height: 300px; + } + `, + ], +}) +export class ChordCouplingRendererComponent implements OnDestroy { + result = input.required(); + type = input.required(); + minConnections = input.required(); + + containerRef = viewChild.required>('container'); + + private chart: echarts.ECharts | null = null; + private resizeObserver: ResizeObserver | null = null; + private zoomScale = 2.0; // start at max zoom + private readonly baseInnerRadiusPct = 40; + private readonly baseOuterRadiusPct = 58; + + constructor() { + afterNextRender(() => this.ensureChartInitialized()); + + effect(() => { + // react to input changes + this.result(); + this.type(); + this.minConnections(); + + if (this.chart) { + this.chart.setOption(this.createOption()); + this.chart.resize(); + } + }); + } + + private ensureChartInitialized(): void { + const container = this.containerRef().nativeElement; + if (container.clientWidth === 0 || container.clientHeight === 0) { + this.observeUntilVisible(container); + return; + } + if (!this.chart) { + this.chart = echarts.init(container); + window.addEventListener('resize', this.onResize); + this.chart.setOption(this.createOption()); + this.chart.resize(); + } + } + + private onResize = () => { + if (this.chart) { + this.chart.resize(); + } + }; + + ngOnDestroy(): void { + window.removeEventListener('resize', this.onResize); + // nothing to clean on container anymore (no listeners) + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + if (this.chart) { + this.chart.dispose(); + this.chart = null; + } + } + + private observeUntilVisible(container: HTMLDivElement): void { + if (this.resizeObserver) { + return; + } + this.resizeObserver = new ResizeObserver(() => { + if (container.clientWidth > 0 && container.clientHeight > 0) { + this.resizeObserver?.disconnect(); + this.resizeObserver = null; + this.chart = echarts.init(container); + window.addEventListener('resize', this.onResize); + this.chart.setOption(this.createOption()); + this.chart.resize(); + } + }); + this.resizeObserver.observe(container); + } + + private createOption(): echarts.EChartsOption { + const result = this.result(); + const type = this.type(); + const minConnections = this.minConnections(); + + const links: Array<{ source: string; target: string; value: number }> = []; + const hasConnection: boolean[] = new Array(result.dimensions.length).fill( + false + ); + const delimiter = type === 'structure' ? '→' : '↔'; + + for (let i = 0; i < result.matrix.length; i++) { + for (let j = 0; j < result.matrix.length; j++) { + const value = result.matrix[i][j]; + if (i !== j && value >= minConnections) { + links.push({ + source: result.dimensions[i], + target: result.dimensions[j], + value, + }); + hasConnection[i] = true; + hasConnection[j] = true; + } + } + } + + const data = result.dimensions + .filter((_, idx) => hasConnection[idx]) + .map((dimension) => ({ name: dimension })); + + const [inner, outer] = this.computeRadii(); + const chordSeries = { + type: 'chord', + radius: [`${inner}%`, `${outer}%`], + center: ['50%', '50%'], + padding: 3, + data, + links, + lineStyle: { + color: 'gradient', + width: 1, + }, + emphasis: { + lineStyle: { width: 2 }, + }, + label: { + show: true, + formatter: (item: { name: string }) => lastSegment(item.name), + }, + } as echarts.SeriesOption; + + const formatterFn = (params: unknown): string => { + if (Array.isArray(params)) { + return ''; + } + const p = params as { + dataType?: string; + name?: string; + data?: { source?: string; target?: string; value?: number }; + }; + if (p.dataType === 'node' && typeof p.name === 'string') { + const idx = result.dimensions.indexOf(p.name); + const label = p.name; + const soc = Array.isArray(result.sumOfCoupling) + ? (result.sumOfCoupling[idx] as number | undefined) + : undefined; + const relSoc = + soc !== undefined + ? Math.round(((soc as number) / result.fileCount[idx]) * 100) + : undefined; + return type === 'structure' + ? `${label}

${result.fileCount[idx]} source files` + + `
Cohesion: ${result.cohesion[idx]}%` + + `
Outgoing Deps: ${sumRow(result.matrix, idx)}` + + `
Incoming Deps: ${sumCol(result.matrix, idx)}` + : `${label}

${result.fileCount[idx]} commits` + + `
Sum of Coupling (SoC): ${soc ?? '-'} ` + + `
SoC per Commit: ${relSoc ?? '-'}%`; + } + if (p.dataType === 'edge' && p.data) { + const d = p.data as { + source?: string; + target?: string; + value?: number; + }; + const s = d.source ?? ''; + const t = d.target ?? ''; + const v = d.value ?? 0; + return `${lastSegment(s)} ${delimiter} ${lastSegment( + t + )}

${v} connections`; + } + return ''; + }; + + return { + tooltip: { + trigger: 'item', + formatter: formatterFn as never, + }, + animation: true, + series: [chordSeries], + } satisfies echarts.EChartsOption; + } + + private computeRadii(): [number, number] { + const inner = this.baseInnerRadiusPct * this.zoomScale; + const outer = this.baseOuterRadiusPct * this.zoomScale; + const clamp = (n: number, lo: number, hi: number) => + Math.max(lo, Math.min(hi, n)); + const ci = clamp(Number(inner.toFixed(2)), 6, 92); + const co = clamp(Number(outer.toFixed(2)), 10, 96); + const adjustedOuter = Math.max(co, ci + 6); + return [ci, adjustedOuter]; + } +} + +function lastSegment(path: string): string { + const parts = path.split('/'); + return parts[parts.length - 1] || path; +} + +function sumRow(matrix: number[][], nodeIndex: number): number { + let sum = 0; + for (let i = 0; i < matrix.length; i++) { + if (i !== nodeIndex) { + sum += matrix[nodeIndex][i]; + } + } + return sum; +} + +function sumCol(matrix: number[][], nodeIndex: number): number { + let sum = 0; + for (let i = 0; i < matrix.length; i++) { + if (i !== nodeIndex) { + sum += matrix[i][nodeIndex]; + } + } + return sum; +} diff --git a/apps/frontend/src/app/features/coupling/coupling.component.html b/apps/frontend/src/app/features/coupling/coupling.component.html index 5bc6530..6ad8177 100644 --- a/apps/frontend/src/app/features/coupling/coupling.component.html +++ b/apps/frontend/src/app/features/coupling/coupling.component.html @@ -1,11 +1,19 @@ -
+
+ Chord Diagram + @if (!showChordDiagram()) { Group by folder + } Min. Connections @@ -13,6 +21,7 @@ type="number" matInput min="1" + placeholder="Min. Connections" [ngModel]="minConnections()" (ngModelChange)="updateFilter({ minConnections: $event })" /> @@ -36,4 +45,14 @@ }
- +
+ @if (!showChordDiagram()) { + + } @else { + + } +
diff --git a/apps/frontend/src/app/features/coupling/coupling.component.ts b/apps/frontend/src/app/features/coupling/coupling.component.ts index 5a006e8..0a2a8af 100644 --- a/apps/frontend/src/app/features/coupling/coupling.component.ts +++ b/apps/frontend/src/app/features/coupling/coupling.component.ts @@ -1,4 +1,11 @@ -import { Component, computed, inject, input } from '@angular/core'; +import { + Component, + computed, + effect, + inject, + input, + signal, +} from '@angular/core'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -6,6 +13,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, Router } from '@angular/router'; import { combineLatest, startWith } from 'rxjs'; import { LimitsStore } from '../../data/limits.store'; @@ -19,6 +27,7 @@ import { LimitsComponent } from '../../ui/limits/limits.component'; import { debounceTimeSkipFirst } from '../../utils/debounce'; import { EventService } from '../../utils/event.service'; +import { ChordCouplingRendererComponent } from './chord-coupling-renderer.component'; import { CouplingFilter, CouplingStore } from './coupling.store'; import { createGroups, createNodes, createEdges } from './graph.adapter'; @@ -39,16 +48,22 @@ const COUPLING_TIP = MatIconModule, MatTooltipModule, GraphComponent, + ChordCouplingRendererComponent, ], templateUrl: './coupling.component.html', styleUrl: './coupling.component.css', }) export class CouplingComponent { + showChordDiagram = signal(false); + + private previousType: GraphType | null = null; private statusStore = inject(StatusStore); private limitsStore = inject(LimitsStore); private couplingStore = inject(CouplingStore); private eventService = inject(EventService); + private route = inject(ActivatedRoute); + private router = inject(Router); type = input('structure'); @@ -73,6 +88,30 @@ export class CouplingComponent { constructor() { this.couplingStore.rxLoad(this.loadOptions$); + + const qp = this.route.snapshot.queryParamMap; + const view = qp.get('view'); + if (view === 'chord') { + this.showChordDiagram.set(true); + } else if (view === 'graph') { + this.showChordDiagram.set(false); + } + effect( + () => { + const currentType = this.type(); + if (currentType !== this.previousType) { + this.showChordDiagram.set(currentType === 'changes'); + this.previousType = currentType; + } + }, + { allowSignalWrites: true } + ); + } + + onToggleView(showChord: boolean): void { + this.showChordDiagram.set(showChord); + const queryParams = { view: showChord ? 'chord' : 'graph' }; + this.router.navigate([], { queryParams, queryParamsHandling: 'merge' }); } updateFilter(filter: Partial) { @@ -90,9 +129,10 @@ export class CouplingComponent { ? createGroups(result.dimensions) : []; - const leafNodes = createNodes(result, groupNodes, this.type()); - const edges = createEdges(result, this.type(), this.minConnections()); - const directed = this.type() === 'structure'; + const type = this.type(); + const leafNodes = createNodes(result, groupNodes, type); + const edges = createEdges(result, type, this.minConnections()); + const directed = type === 'structure'; const graph: Graph = { nodes: [...groupNodes, ...leafNodes], diff --git a/apps/frontend/src/app/features/hotspot-city/city3d.component.ts b/apps/frontend/src/app/features/hotspot-city/city3d.component.ts new file mode 100644 index 0000000..a2862ba --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/city3d.component.ts @@ -0,0 +1,462 @@ +import { CommonModule } from '@angular/common'; +import { + Component, + ElementRef, + HostListener, + Input, + OnChanges, + OnDestroy, + Output, + SimpleChanges, + viewChild, + EventEmitter, +} from '@angular/core'; +import * as THREE from 'three'; + +import { City3DBoundaries, City3DItem, City3DMeta } from './city3d.types'; + +@Component({ + selector: 'app-city3d', + standalone: true, + imports: [CommonModule], + template: `
`, + styles: [ + ` + :host { + display: block; + position: relative; + flex: 1 1 auto; + min-height: 240px; + width: 100%; + } + `, + ` + .canvas { + position: relative; + height: 100%; + width: 100%; + min-height: 240px; + z-index: 0; + } + `, + ` + .canvas canvas { + position: absolute; + inset: 0; + z-index: 0; + } + `, + ], +}) +export class City3DComponent implements OnChanges, OnDestroy { + @Input({ required: true }) items: City3DItem[] = []; + @Input({ required: true }) mode: 'file' | 'module' = 'file'; + @Input() boundaries?: City3DBoundaries; + + @Output() hoverChange = new EventEmitter<{ + meta: City3DMeta | null; + x: number; + y: number; + }>(); + @Output() buildingClick = new EventEmitter(); + + canvasContainer = + viewChild.required>('canvasContainer'); + + private scene?: THREE.Scene; + private camera?: THREE.PerspectiveCamera; + private renderer?: THREE.WebGLRenderer; + private raycaster?: THREE.Raycaster; + private mouse?: THREE.Vector2; + private animationHandle = 0; + private cleanup?: () => void; + + private buildings: THREE.Mesh[] = []; + private hoveredObject: THREE.Mesh | null = null; + private skipNextClick = false; + + // simple camera controls state + private isLeftMouseDown = false; + private isRightMouseDown = false; + private isSpacePan = false; + private mouseX = 0; + private mouseY = 0; + private targetRotationX = Math.PI / 4; + private targetRotationY = 0; + private targetPanX = 0; + private targetPanZ = 0; + private distance = 100; + + ngOnChanges(changes: SimpleChanges): void { + if (changes['items'] || changes['mode']) { + if (!this.scene) { + this.setupScene(); + this.startAnimation(); + } + this.buildCity(); + } + } + + ngOnDestroy(): void { + cancelAnimationFrame(this.animationHandle); + this.teardownScene(); + } + + private setupScene(): void { + this.teardownScene(); + + const container = this.canvasContainer().nativeElement; + const width = container.clientWidth || 600; + const height = Math.max(200, container.clientHeight || 400); + + const scene = new THREE.Scene(); + scene.fog = null; + + const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 5000); + const initialDistance = 180; + const angle = Math.PI / 4; // 45° + camera.position.set( + Math.sin(angle) * Math.cos(angle) * initialDistance, + Math.sin(angle) * initialDistance, + Math.cos(angle) * Math.cos(angle) * initialDistance + ); + camera.lookAt(0, 0, 0); + + const renderer = new THREE.WebGLRenderer({ + antialias: true, + logarithmicDepthBuffer: true, + alpha: true, + }); + renderer.setSize(width, height); + renderer.setPixelRatio(window.devicePixelRatio || 1); + renderer.setClearColor(0x000000, 0); + container.innerHTML = ''; + container.appendChild(renderer.domElement); + + // lights + const ambientLight = new THREE.AmbientLight(0x404040, 1.0); + scene.add(ambientLight); + const directionalLight = new THREE.DirectionalLight(0xffffff, 1); + directionalLight.position.set(50, 100, 50); + scene.add(directionalLight); + const pointLight1 = new THREE.PointLight(0x4fc3f7, 0.5, 100); + pointLight1.position.set(20, 30, 20); + scene.add(pointLight1); + const pointLight2 = new THREE.PointLight(0xff6b6b, 0.5, 100); + pointLight2.position.set(-20, 30, -20); + scene.add(pointLight2); + + // raycaster + this.raycaster = new THREE.Raycaster(); + this.mouse = new THREE.Vector2(); + + // resize observer + const resizeObserver = new ResizeObserver(() => { + const w = container.clientWidth || 600; + const h = Math.max(200, container.clientHeight || 400); + if (w > 0 && h > 0) { + camera.aspect = w / h; + camera.updateProjectionMatrix(); + renderer.setSize(w, h); + } + }); + resizeObserver.observe(container); + + this.scene = scene; + this.camera = camera; + this.renderer = renderer; + + this.cleanup = () => { + resizeObserver.disconnect(); + }; + } + + private startAnimation(): void { + const animate = () => { + const renderer = this.renderer; + const scene = this.scene; + const camera = this.camera; + if (!renderer || !scene || !camera) { + this.animationHandle = requestAnimationFrame(animate); + return; + } + this.updateCamera(camera); + renderer.render(scene, camera); + this.animationHandle = requestAnimationFrame(animate); + }; + this.animationHandle = requestAnimationFrame(animate); + } + + private updateCamera(camera: THREE.PerspectiveCamera): void { + camera.position.x = + Math.sin(this.targetRotationY) * + Math.cos(this.targetRotationX) * + this.distance + + this.targetPanX; + camera.position.y = Math.sin(this.targetRotationX) * this.distance; + camera.position.z = + Math.cos(this.targetRotationY) * + Math.cos(this.targetRotationX) * + this.distance + + this.targetPanZ; + camera.lookAt(this.targetPanX, 0, this.targetPanZ); + } + + private buildCity(): void { + const scene = this.scene; + if (!scene) return; + + // remove old buildings + this.buildings.forEach((b) => scene.remove(b)); + this.buildings = []; + + // platform + const total = this.items.length; + const gridSize = Math.ceil(Math.sqrt(total)); + const maxSide = this.items.length + ? Math.max( + ...this.items.map((i) => this.computeFootprintFromLoc(i.footprint)) + ) + : 1; + const cellSize = Math.max(4, Math.ceil(maxSide + 2)); + const platformSize = Math.max(20, gridSize * cellSize + 6); + + const platformGeometry = new THREE.BoxGeometry( + platformSize, + 0.5, + platformSize + ); + const platformMaterial = new THREE.MeshPhongMaterial({ + color: 0xffffff, + emissive: 0x000000, + emissiveIntensity: 0.0, + shininess: 10, + specular: 0xdddddd, + }); + const platform = new THREE.Mesh(platformGeometry, platformMaterial); + platform.position.set(0, 0, 0); + platform.receiveShadow = false; + scene.add(platform); + + const bbox = new THREE.Box3(); + this.items.forEach((item, index) => { + const row = Math.floor(index / gridSize); + const col = index % gridSize; + + const height = Math.max(1, this.normalizeComplexity(item.height)); + const side = this.computeFootprintFromLoc(item.footprint); + const width = side; + const depth = side; + + const buildingGeometry = new THREE.BoxGeometry(width, height, depth); + + const color = this.resolveColor(item); + const emissiveIntensity = + color === 0xffc107 || color === 0xf44336 ? 0.3 : 0.1; + const buildingMaterial = new THREE.MeshPhongMaterial({ + color, + emissive: color, + emissiveIntensity, + }); + + const building = new THREE.Mesh(buildingGeometry, buildingMaterial); + building.position.set( + (col - gridSize / 2) * cellSize, + height / 2 + 0.25, + (row - gridSize / 2) * cellSize + ); + building.castShadow = false; + building.receiveShadow = false; + building.userData = item.meta; + + this.buildings.push(building); + scene.add(building); + bbox.expandByObject(building); + }); + + const size = bbox.getSize(new THREE.Vector3()); + const maxDim = Math.max(size.x, size.y, size.z); + const camera = this.camera; + if (camera) { + const fov = (camera.fov * Math.PI) / 180; + const fitHeightDistance = maxDim / 2 / Math.tan(fov / 2); + const fitWidthDistance = fitHeightDistance / camera.aspect; + const distance = Math.max(fitHeightDistance, fitWidthDistance) * 1.15; + this.distance = Math.max(20, Math.min(4000, distance)); + } + } + + private resolveColor(item: City3DItem): number { + if (this.mode === 'file') { + const mcCabe = item.height; + if (mcCabe < 10) return 0x4caf50; + if (mcCabe < 20) return 0xffc107; + if (mcCabe < 40) return 0xff9800; + return 0xf44336; + } else { + const b = this.boundaries; + if (!b) return 0x4caf50; + const meta = item.meta; + if (meta.kind !== 'module') return 0x4caf50; + const moduleScore = + meta.countHotspot > 0 + ? b.hotspotBoundary + : meta.countWarning > 0 + ? b.warningBoundary + : 0; + if (moduleScore >= b.hotspotBoundary) return 0xf44336; + if (moduleScore >= b.warningBoundary) return 0xffc107; + return 0x4caf50; + } + } + + private normalizeComplexity(value: number): number { + const height = (value / 100) * 20; + return Math.min(20, Math.max(1, height)); + } + + private computeFootprintFromLoc(loc: number): number { + const side = Math.sqrt(Math.max(1, loc)); + const scaled = Math.min(6, 0.5 + side / 6); + return scaled; + } + + private teardownScene(): void { + if (this.cleanup) { + this.cleanup(); + this.cleanup = undefined; + } + if (this.renderer) { + this.renderer.dispose(); + this.renderer.forceContextLoss(); + } + this.scene = undefined; + this.camera = undefined; + this.renderer = undefined; + this.raycaster = undefined; + this.mouse = undefined; + this.buildings = []; + this.hoveredObject = null; + } + + // interaction + @HostListener('mousedown', ['$event']) + onMouseDown(event: MouseEvent): void { + const target = event.target as Node | null; + if (!this.renderer || !target || !this.renderer.domElement.contains(target)) + return; + if (event.button === 0) this.isLeftMouseDown = true; + if (event.button === 2) this.isRightMouseDown = true; + this.mouseX = event.clientX; + this.mouseY = event.clientY; + } + + @HostListener('mouseup', ['$event']) + onMouseUp(event: MouseEvent): void { + const target = event.target as Node | null; + if (!this.renderer || !target || !this.renderer.domElement.contains(target)) + return; + if (event.button === 0) this.isLeftMouseDown = false; + if (event.button === 2) this.isRightMouseDown = false; + } + + @HostListener('mousemove', ['$event']) + onMouseMove(event: MouseEvent): void { + const renderer = this.renderer; + const camera = this.camera; + if (!renderer || !camera) return; + const targetNode = event.target as Node | null; + const isOverCanvas = + !!targetNode && renderer.domElement.contains(targetNode); + + if (this.isLeftMouseDown && !this.isSpacePan) { + this.targetRotationY += (event.clientX - this.mouseX) * 0.005; + this.targetRotationX += (event.clientY - this.mouseY) * 0.005; + } + if (this.isRightMouseDown || (this.isLeftMouseDown && this.isSpacePan)) { + const dx = event.clientX - this.mouseX; + const dy = event.clientY - this.mouseY; + this.targetPanX -= dx * 0.2; + this.targetPanZ -= dy * 0.2; + if (Math.abs(dx) > 2 || Math.abs(dy) > 2) this.skipNextClick = true; + } + this.mouseX = event.clientX; + this.mouseY = event.clientY; + + if (!isOverCanvas || !this.mouse || !this.raycaster) return; + + const rect = renderer.domElement.getBoundingClientRect(); + const normX = ((event.clientX - rect.left) / rect.width) * 2 - 1; + const normY = -((event.clientY - rect.top) / rect.height) * 2 + 1; + this.mouse.set(normX, normY); + this.raycaster.setFromCamera(this.mouse, camera); + const intersects = this.raycaster.intersectObjects(this.buildings); + + if (intersects.length > 0) { + const object = intersects[0].object as THREE.Mesh; + if (this.hoveredObject !== object) { + if (this.hoveredObject) this.hoveredObject.scale.set(1, 1, 1); + this.hoveredObject = object; + object.scale.set(1.1, 1.1, 1.1); + } + const meta = object.userData as City3DMeta; + const left = event.clientX - rect.left + 12; + const top = event.clientY - rect.top + 12; + this.hoverChange.emit({ meta, x: left, y: top }); + } else { + if (this.hoveredObject) { + this.hoveredObject.scale.set(1, 1, 1); + this.hoveredObject = null; + } + this.hoverChange.emit({ meta: null, x: 0, y: 0 }); + } + } + + @HostListener('wheel', ['$event']) + onWheel(event: WheelEvent): void { + const target = event.target as Node | null; + if (!this.renderer || !target || !this.renderer.domElement.contains(target)) + return; + this.distance += event.deltaY * 0.05; + this.distance = Math.max(20, Math.min(4000, this.distance)); + event.preventDefault(); + } + + @HostListener('contextmenu', ['$event']) + onContextMenu(event: MouseEvent): void { + const target = event.target as Node | null; + if (!this.renderer || !target || !this.renderer.domElement.contains(target)) + return; + event.preventDefault(); + } + + @HostListener('click', ['$event']) + onClick(event: MouseEvent): void { + const target = event.target as Node | null; + if (!this.renderer || !target || !this.renderer.domElement.contains(target)) + return; + if (this.skipNextClick) { + this.skipNextClick = false; + return; + } + if (!this.hoveredObject) return; + const meta = this.hoveredObject.userData as City3DMeta; + this.buildingClick.emit(meta); + } + + @HostListener('window:keydown', ['$event']) + onKeyDown(event: KeyboardEvent): void { + if (event.code === 'Space') { + this.isSpacePan = true; + event.preventDefault(); + } + } + + @HostListener('window:keyup', ['$event']) + onKeyUp(event: KeyboardEvent): void { + if (event.code === 'Space') { + this.isSpacePan = false; + event.preventDefault(); + } + } +} diff --git a/apps/frontend/src/app/features/hotspot-city/city3d.types.ts b/apps/frontend/src/app/features/hotspot-city/city3d.types.ts new file mode 100644 index 0000000..66f7905 --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/city3d.types.ts @@ -0,0 +1,38 @@ +export type City3DFileMeta = { + kind: 'file'; + filePath: string; + folder: string; + complexity: number; // McCabe + commits: number; + changedLines: number; + score: number; +}; + +export type City3DModuleMeta = { + kind: 'module'; + moduleKey: string; // parent/module + parent: string; + module: string; + countHotspot: number; + countWarning: number; + countOk: number; + total: number; +}; + +export type City3DMeta = City3DFileMeta | City3DModuleMeta; + +export interface City3DItem { + id: string; + label: string; + // numbers used to derive footprint (X/Z) and height (Y) + footprint: number; + height: number; + // metadata used for tooltips/click handling in parent + meta: City3DMeta; +} + +export interface City3DBoundaries { + warningBoundary: number; + hotspotBoundary: number; + maxScore: number; +} diff --git a/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.css b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.css new file mode 100644 index 0000000..15e6cc9 --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.css @@ -0,0 +1,87 @@ +.city-container { + position: relative; + width: 100%; + height: 60vh; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.controls { + display: flex; + flex: 0 0 auto; + justify-content: space-between; + padding: 8px 12px 8px 12px; + z-index: 1; + position: relative; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.controls-left { + display: flex; + align-items: center; + gap: 10px; +} + +.tolerance-container { + display: flex; + align-items: center; +} + +.tolerance-slider { + width: 150px; +} + +.tolerance-value { + margin-right: 20px; + margin-left: 10px; +} + +.canvas { + position: relative; + flex: 0 0 auto; + height: 100%; + min-height: 240px; + z-index: 0; +} + +.canvas canvas { + position: absolute; + inset: 0; + z-index: 0; +} + +.hover-info { + position: absolute; + background: rgba(10, 14, 39, 0.95); + color: #fff; + padding: 12px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.2); + display: none; + pointer-events: none; + max-width: 300px; + z-index: 1000; + transform: translateZ(0); +} + +.hover-info.visible { + display: block; +} + +.hover-info .filename { + color: #4fc3f7; + font-weight: 600; + margin-bottom: 6px; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; +} + +.hover-info .metric { + font-size: 11px; + color: rgba(255, 255, 255, 0.85); + margin: 2px 0; +} diff --git a/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.html b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.html new file mode 100644 index 0000000..a0a73a9 --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.html @@ -0,0 +1,74 @@ +
+
+ + + By File + +
+ Tolerance + + + + + {{ store.minScore() }} % +
+ + help_outline + +
+ + + +
+ +
+ + +
+
{{ hoverTitle() }}
+
+ @for (m of hoverMetrics(); track m) { +
{{ m }}
+ } +
+
+
diff --git a/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.ts b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.ts new file mode 100644 index 0000000..a339940 --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/hotspot-city.component.ts @@ -0,0 +1,262 @@ +import { CommonModule } from '@angular/common'; +import { + Component, + ElementRef, + effect, + inject, + signal, + viewChild, +} from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { scan, startWith } from 'rxjs'; + +import { LimitsStore } from '../../data/limits.store'; +import { StatusStore } from '../../data/status.store'; +// +import { Limits } from '../../model/limits'; +import { LimitsComponent } from '../../ui/limits/limits.component'; +import { EventService } from '../../utils/event.service'; +import type { ScoreType } from '../hotspot/hotspot-adapter'; +import { HotspotDetailComponent } from '../hotspot/hotspot-detail/hotspot-detail.component'; +import { HotspotStore } from '../hotspot/hotspot.store'; +import { XRayDialogComponent } from '../trend-analysis/x-ray/x-ray-dialog.component'; + +import { City3DComponent } from './city3d.component'; +import { City3DBoundaries, City3DItem, City3DMeta } from './city3d.types'; +import { HotspotCityStore } from './hotspot-city.store'; + +@Component({ + selector: 'app-hotspot-city', + standalone: true, + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatCheckboxModule, + MatDialogModule, + MatSliderModule, + MatIconModule, + MatTooltipModule, + LimitsComponent, + City3DComponent, + ], + templateUrl: './hotspot-city.component.html', + styleUrl: './hotspot-city.component.css', + providers: [HotspotCityStore], +}) +export class HotspotCityComponent { + hoverEl = viewChild>('hoverEl'); + containerEl = viewChild>('containerEl'); + + private dialog = inject(MatDialog); + private limitsStore = inject(LimitsStore); + private statusStore = inject(StatusStore); + protected store = inject(HotspotCityStore); + private hotspotStore = inject(HotspotStore); + private eventService = inject(EventService); + private filterChangedSig = toSignal( + this.eventService.filterChanged.pipe( + startWith(0), + scan((acc) => acc + 1, 0) + ), + { initialValue: 0 } + ); + protected byFile = signal(false); + + hoverVisible = signal(false); + hoverX = signal(0); + hoverY = signal(0); + hoverTitle = signal(''); + hoverMetrics = signal([]); + + // controls bindings + totalCommits = this.statusStore.commits; + limits = this.limitsStore.limits; + + // derived inputs for 3D component + items = signal([]); + boundaries = signal(undefined); + + constructor() { + effect( + () => { + const byFile = this.byFile(); + if (byFile) { + const hotspots = this.store.hotspotData(); + const mapped: City3DItem[] = hotspots.map((f) => ({ + id: f.fileName, + label: f.fileName, + footprint: f.loc, + height: f.mcCabe, + meta: { + kind: 'file', + filePath: f.fileName, + folder: this.getLastSegment(this.dirname(f.fileName)), + complexity: f.mcCabe, + commits: f.commits, + changedLines: f.changedLines, + score: f.score, + }, + })); + this.items.set(mapped); + this.boundaries.set(undefined); + } else { + const { aggregated, warningBoundary, hotspotBoundary, maxScore } = + this.hotspotStore.aggregatedResult(); + const mapped: City3DItem[] = aggregated.map((a) => ({ + id: this.getModuleKey(a.parent, a.module), + label: this.getModuleKey(a.parent, a.module), + footprint: a.count, + height: a.count, + meta: { + kind: 'module', + moduleKey: this.getModuleKey(a.parent, a.module), + parent: a.parent, + module: a.module, + countHotspot: a.countHotspot, + countWarning: a.countWarning, + countOk: a.countOk, + total: a.count, + }, + })); + this.items.set(mapped); + this.boundaries.set({ warningBoundary, hotspotBoundary, maxScore }); + } + }, + { allowSignalWrites: true } + ); + + // Keep aggregated hotspots in sync while in module mode + effect( + () => { + if (this.byFile()) return; + // react to limits, minScore, selected metric and global filter changes + const lim = this.limits(); + const min = this.store.minScore(); + const metric = this.hotspotStore.filter.metric(); + void this.filterChangedSig(); + this.hotspotStore.rxLoadAggregated({ + limits: lim, + minScore: min, + metric, + }); + }, + { allowSignalWrites: true } + ); + } + + updateLimits(limits: Limits): void { + this.limitsStore.updateLimits(limits); + } + + openFilterDocs(): void { + window.open( + 'https://github.com/angular-architects/detective?tab=readme-ov-file#filtering-the-git-log', + '_blank', + 'noopener' + ); + } + + private dirname(path: string): string { + const idx = path.lastIndexOf('/'); + return idx === -1 ? '' : path.substring(0, idx); + } + + private getLastSegment(folder: string): string { + if (!folder) return ''; + const parts = folder.split('/').filter((p) => p.length > 0); + return parts.length ? parts[parts.length - 1] : folder; + } + + private getModuleKey(parent: string, module: string): string { + if (!parent) return module; + // avoid duplicated prefix like parent/parent/module + return module.startsWith(parent + '/') ? module : `${parent}/${module}`; + } + + onHover(event: { meta: City3DMeta | null; x: number; y: number }): void { + const meta = event.meta; + if (!meta) { + this.hoverVisible.set(false); + return; + } + const container = this.containerEl()?.nativeElement; + const tipEl = this.hoverEl()?.nativeElement; + const rectW = container?.clientWidth ?? 600; + const rectH = container?.clientHeight ?? 400; + const tipW = tipEl?.offsetWidth ?? 260; + const tipH = tipEl?.offsetHeight ?? 120; + const left = Math.min(Math.max(8, event.x), rectW - tipW - 8); + const top = Math.min(Math.max(8, event.y), rectH - tipH - 8); + this.hoverX.set(left); + this.hoverY.set(top); + + if (meta.kind === 'file') { + this.hoverTitle.set(meta.filePath); + this.hoverMetrics.set([ + `Folder: ${meta.folder}`, + `Complexity: ${meta.complexity}`, + `Commits: ${meta.commits}`, + `Changed Lines: ${meta.changedLines}`, + `Score: ${meta.score}`, + ]); + } else { + this.hoverTitle.set(meta.moduleKey); + this.hoverMetrics.set([ + `File Count`, + `Hotspots: ${meta.countHotspot}`, + `Warnings: ${meta.countWarning}`, + `Fine: ${meta.countOk}`, + `Total: ${meta.total}`, + ]); + } + this.hoverVisible.set(true); + } + + onBuildingClick(meta: City3DMeta): void { + if (meta.kind === 'file') { + this.dialog.open(XRayDialogComponent, { + data: { filePath: meta.filePath }, + width: '95%', + maxWidth: '900px', + }); + return; + } + const selectedModule = meta.moduleKey; + const agg = this.hotspotStore.aggregatedResult(); + const type: ScoreType = + meta.countHotspot > 0 + ? 'hotspot' + : meta.countWarning > 0 + ? 'warning' + : 'fine'; + const range = [ + 0, + agg.warningBoundary, + agg.hotspotBoundary, + agg.maxScore + 1, + ]; + const idx = type === 'fine' ? 0 : type === 'warning' ? 1 : 2; + const scoreRange = { from: range[idx], to: range[idx + 1] }; + + this.hotspotStore.rxLoadHotspots({ + limits: this.limits(), + metric: this.hotspotStore.filter.metric(), + selectedModule, + scoreRange, + scoreType: type, + }); + + this.dialog.open(HotspotDetailComponent, { + width: '95%', + height: '700px', + }); + } +} diff --git a/apps/frontend/src/app/features/hotspot-city/hotspot-city.store.ts b/apps/frontend/src/app/features/hotspot-city/hotspot-city.store.ts new file mode 100644 index 0000000..e02d565 --- /dev/null +++ b/apps/frontend/src/app/features/hotspot-city/hotspot-city.store.ts @@ -0,0 +1,176 @@ +import { computed, inject } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { + patchState, + signalStore, + withMethods, + withState, + withHooks, + withComputed, +} from '@ngrx/signals'; +import { rxMethod } from '@ngrx/signals/rxjs-interop'; +import { forkJoin, map, tap, switchMap, startWith, scan } from 'rxjs'; + +import { ConfigService } from '../../data/config.service'; +import { HotspotService } from '../../data/hotspot.service'; +import { LimitsStore } from '../../data/limits.store'; +import { FlatHotspot } from '../../model/hotspot-result'; +import { Limits } from '../../model/limits'; +import { EventService } from '../../utils/event.service'; + +export interface CombinedHotspot { + fileName: string; + loc: number; + mcCabe: number; + commits: number; + changedLines: number; + score: number; +} + +export interface LoadOptions { + limits: Limits; +} + +export const HotspotCityStore = signalStore( + withState({ + loading: false, + minScore: 33, + hotspotData: [] as CombinedHotspot[], + }), + withMethods((store) => ({ + setLoading(loading: boolean) { + patchState(store, { loading }); + }, + })), + withComputed( + ( + store, + limitsStore = inject(LimitsStore), + eventService = inject(EventService) + ) => { + const filterChanged = toSignal( + eventService.filterChanged.pipe( + startWith(0), + scan((acc) => acc + 1, 0) + ), + { initialValue: 0 } + ); + + return { + _filter: computed(() => ({ + minScore: store.minScore(), + limits: limitsStore.limits(), + filterChanged: filterChanged(), + })), + }; + } + ), + withMethods( + ( + store, + config = inject(ConfigService), + hotspot = inject(HotspotService) + ) => { + const merge = ( + lengthResults: { hotspots: FlatHotspot[] }[], + mcCabeResults: { hotspots: FlatHotspot[] }[] + ): CombinedHotspot[] => { + const byFile = new Map(); + + for (const res of lengthResults) { + for (const h of res.hotspots) { + const loc = h.complexity; + const prev = byFile.get(h.fileName); + if (!prev) { + byFile.set(h.fileName, { + fileName: h.fileName, + loc, + mcCabe: 0, + commits: h.commits, + changedLines: h.changedLines, + score: h.score, + }); + } else { + prev.loc = Math.max(prev.loc, loc); + prev.commits = Math.max(prev.commits, h.commits); + prev.changedLines = Math.max(prev.changedLines, h.changedLines); + prev.score = Math.max(prev.score, h.score); + } + } + } + + for (const res of mcCabeResults) { + for (const h of res.hotspots) { + const mc = h.complexity; + const prev = byFile.get(h.fileName); + if (!prev) { + byFile.set(h.fileName, { + fileName: h.fileName, + loc: 0, + mcCabe: mc, + commits: h.commits, + changedLines: h.changedLines, + score: h.score, + }); + } else { + prev.mcCabe = Math.max(prev.mcCabe, mc); + prev.commits = Math.max(prev.commits, h.commits); + prev.changedLines = Math.max(prev.changedLines, h.changedLines); + prev.score = Math.max(prev.score, h.score); + } + } + } + + return Array.from(byFile.values()); + }; + + return { + setMinScore: (minScore: number) => { + patchState(store, { minScore }); + }, + rxLoadCombined: rxMethod<{ + limits: Limits; + minScore: number; + }>((ev$) => + ev$.pipe( + switchMap(({ limits, minScore }) => { + store.setLoading(true); + return config.load().pipe( + switchMap((cfg) => { + const scopes = cfg.scopes?.length ? cfg.scopes : ['']; + const lengthReqs = scopes.map((s) => + hotspot.load( + { module: s, metric: 'Length', minScore }, + limits + ) + ); + const mcCabeReqs = scopes.map((s) => + hotspot.load( + { module: s, metric: 'McCabe', minScore }, + limits + ) + ); + return forkJoin({ + lengths: forkJoin(lengthReqs), + mcCabes: forkJoin(mcCabeReqs), + }).pipe( + map(({ lengths, mcCabes }) => merge(lengths, mcCabes)) + ); + }) + ); + }), + tap((hotspotData) => { + store.setLoading(false); + patchState(store, { hotspotData }); + }) + ) + ), + }; + } + ), + withHooks({ + onInit({ rxLoadCombined, _filter }) { + rxLoadCombined(_filter); + }, + }) +); diff --git a/apps/frontend/src/app/features/hotspot/hotspot-adapter.ts b/apps/frontend/src/app/features/hotspot/hotspot-adapter.ts index 50fb744..d06dbb5 100644 --- a/apps/frontend/src/app/features/hotspot/hotspot-adapter.ts +++ b/apps/frontend/src/app/features/hotspot/hotspot-adapter.ts @@ -99,10 +99,10 @@ export function toTreeMapConfig( export function getScoreTypeColor(scoreType: ScoreType) { switch (scoreType) { case 'hotspot': - return '#E74C3C'; + return '#F44336'; case 'warning': - return '#F1C40F'; + return '#FFC107'; case 'fine': - return '#2ECC71'; + return '#4CAF50'; } } diff --git a/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.css b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.css new file mode 100644 index 0000000..abc1b1c --- /dev/null +++ b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.css @@ -0,0 +1,131 @@ +.help-icon { + font-size: 1.125rem; + vertical-align: middle; + margin-left: 0.5rem; + cursor: help; + opacity: 0.7; +} + +.dialog-grid { + display: grid; + grid-template-rows: 1fr auto; + height: 100%; + gap: 1rem; + overflow: hidden; +} + +.table-container { + overflow-y: auto; + border: 0.0625rem solid #e0e0e0; + border-radius: 0.25rem; + min-height: 0; +} + +.bus-factor-table { + width: 100%; + border-collapse: collapse; +} + +.bus-factor-table thead { + position: sticky; + top: 0; + background: #f5f5f5; + z-index: 1; +} + +.bus-factor-table th { + padding: 0.75rem 0.5rem; + text-align: left; + font-weight: 600; + border-bottom: 0.125rem solid #ddd; +} + +.bus-factor-table td { + padding: 0.5rem; + border-bottom: 0.0625rem solid #eee; +} + +.bus-factor-table tbody tr:hover { + background-color: #f9f9f9; +} + +.percentage { + text-align: right; + font-weight: 500; +} + +.bus-factor { + text-align: center; + font-weight: bold; +} + +.risk-badge { + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + display: inline-block; +} + +.risk-badge.risk-critical { + background-color: #ff1744; + color: white; +} + +.risk-badge.risk-high { + background-color: #ff9800; + color: white; +} + +.risk-badge.risk-medium { + background-color: #ffc107; + color: #000; +} + +.risk-badge.risk-low { + background-color: #4caf50; + color: white; +} + +.risk-critical { + background-color: rgba(255, 23, 68, 0.05); +} + +.risk-high { + background-color: rgba(255, 152, 0, 0.05); +} + +.risk-medium { + background-color: rgba(255, 193, 7, 0.05); +} + +.risk-low { + background-color: rgba(76, 175, 80, 0.05); +} + +.legend { + padding: 0.5rem 0.75rem; + background-color: #f5f5f5; + border-radius: 0.25rem; + display: flex; + gap: 1rem; + flex-wrap: wrap; + font-size: 0.75rem; +} + +.legend-item { + display: flex; + align-items: center; + gap: 0.375rem; +} + +.legend-item .risk-badge { + min-width: 3.75rem; + text-align: center; +} + +mat-dialog-actions { + justify-content: space-between; + padding: 1rem 1.5rem; +} diff --git a/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.html b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.html new file mode 100644 index 0000000..dfff463 --- /dev/null +++ b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.html @@ -0,0 +1,85 @@ +

+ Bus Factor Report + + help_outline + +

+ + +
+ + + + + + + + + + + + + + @for (entry of busFactorData(); track entry.module) { + + + + + + + + + + } + +
Module{{ isTeamMode() ? 'Primary Team' : 'Primary Contributor' }}% of Changes + {{ isTeamMode() ? 'Secondary Team' : 'Secondary Contributor' }} + % of ChangesBus FactorRisk Level
{{ entry.module }}{{ entry.primaryContributor }}{{ entry.primaryPercentage.toFixed(1) }}%{{ entry.secondaryContributor }} + @if (entry.secondaryPercentage > 0) { + {{ entry.secondaryPercentage.toFixed(1) }}% } @else { - } + {{ entry.busFactor }} + + {{ entry.riskLevel }} + +
+
+ +
+
+ Critical + Single point of failure (100%) +
+
+ High + Bus Factor 1 or >70% +
+
+ Medium + Bus Factor 2 +
+
+ Low + Bus Factor 3+ +
+
+
+ + + + + diff --git a/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.ts b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.ts new file mode 100644 index 0000000..587e29e --- /dev/null +++ b/apps/frontend/src/app/features/team-alignment/bus-factor-report/bus-factor-report.component.ts @@ -0,0 +1,166 @@ +import { DialogRef } from '@angular/cdk/dialog'; +import { CommonModule } from '@angular/common'; +import { Component, computed, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +import { TeamAlignmentStore } from '../team-alignment.store'; + +export interface BusFactorEntry { + module: string; + primaryContributor: string; + primaryPercentage: number; + secondaryContributor: string; + secondaryPercentage: number; + busFactor: number; + riskLevel: 'Critical' | 'High' | 'Medium' | 'Low'; +} + +@Component({ + selector: 'app-bus-factor-report', + standalone: true, + imports: [ + CommonModule, + MatButtonModule, + MatDialogModule, + MatIconModule, + MatTooltipModule, + ], + templateUrl: './bus-factor-report.component.html', + styleUrl: './bus-factor-report.component.css', +}) +export class BusFactorReportComponent { + private taStore = inject(TeamAlignmentStore); + dialogRef = inject(DialogRef); + + isTeamMode = computed(() => { + const result = this.taStore.result(); + return ( + result.teams.length > 0 && + result.teams.some((team) => team.startsWith('Team')) + ); + }); + + busFactorData = computed(() => { + const result = this.taStore.result(); + const entries: BusFactorEntry[] = []; + + for (const [moduleName, moduleDetails] of Object.entries(result.modules)) { + const contributors = Object.entries(moduleDetails.changes).sort( + (a, b) => b[1] - a[1] + ); + + if (contributors.length === 0) continue; + + const totalChanges = contributors.reduce( + (sum, [_, changes]) => sum + changes, + 0 + ); + + const primaryContributor = contributors[0] || ['', 0]; + const secondaryContributor = contributors[1] || ['-', 0]; + + const primaryPercentage = (primaryContributor[1] / totalChanges) * 100; + const secondaryPercentage = secondaryContributor[1] + ? (secondaryContributor[1] / totalChanges) * 100 + : 0; + + const busFactor = this.calculateBusFactor(contributors, totalChanges); + const riskLevel = this.determineRiskLevel(busFactor, primaryPercentage); + + entries.push({ + module: moduleName, + primaryContributor: primaryContributor[0], + primaryPercentage, + secondaryContributor: secondaryContributor[0] || '-', + secondaryPercentage, + busFactor, + riskLevel, + }); + } + + return entries.sort((a, b) => { + const riskOrder = { Critical: 0, High: 1, Medium: 2, Low: 3 }; + return riskOrder[a.riskLevel] - riskOrder[b.riskLevel]; + }); + }); + + private calculateBusFactor( + contributors: [string, number][], + totalChanges: number + ): number { + if (contributors.length === 0) return 0; + + let accumulatedChanges = 0; + let contributorCount = 0; + + // Calculate how many contributors make up 50% of changes + for (const [, changes] of contributors) { + accumulatedChanges += changes; + contributorCount++; + if (accumulatedChanges >= totalChanges * 0.5) { + break; + } + } + + return contributorCount; + } + + private determineRiskLevel( + busFactor: number, + primaryPercentage: number + ): 'Critical' | 'High' | 'Medium' | 'Low' { + if (busFactor === 1 && primaryPercentage >= 80) return 'Critical'; + if (busFactor === 1 || primaryPercentage >= 70) return 'High'; + if (busFactor === 2) return 'Medium'; + return 'Low'; + } + + exportToCSV(): void { + const data = this.busFactorData(); + const headers = [ + 'Module', + 'Primary Contributor', + '% of Changes', + 'Secondary Contributor', + '% of Changes', + 'Bus Factor', + 'Risk Level', + ]; + + const csvContent = [ + headers.join(','), + ...data.map((row) => + [ + row.module, + row.primaryContributor, + row.primaryPercentage.toFixed(1) + '%', + row.secondaryContributor, + row.secondaryPercentage > 0 + ? row.secondaryPercentage.toFixed(1) + '%' + : '-', + row.busFactor, + row.riskLevel, + ] + .map((field) => `"${field}"`) + .join(',') + ), + ].join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv' }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `bus-factor-report-${ + new Date().toISOString().split('T')[0] + }.csv`; + link.click(); + window.URL.revokeObjectURL(url); + } + + close(): void { + this.dialogRef.close(); + } +} diff --git a/apps/frontend/src/app/features/team-alignment/bus-factor-report/index.ts b/apps/frontend/src/app/features/team-alignment/bus-factor-report/index.ts new file mode 100644 index 0000000..fef2699 --- /dev/null +++ b/apps/frontend/src/app/features/team-alignment/bus-factor-report/index.ts @@ -0,0 +1 @@ +export * from './bus-factor-report.component'; diff --git a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.css b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.css index f979cf6..49bfe42 100644 --- a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.css +++ b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.css @@ -1,63 +1,132 @@ .teams { - text-wrap: nowrap; - height: 100%; + overflow-wrap: anywhere; + height: 100dvh; overflow-x: hidden; - margin-left: 10px; + margin-left: 0.625rem; + display: grid; + grid-template-columns: 12.5rem 1fr; + column-gap: 0.75rem; .teams__list { - display: inline-block; - overflow: scroll; - text-wrap: nowrap; - max-width: calc(100% - 220px); - height: calc(100% - 10px); + display: grid; + grid-auto-flow: column; + grid-auto-columns: 12.5rem; + gap: 0.75rem; + overflow-x: auto; + overflow-y: hidden; + width: 100%; + height: 100%; } .team { display: inline-block; vertical-align: top; - width: 200px; + width: 12.5rem; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 0.5rem; + box-shadow: 0 0.0625rem 0.125rem rgba(0, 0, 0, 0.04); + height: 100%; + display: flex; + flex-direction: column; h2 { - height: 48px; + height: 3rem; + display: flex; + align-items: center; + padding: 0 0.5rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.06); } span.team__name--fixed { display: inline-block; - padding-top: 12px; + padding-top: 0.75rem; + } + + .team__name--editable { + cursor: text; + font-weight: 600; + -webkit-user-select: none; + user-select: none; + } + + .team__spacer { + flex: 1 1 auto; + } + + .team__count { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.5rem; + height: 1.5rem; + padding: 0 0.375rem; + border-radius: 999px; + background: rgba(0, 0, 0, 0.06); + font-size: 0.75rem; + margin-right: 0.25rem; } input.team__name { - width: 175px; - height: 48px; - font-size: 20px; + width: 10.9375rem; + height: 3rem; + font-size: 1.25rem; font-weight: 600; + border: none; + outline: none; + background: transparent; } .team__users { list-style-type: none; - min-height: 280px; - margin-left: 0px; - padding: 0 10px 32px 10px; + min-height: 17.5rem; + margin-left: 0; + padding: 0.5rem 0.625rem 2rem 0.625rem; + flex: 1 1 auto; + overflow-y: auto; + } + + .team__empty { + color: rgba(0, 0, 0, 0.5); + font-size: 0.75rem; + text-align: center; + padding: 0.5rem 0 1rem 0; + } + + .team__user { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem 0.5rem; + margin: 0.25rem 0; + border-radius: 0.375rem; + background: rgba(0, 0, 0, 0.03); + } + + .team__user .drag-handle { + font-size: 1.125rem; + color: rgba(0, 0, 0, 0.38); + cursor: grab; } } } button + button { - margin-left: 16px; + margin-left: 1rem; } li, ul { list-style-type: none; - margin-left: 0 !important; - padding-left: 0 !important; + margin-left: 0; + padding-left: 0; cursor: pointer; } .dialog-container { display: flex; flex-direction: column; - height: 100%; + height: 100dvh; } .dialog-content { @@ -65,13 +134,13 @@ ul { overflow: auto; display: flex; flex-direction: column; - padding: 16px; + padding: 1rem; } .dialog-actions { display: flex; justify-content: flex-end; - padding: 8px 16px; + padding: 0.5rem 1rem; } .h-full { @@ -79,5 +148,28 @@ ul { } .delete-button { - padding: 10px !important; + padding: 0.625rem; +} + +.team--unknown { + opacity: 0.9; +} + +/* CDK drag previews and placeholders */ +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 0.375rem; + box-shadow: 0 0.3125rem 0.625rem rgba(0, 0, 0, 0.2); +} + +.cdk-drag-placeholder { + opacity: 0; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.add-team-button mat-icon { + margin-right: 0.25rem; } diff --git a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.html b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.html index db3bb7a..5382986 100644 --- a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.html +++ b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.html @@ -14,26 +14,42 @@

Teams

-
-

+
+

@if (editing === team.name){ } @else if (team.mayBeEdited) { - {{ team.name }} + {{ + team.name + }} + + {{ + team.users.length + }} - } @else { {{ team.name }} } + } @else { {{ team.name }} + + {{ + team.users.length + }} + }

    class="simple team__users" cdkDropListSortingDisabled (cdkDropListDropped)="droppedOnTeam($event)" + aria-label="Team users list" > - @for (user of team.users; track user) { -
  • {{ user }}
  • - } +
  • + drag_indicator + {{ user }} +
  • +
  • + Drop users here +

- +
diff --git a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.ts b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.ts index 28677aa..88097b9 100644 --- a/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.ts +++ b/apps/frontend/src/app/features/team-alignment/define-teams/define-teams.component.ts @@ -4,6 +4,7 @@ import { CdkDragDrop, CdkDropList, CdkDropListGroup, + CdkDragHandle, } from '@angular/cdk/drag-drop'; import { CommonModule } from '@angular/common'; import { Component, inject, OnInit } from '@angular/core'; @@ -23,6 +24,7 @@ import { DefineTeamsStore, Team, User } from './define-teams.store'; CdkDropListGroup, CdkDropList, CdkDrag, + CdkDragHandle, MatButtonModule, MatIcon, MatDialogModule, @@ -66,7 +68,7 @@ export class DefineTeamsComponent implements OnInit { this.doneAutoFocus = true; } - doRename(name: string, event: FocusEvent) { + doRename(name: string, event: Event) { const inputElm = event.currentTarget as HTMLInputElement; const newName = inputElm.value; this.store.renameTeam(name, newName); @@ -74,6 +76,15 @@ export class DefineTeamsComponent implements OnInit { this.doneAutoFocus = false; } + cancelRename() { + this.editing = undefined; + this.doneAutoFocus = false; + } + + trackByUser(index: number, user: User): string { + return user; + } + close(): void { this.dialogRef.close(); } diff --git a/apps/frontend/src/app/features/team-alignment/team-alignment.component.html b/apps/frontend/src/app/features/team-alignment/team-alignment.component.html index d96e772..7fcb29b 100644 --- a/apps/frontend/src/app/features/team-alignment/team-alignment.component.html +++ b/apps/frontend/src/app/features/team-alignment/team-alignment.component.html @@ -7,10 +7,24 @@ >By User - + + { + this.reload.next(); + }); + } + + showBusFactorReport() { + this.dialog.open(BusFactorReportComponent, { + width: '90%', + maxWidth: '1200px', + height: '80%', + }); } } diff --git a/apps/frontend/src/app/features/trend-analysis/file-tree-node.model.ts b/apps/frontend/src/app/features/trend-analysis/file-tree-node.model.ts new file mode 100644 index 0000000..f76b486 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/file-tree-node.model.ts @@ -0,0 +1,36 @@ +/** + * Represents a node in the file tree structure + */ +export interface FileTreeNode { + /** The name of the file or folder */ + name: string; + + /** The full path from the root */ + fullPath?: string; + + /** Whether this node represents a file (true) or folder (false) */ + isFile: boolean; + + /** Child nodes for folders */ + children?: FileTreeNode[]; + + /** Change frequency for files */ + changeFreq?: number; + + /** Average complexity for files */ + avgComplexity?: number; + + /** Number of files in this folder (for folder nodes) */ + fileCount?: number; + + /** Whether this folder node is expanded in the tree view */ + isExpanded?: boolean; +} + +/** + * Represents a flattened tree node with level information for MatTree + */ +export interface FlatFileTreeNode extends FileTreeNode { + /** The depth level in the tree (0 for root) */ + level: number; +} diff --git a/apps/frontend/src/app/features/trend-analysis/file-tree.store.ts b/apps/frontend/src/app/features/trend-analysis/file-tree.store.ts new file mode 100644 index 0000000..4f5fce0 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/file-tree.store.ts @@ -0,0 +1,285 @@ +import { computed, inject } from '@angular/core'; +import { + patchState, + signalStore, + withComputed, + withHooks, + withMethods, + withState, +} from '@ngrx/signals'; +import { rxMethod } from '@ngrx/signals/rxjs-interop'; +import { switchMap, tap } from 'rxjs'; + +import { ConfigService } from '../../data/config.service'; +import { TrendAnalysisStore } from '../../data/trend-analysis.store'; +import { FileTrend } from '../../model/trend-analysis-result'; +import { EventService } from '../../utils/event.service'; + +import { FileTreeNode } from './file-tree-node.model'; +import { + buildFileTree, + expandAllNodes, + calculateFolderMetrics, + childrenAccessor, + isExpandable, + filterFilesByScopes, + getNodePath, + flattenTree, +} from './utils'; + +export const FileTreeStore = signalStore( + { providedIn: 'root' }, + withState({ + maxCommits: 100, + scopes: [] as string[], + expandedNodes: new Set(), + selectedFile: null as string | null, + selectedFolder: null as string | null, + }), + withComputed((store) => { + const trendAnalysisStore = inject(TrendAnalysisStore); + const files = computed(() => { + const result = trendAnalysisStore.result(); + if (!result) return [] as FileTrend[]; + return result.files; + }); + + // Reactive filtered files based on scopes + const filteredFiles = computed(() => { + return filterFilesByScopes(files(), store.scopes()); + }); + + const selectedFolderFiles = computed(() => { + const folderPath = store.selectedFolder(); + const files = filteredFiles(); + + if (!folderPath || !files.length) return []; + + return files.filter((file) => file.filePath.startsWith(folderPath + '/')); + }); + + // Define treeData + const treeData = computed(() => { + return buildFileTree(files(), store.scopes()); + }); + + const selectedFileTrend = computed(() => { + const selectedPath = store.selectedFile(); + const resultData = trendAnalysisStore.result(); + if (!selectedPath || !resultData || !resultData.files) return null; + + return ( + resultData.files.find((file) => file.filePath === selectedPath) || null + ); + }); + + const computeDelta = ( + points: { date: string; complexity?: number; lines?: number }[] + ) => { + if (!points || points.length < 2) + return { complexityDelta: 0, linesDelta: 0 }; + const first = points[0]; + const last = points[points.length - 1]; + const complexityDelta = (last.complexity || 0) - (first.complexity || 0); + const linesDelta = (last.lines || 0) - (first.lines || 0); + return { complexityDelta, linesDelta }; + }; + + const topComplexityIncreases = computed(() => { + return filteredFiles() + .map((f) => { + const { complexityDelta } = computeDelta(f.complexityTrend || []); + return { filePath: f.filePath, delta: complexityDelta, file: f }; + }) + .filter((x) => x.delta > 0) + .sort((a, b) => b.delta - a.delta) + .slice(0, 10); + }); + + const topSizeIncreases = computed(() => { + return filteredFiles() + .map((f) => { + const { linesDelta } = computeDelta(f.sizeTrend || []); + return { filePath: f.filePath, delta: linesDelta, file: f }; + }) + .filter((x) => x.delta > 0) + .sort((a, b) => b.delta - a.delta) + .slice(0, 10); + }); + + return { + files, + filteredFiles, + treeData, + selectedFolderFiles, + selectedFileTrend, + topComplexityIncreases, + topSizeIncreases, + progress: computed(() => trendAnalysisStore.progress()), + // Computed metrics for selected folder + selectedFolderMetrics: computed(() => { + const folderPath = store.selectedFolder(); + const filesInFolder = selectedFolderFiles(); + + if (!folderPath) return null; + + return calculateFolderMetrics(filesInFolder, folderPath); + }), + + selectedFileData: computed(() => { + const filePath = store.selectedFile(); + const files = filteredFiles(); + + if (!filePath) return null; + + return files.find((f) => f.filePath === filePath) || null; + }), + + flattenedTreeData: computed(() => { + const tree = treeData(); + const expandedNodes = store.expandedNodes(); + + if (!tree || !Array.isArray(tree) || tree.length === 0) { + return []; + } + + return flattenTree(tree, expandedNodes); + }), + + fileCountDisplay: computed(() => { + const totalFiles = files().length; + const displayedFiles = treeData().length; + + return { + totalFiles, + displayedFiles, + isFiltered: displayedFiles !== totalFiles, + isEmpty: totalFiles === 0, + }; + }), + + hasFiles: computed(() => files().length > 0), + hasTreeData: computed(() => treeData().length > 0), + hasSelectedFolder: computed(() => !!store.selectedFolder()), + hasSelectedFile: computed(() => !!store.selectedFile()), + hasSelectedFileTrend: computed(() => !!selectedFileTrend()), + }; + }), + withMethods( + ( + store, + configService = inject(ConfigService), + trendAnalysisStore = inject(TrendAnalysisStore) + ) => ({ + loadAndUpdateConfig: rxMethod(($ev) => + $ev.pipe( + switchMap(() => configService.load()), + tap((config) => { + if (config?.scopes) { + patchState(store, { scopes: config.scopes }); + } + }) + ) + ), + + startAnalysis: rxMethod(($ev) => + $ev.pipe( + tap(() => { + trendAnalysisStore.startStreamingAnalysis(store.maxCommits()); + }) + ) + ), + + childrenAccessor: () => childrenAccessor, + isExpandable: () => isExpandable, + + isExpanded: + () => + (node: FileTreeNode): boolean => { + return store.expandedNodes().has(getNodePath(node)); + }, + + setScopes: (scopes: string[]) => { + patchState(store, { scopes }); + }, + + updateMaxCommits: (maxCommits: number) => { + patchState(store, { maxCommits }); + }, + + toggleNode: (node: FileTreeNode) => { + const nodePath = getNodePath(node); + const expanded = new Set(store.expandedNodes()); + + if (expanded.has(nodePath)) { + expanded.delete(nodePath); + } else { + expanded.add(nodePath); + } + + patchState(store, { expandedNodes: expanded }); + }, + + expandAll: () => { + const tree = store.treeData(); + const expanded = expandAllNodes(tree); + patchState(store, { expandedNodes: expanded }); + }, + + collapseAll: () => { + patchState(store, { expandedNodes: new Set() }); + }, + + selectFile: (filePath: string | null) => { + patchState(store, { + selectedFile: filePath, + selectedFolder: null, + }); + }, + + selectFolder: (folderPath: string | null) => { + patchState(store, { + selectedFolder: folderPath, + selectedFile: null, + }); + }, + + clearSelections: () => { + patchState(store, { + selectedFile: null, + selectedFolder: null, + }); + }, + + expandPathToFile: (filePath: string) => { + const pathParts = filePath.split('/'); + const expanded = new Set(store.expandedNodes()); + let currentPath = ''; + + for (let i = 0; i < pathParts.length - 1; i++) { + currentPath += (i > 0 ? '/' : '') + pathParts[i]; + expanded.add(currentPath); + } + + patchState(store, { expandedNodes: expanded }); + }, + + reset: () => { + patchState(store, { + maxCommits: 100, + scopes: [], + expandedNodes: new Set(), + selectedFile: null, + selectedFolder: null, + }); + }, + }) + ), + withHooks({ + onInit: (store) => { + store.loadAndUpdateConfig(inject(EventService).filterChanged); + store.loadAndUpdateConfig(); + store.startAnalysis(store.maxCommits); + }, + }) +); diff --git a/apps/frontend/src/app/features/trend-analysis/hotspots/hotspots-dialog.component.ts b/apps/frontend/src/app/features/trend-analysis/hotspots/hotspots-dialog.component.ts new file mode 100644 index 0000000..4b25236 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/hotspots/hotspots-dialog.component.ts @@ -0,0 +1,181 @@ +import { CommonModule } from '@angular/common'; +import { Component, computed, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { + MatDialogModule, + MatDialogRef, + MatDialog, +} from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabsModule } from '@angular/material/tabs'; + +import { FileTreeStore } from '../file-tree.store'; +import { XRayDialogComponent } from '../x-ray/x-ray-dialog.component'; + +@Component({ + selector: 'app-hotspots-dialog', + standalone: true, + imports: [ + CommonModule, + MatDialogModule, + MatButtonModule, + MatIconModule, + MatTabsModule, + ], + template: ` +
+
+
+ whatshot + Top 10 Complexity Spikes +
+ +
+ +
+ + +
+
+
File
+
Δ Complexity
+
+ @for (item of topComplexity(); track item.filePath) { +
+
+ {{ item.filePath }} +
+
+ {{ item.delta | number : '1.0-2' }} +
+
+ } +
+
+ +
+
+
File
+
Δ LOC
+
+ @for (item of topSize(); track item.filePath) { +
+
+ {{ item.filePath }} +
+
{{ item.delta | number }}
+
+ } +
+
+
+
+
+ `, + styles: [ + ` + .hotspots-dialog { + width: 100%; + max-width: 900px; + max-height: 80vh; + } + + .dialog-header { + display: flex; + justify-content: space-between; + align-items: center; + margin: 0; + padding: 16px 24px; + border-bottom: 1px solid #e0e0e0; + } + + .title-content { + display: flex; + align-items: center; + gap: 8px; + font-size: 18px; + font-weight: 500; + } + + .close-button { + margin-left: auto; + } + + .dialog-content { + padding: 16px 0 0 0; + margin: 0; + max-height: 70vh; + overflow-y: auto; + } + + .list { + display: grid; + grid-template-columns: 1fr; + gap: 8px; + padding: 16px; + } + + .row { + display: grid; + grid-template-columns: 1fr 140px; + align-items: center; + gap: 16px; + padding: 8px 12px; + border-bottom: 1px solid #eee; + } + + .row.clickable { + cursor: pointer; + } + + .row.header { + position: sticky; + top: 0; + background: #fafafa; + border-bottom: 1px solid #e0e0e0; + font-weight: 600; + z-index: 1; + } + + .cell.path { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + 'Liberation Mono', 'Courier New', monospace; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .cell.delta { + text-align: right; + } + `, + ], +}) +export class HotspotsDialogComponent { + private dialogRef = inject(MatDialogRef); + private dialog = inject(MatDialog); + protected fileTreeStore = inject(FileTreeStore); + + protected topComplexity = computed(() => + this.fileTreeStore.topComplexityIncreases() + ); + protected topSize = computed(() => this.fileTreeStore.topSizeIncreases()); + + close(): void { + this.dialogRef.close(); + } + + openXRay(filePath: string): void { + if (!filePath) return; + this.dialog.open(XRayDialogComponent, { + data: { filePath }, + width: '90vw', + maxWidth: '900px', + maxHeight: '80vh', + autoFocus: false, + }); + this.close(); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/file-tree-node.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/file-tree-node.component.ts new file mode 100644 index 0000000..c51fa5a --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/file-tree-node.component.ts @@ -0,0 +1,101 @@ +import { Component, input, output } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; + +import { FileIconPipe } from '../../../ui/file-icon.pipe'; + +export interface FileNodeData { + name: string; + fullPath?: string; + isFile: boolean; + changeFreq?: number; + avgComplexity?: number; + fileCount?: number; +} + +@Component({ + selector: 'app-file-tree-node', + standalone: true, + imports: [MatIconModule, MatButtonModule, FileIconPipe], + template: ` +
+ {{ nodeData().name | fileIcon }} + {{ nodeData().name }} +
+ {{ nodeData().changeFreq }}× + C: {{ nodeData().avgComplexity }} +
+
+ `, + styles: [ + ` + .file-node { + display: flex; + align-items: center; + padding: 4px 8px; + cursor: pointer; + border-radius: 4px; + transition: background-color 0.2s; + } + + .file-node:hover { + background-color: rgba(0, 0, 0, 0.04); + } + + .file-node.selected { + background-color: rgba(63, 81, 181, 0.1); + color: #3f51b5; + } + + .file-icon { + margin-right: 8px; + font-size: 16px; + width: 16px; + height: 16px; + color: #666; + } + + .file-name { + flex: 1; + font-size: 13px; + margin-right: 8px; + } + + .file-metrics { + display: flex; + gap: 8px; + margin-left: 8px; + } + + .change-freq, + .complexity { + font-size: 11px; + padding: 2px 6px; + border-radius: 10px; + background: rgba(0, 0, 0, 0.1); + color: rgba(0, 0, 0, 0.7); + font-weight: 500; + } + + .change-freq { + background: rgba(255, 152, 0, 0.2); + color: #f57c00; + } + + .complexity { + background: rgba(76, 175, 80, 0.2); + color: #388e3c; + } + `, + ], +}) +export class FileTreeNodeComponent { + nodeData = input.required(); + isSelected = input(false); + + nodeClick = output(); +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-file-item.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-file-item.component.ts new file mode 100644 index 0000000..cf44f86 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-file-item.component.ts @@ -0,0 +1,77 @@ +import { Component, input, output } from '@angular/core'; +import { MatIconModule } from '@angular/material/icon'; + +import { FileTrend } from '../../../model/trend-analysis-result'; +import { FileIconPipe } from '../../../ui/file-icon.pipe'; +import { FileNamePipe } from '../../../ui/file-name.pipe'; + +@Component({ + selector: 'app-folder-file-item', + standalone: true, + imports: [MatIconModule, FileIconPipe, FileNamePipe], + template: ` +
+ {{ file().filePath | fileIcon }} + {{ file().filePath | fileName }} +
+ {{ file().changeFrequency }}× + C: {{ file().averageComplexity }} +
+
+ `, + styles: [ + ` + .folder-file-item { + display: flex; + align-items: center; + padding: 8px 12px; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: background-color 0.2s; + } + + .folder-file-item:last-child { + border-bottom: none; + } + + .folder-file-item:hover { + background: #f5f5f5; + } + + .file-icon { + margin-right: 8px; + font-size: 16px; + width: 16px; + height: 16px; + color: #666; + } + + .file-name { + flex: 1; + font-size: 13px; + margin-right: 8px; + } + + .file-metrics { + display: flex; + gap: 8px; + font-size: 11px; + } + + .change-freq { + color: #2196f3; + font-weight: 500; + } + + .complexity { + color: #ff9800; + font-weight: 500; + } + `, + ], +}) +export class FolderFileItemComponent { + file = input.required(); + + fileClick = output(); +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-tree-node.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-tree-node.component.ts new file mode 100644 index 0000000..59db9f2 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/folder-tree-node.component.ts @@ -0,0 +1,92 @@ +import { Component, input, output } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; + +export interface FolderNodeData { + name: string; + fullPath?: string; + isFile: boolean; + fileCount?: number; + isExpanded?: boolean; +} + +@Component({ + selector: 'app-folder-tree-node', + standalone: true, + imports: [MatIconModule, MatButtonModule], + template: ` + +
+ folder + {{ nodeData().name }} + ({{ nodeData().fileCount }}) +
+ `, + styles: [ + ` + :host { + display: flex; + align-items: center; + } + + .folder-node { + display: flex; + align-items: center; + padding: 4px 8px; + cursor: pointer; + border-radius: 4px; + transition: background-color 0.2s; + flex: 1; + } + + .folder-node:hover { + background-color: rgba(0, 0, 0, 0.04); + } + + .folder-node.folder-selected { + background-color: rgba(76, 175, 80, 0.1); + color: #4caf50; + } + + .folder-icon { + margin-right: 8px; + color: #ffb74d; + font-size: 16px; + width: 16px; + height: 16px; + } + + .folder-name { + flex: 1; + font-size: 13px; + font-weight: 500; + margin-right: 8px; + } + + .file-count { + font-size: 12px; + color: rgba(0, 0, 0, 0.6); + margin-left: 4px; + } + `, + ], +}) +export class FolderTreeNodeComponent { + nodeData = input.required(); + isSelected = input(false); + + nodeClick = output(); + toggleClick = output(); +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/recent-commits-table.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/recent-commits-table.component.ts new file mode 100644 index 0000000..2c7ae22 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/recent-commits-table.component.ts @@ -0,0 +1,149 @@ +import { Component, input, computed } from '@angular/core'; +import { MatTableModule } from '@angular/material/table'; + +import { CommitMetric } from '../../../model/trend-analysis-result'; + +export interface CommitData { + commitHash: string; + date: string; + author: string; + message: string; + linesAdded: number; + linesRemoved: number; + complexity: number; + totalLines: number; +} + +@Component({ + selector: 'app-recent-commits-table', + standalone: true, + imports: [MatTableModule], + template: ` +
+

Recent Commits

+
+ + + Commit + {{ + commit.commitHash + }} + + + + Date + {{ + formatDate(commit.date) + }} + + + + Author + {{ commit.author }} + + + + Message + + {{ + commit.message + }} + + + + + Changes + + +{{ commit.linesAdded }} + -{{ commit.linesRemoved }} + + + + + Complexity + {{ + commit.complexity + }} + + + + Lines + {{ + commit.totalLines + }} + + + + + +
+
+ `, + styles: [ + ` + .commits-section { + margin-top: 32px; + } + + .commits-section h4 { + margin-bottom: 16px; + font-size: 16px; + font-weight: 600; + } + + .commits-table { + max-height: 400px; + overflow: auto; + border: 1px solid #e0e0e0; + border-radius: 8px; + } + + .message-cell { + max-width: 300px; + } + + .commit-message { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .added { + color: #4caf50; + margin-right: 8px; + } + + .removed { + color: #f44336; + } + `, + ], +}) +export class RecentCommitsTableComponent { + commitMetrics = input(); + + displayedColumns = [ + 'commit', + 'date', + 'author', + 'message', + 'changes', + 'complexity', + 'lines', + ]; + + // Computed signal to handle different input types + displayCommits = computed(() => { + const rawMetrics = this.commitMetrics(); + if (rawMetrics?.length) { + return rawMetrics; + } + + return []; + }); + + formatDate(dateStr: string): string { + return new Date(dateStr).toLocaleDateString(); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-chart.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-chart.component.ts new file mode 100644 index 0000000..2b43b8f --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-chart.component.ts @@ -0,0 +1,338 @@ +import { + Component, + ElementRef, + input, + viewChild, + effect, + computed, + afterNextRender, + DestroyRef, + inject, +} from '@angular/core'; +import * as echarts from 'echarts'; + +import { FileTrend } from '../../../model/trend-analysis-result'; + +export interface TrendChartData { + date: string; + value: number; + commit: string; + label?: string; // For custom labels like "Complexity" or "Lines" +} + +export interface FolderTrendData { + complexityTrend: { date: string; complexity: number; commit: string }[]; + sizeTrend: { date: string; lines: number; commit: string }[]; + fileCount: number; +} + +export type ChartType = + | 'complexity' + | 'size' + | 'folder-complexity' + | 'folder-size'; + +@Component({ + selector: 'app-trend-chart', + standalone: true, + template: ` +
+ @if (title()) { +

{{ title() }}

+ } @if (description()) { +

{{ description() }}

+ } +
+
+ `, + styles: [ + ` + .chart-container { + margin-bottom: 24px; + } + + .chart-container h4 { + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; + } + + .chart-container.folder-chart { + border: 2px solid #e8f5e8; + background: linear-gradient(135deg, #f8fffe 0%, #f0fff4 100%); + border-radius: 8px; + padding: 16px; + } + + .chart-container.folder-chart h4 { + color: #2e7d2e; + font-size: 16px; + margin-bottom: 4px; + } + + .chart-description { + font-size: 12px; + color: #666; + margin: 0 0 12px 0; + font-style: italic; + } + + .chart { + width: 100%; + border: 1px solid #e0e0e0; + border-radius: 4px; + } + `, + ], +}) +export class TrendChartComponent { + // Inputs + chartType = input.required(); + fileTrend = input(); + folderTrendData = input(); + + title = input(); + description = input(); + height = input(300); + folderFileCount = input(); + + // Template reference + chartElement = viewChild.required('chartElement'); + + // Computed signal to map raw data to chart data format + chartData = computed(() => { + const chartType = this.chartType(); + const fileData = this.fileTrend(); + const folderData = this.folderTrendData(); + + // Map file trend data based on chart type + if (fileData) { + switch (chartType) { + case 'complexity': + return ( + fileData.complexityTrend?.map((t) => ({ + date: t.date, + value: t.complexity || 0, + commit: t.commit, + })) || [] + ); + case 'size': + return ( + fileData.sizeTrend?.map((t) => ({ + date: t.date, + value: t.lines || 0, + commit: t.commit, + })) || [] + ); + } + } + + // Map folder trend data based on chart type + if (folderData) { + switch (chartType) { + case 'folder-complexity': + return ( + folderData.complexityTrend?.map((t) => ({ + date: t.date, + value: t.complexity || 0, + commit: t.commit, + })) || [] + ); + case 'folder-size': + return ( + folderData.sizeTrend?.map((t) => ({ + date: t.date, + value: t.lines || 0, + commit: t.commit, + })) || [] + ); + } + } + + return []; + }); + + isFolderChart = () => this.chartType().startsWith('folder-'); + + private chart: echarts.ECharts | null = null; + private resizeObserver: ResizeObserver | null = null; + private destroyRef = inject(DestroyRef); + + constructor() { + effect(() => { + const data = this.chartData(); + const chartEl = this.chartElement(); + + if (chartEl && data?.length) { + this.updateChart(); + } + }); + + afterNextRender(() => { + this.setupResizeObserver(); + }); + + this.destroyRef.onDestroy(() => { + if (this.chart) { + this.chart.dispose(); + this.chart = null; + } + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + }); + } + + private setupResizeObserver(): void { + const chartElement = this.chartElement(); + if (!chartElement?.nativeElement) return; + + this.resizeObserver = new ResizeObserver(() => { + if (this.chart) { + this.chart.resize(); + } + }); + + this.resizeObserver.observe(chartElement.nativeElement); + } + + private updateChart(): void { + const chartElement = this.chartElement(); + if (!chartElement) return; + + if (!this.chart) { + this.chart = echarts.init(chartElement.nativeElement); + } + + const chartType = this.chartType(); + const data = this.chartData(); + + const configs = { + complexity: { + label: 'Complexity', + yAxisName: 'Complexity', + color: '#ff9800', + colorLight: 'rgba(255, 152, 0, 0.3)', + colorFade: 'rgba(255, 152, 0, 0.1)', + symbolSize: 6, + lineWidth: 2, + }, + size: { + label: 'Lines', + yAxisName: 'Lines of Code', + color: '#2196f3', + colorLight: 'rgba(33, 150, 243, 0.3)', + colorFade: 'rgba(33, 150, 243, 0.1)', + symbolSize: 6, + lineWidth: 2, + }, + 'folder-complexity': { + label: 'Avg Complexity', + yAxisName: 'Avg Complexity', + color: '#4caf50', + colorLight: 'rgba(76, 175, 80, 0.3)', + colorFade: 'rgba(76, 175, 80, 0.1)', + symbolSize: 8, + lineWidth: 3, + extraTooltip: `Files: ${this.folderFileCount() || 0}`, + }, + 'folder-size': { + label: 'Total Lines', + yAxisName: 'Total Lines of Code', + color: '#9c27b0', + colorLight: 'rgba(156, 39, 176, 0.3)', + colorFade: 'rgba(156, 39, 176, 0.1)', + symbolSize: 8, + lineWidth: 3, + extraTooltip: `Files: ${this.folderFileCount() || 0}`, + }, + }; + + const config = configs[chartType]; + if (config) { + this.renderChart(data, config); + } + } + + private renderChart( + data: TrendChartData[], + config: { + label: string; + yAxisName: string; + color: string; + colorLight: string; + colorFade: string; + symbolSize: number; + lineWidth: number; + extraTooltip?: string; + } + ): void { + if (!this.chart) return; + + const dates = data.map((d) => new Date(d.date).toLocaleDateString()); + const values = data.map((d) => d.value); + + type TooltipParam = { + name: string; + value: number | string; + dataIndex: number; + }; + + const option = { + title: { show: false }, + tooltip: { + trigger: 'axis', + formatter: (params: TooltipParam[]) => { + const point = params[0]; + const dataPoint = data[point.dataIndex]; + return ` +
${point.name}
+
${config.label}: ${point.value}
+
Commit: ${dataPoint.commit}
+ ${config.extraTooltip ? `
${config.extraTooltip}
` : ''} + `; + }, + }, + grid: { left: '10%', right: '10%', bottom: '15%', top: '10%' }, + xAxis: { + type: 'category', + data: dates, + axisLabel: { rotate: 45, fontSize: 10 }, + }, + yAxis: { + type: 'value', + name: config.yAxisName, + nameLocation: 'middle', + nameGap: 40, + }, + series: [ + { + name: config.label, + type: 'line', + data: values, + smooth: true, + symbol: 'circle', + symbolSize: config.symbolSize, + lineStyle: { color: config.color, width: config.lineWidth }, + itemStyle: { color: config.color }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { offset: 0, color: config.colorLight }, + { offset: 1, color: config.colorFade }, + ], + }, + }, + }, + ], + }; + + this.chart.setOption(option); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-metrics-card.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-metrics-card.component.ts new file mode 100644 index 0000000..46a1538 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis-details/trend-metrics-card.component.ts @@ -0,0 +1,119 @@ +import { Component, input, computed } from '@angular/core'; + +import { FileTrend } from '../../../model/trend-analysis-result'; + +export interface MetricData { + label: string; + value: string | number; +} + +export interface FolderMetricsData { + fileCount: number; + avgComplexity: number; + minComplexity: number; + maxComplexity: number; + avgSize: number; + totalChanges: number; + avgChangeFreq: number; + folderName?: string; + folderPath?: string; + files?: FileTrend[]; + complexityTrend?: Array<{ date: string; complexity: number; commit: string }>; + sizeTrend?: Array<{ date: string; lines: number; commit: string }>; +} + +@Component({ + selector: 'app-trend-metrics-card', + standalone: true, + template: ` +
+ @for (metric of displayMetrics(); track metric.label) { +
+ {{ metric.label }} + {{ metric.value }} +
+ } +
+ `, + styles: [ + ` + .trend-metrics { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + margin: 16px 0; + } + + .metric-card { + display: flex; + flex-direction: column; + padding: 16px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background: #fafafa; + } + + .metric-label { + font-size: 12px; + color: rgba(0, 0, 0, 0.6); + margin-bottom: 4px; + font-weight: 500; + } + + .metric-value { + font-size: 18px; + font-weight: 600; + color: #333; + } + `, + ], +}) +export class TrendMetricsCardComponent { + // Input for backward compatibility with existing MetricData[] + metrics = input(); + + // New inputs for raw data that will be mapped internally + fileTrend = input(); + folderMetrics = input(); + + // Computed signal to map raw data to MetricData format + displayMetrics = computed(() => { + // If metrics are provided directly, use them + const directMetrics = this.metrics(); + if (directMetrics?.length) { + return directMetrics; + } + + // If fileTrend is provided, map it to metrics + const fileData = this.fileTrend(); + if (fileData) { + return [ + { label: 'Change Frequency', value: fileData.changeFrequency }, + { label: 'Avg Complexity', value: fileData.averageComplexity }, + { label: 'Avg Size', value: `${fileData.averageSize} lines` }, + { label: 'Total Changes', value: fileData.totalChanges }, + ]; + } + + // If folderMetrics is provided, map it to metrics + const folderData = this.folderMetrics(); + if (folderData) { + return [ + { label: 'Files in Folder', value: folderData.fileCount || 0 }, + { label: 'Avg Complexity', value: folderData.avgComplexity || 0 }, + { + label: 'Complexity Range', + value: `${folderData.minComplexity || 0} - ${ + folderData.maxComplexity || 0 + }`, + }, + { label: 'Avg File Size', value: `${folderData.avgSize || 0} lines` }, + { label: 'Total Changes', value: folderData.totalChanges || 0 }, + { label: 'Avg Change Freq', value: folderData.avgChangeFreq || 0 }, + ]; + } + + // Fallback to empty array + return []; + }); +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.css b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.css new file mode 100644 index 0000000..7f1a6ad --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.css @@ -0,0 +1,256 @@ +.trend-analysis-container { + height: calc(100vh - 100px); + display: flex; + flex-direction: column; + padding: 16px; + box-sizing: border-box; +} + +.trend-analysis-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + border-bottom: 1px solid #e0e0e0; + padding-bottom: 16px; +} + +.trend-analysis-header h2 { + margin: 0; + font-weight: 500; +} + +.controls { + display: flex; + gap: 16px; + align-items: center; +} + +.controls mat-form-field { + width: 120px; +} + +.progress-section { + margin-bottom: 16px; + padding: 16px; + background: #f5f5f5; + border-radius: 4px; +} + +.progress-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.progress-message { + font-weight: 500; +} + +.progress-percent { + font-size: 14px; + color: #666; +} + +.progress-details { + margin-top: 8px; + display: flex; + gap: 16px; + font-size: 12px; + color: #666; +} + +.content-layout { + flex: 1; + display: flex; + min-height: 0; + position: relative; +} + +.left-panel, +.right-panel { + border: 1px solid #e0e0e0; + border-radius: 4px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.left-panel { + min-width: 250px; + max-width: 600px; + flex-shrink: 0; +} + +.right-panel { + flex: 1; + min-width: 0; +} + +.panel-header { + padding: 16px; + background: #fafafa; + border-bottom: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; + align-items: center; +} + +.panel-header h3 { + margin: 0; + font-size: 16px; + font-weight: 500; +} + +.file-count { + font-size: 12px; + color: #666; + background: #e0e0e0; + padding: 2px 6px; + border-radius: 2px; +} + +.file-tree { + flex: 1; + overflow: auto; + padding: 8px; + background-color: #fff; +} + +.file-tree-control { + font-size: 14px; +} + +.empty-state, +.empty-selection { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 300px; + color: #999; + text-align: center; +} + +.empty-state mat-icon, +.empty-selection mat-icon { + font-size: 48px; + width: 48px; + height: 48px; + margin-bottom: 16px; + color: #ccc; +} + +.empty-state p, +.empty-selection p { + margin: 4px 0; +} + +.hint { + font-size: 12px; + color: #bbb !important; +} + +.trend-details { + padding: 16px; + height: 100%; + overflow: auto; +} + +.trend-header { + margin-bottom: 24px; +} + +.file-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; +} + +.file-info { + flex: 1; +} + +.file-actions { + display: flex; + gap: 8px; + align-items: center; +} + +.trend-header h3 { + margin: 0 0 4px 0; + font-size: 18px; + font-weight: 500; +} + +.file-path { + margin: 0; + font-size: 12px; + color: #666; + font-family: monospace; + background: #f5f5f5; + padding: 4px 8px; + border-radius: 2px; +} + +.chart-container { + margin-bottom: 24px; +} + +.chart-container h4 { + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; +} + +/* Folder chart styling */ +.chart-container.folder-chart { + border: 2px solid #e8f5e8; + background: linear-gradient(135deg, #f8fffe 0%, #f0fff4 100%); + border-radius: 8px; + padding: 16px; +} + +.chart-container.folder-chart h4 { + color: #2e7d2e; + font-size: 16px; + margin-bottom: 4px; +} + +.chart-description { + font-size: 12px; + color: #666; + margin: 0 0 12px 0; + font-style: italic; +} + +.chart { + width: 100%; + border: 1px solid #e0e0e0; + border-radius: 4px; +} + +/* Resizer wrapper */ +.resizer-container { + position: relative; +} + +/* Folder files section */ +.folder-files-section { + margin-top: 24px; +} + +.folder-files-section h4 { + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; +} + +.folder-files-list { + max-height: 300px; + overflow-y: auto; + border: 1px solid #e0e0e0; + border-radius: 4px; +} diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.html b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.html new file mode 100644 index 0000000..c399132 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.html @@ -0,0 +1,277 @@ +
+
+

Trend Analysis

+
+ + + + Max. Commits + + 25 + 50 + 100 + 250 + 500 + 1000 + 2000 + 5000 + 10000 + + +
+
+ + + @if (fileTreeStore.progress()) { @let progressData = fileTreeStore.progress(); +
+
+ {{ progressData?.message }} + {{ progressData?.progress }}% +
+ + + @if (progressData?.commitsProcessed && progressData?.totalCommits) { +
+ Commits: {{ progressData?.commitsProcessed }}/{{ + progressData?.totalCommits + }} + @if (progressData?.filesAnalyzed) { + Files: {{ progressData?.filesAnalyzed }} + } @if (progressData?.currentCommit && progressData?.currentAuthor) { + Current: {{ progressData?.currentCommit?.substring(0, 8) }} ({{ + progressData?.currentAuthor + }}) + } +
+ } +
+ } + +
+ + @if (showLeftPanel()) { +
+
+

File Tree

+ @let counts = fileTreeStore.fileCountDisplay(); @if (!counts.isEmpty) { + @if (counts.isFiltered) { + {{ counts.displayedFiles }} / {{ counts.totalFiles }} files + (filtered) + } @else { + {{ counts.totalFiles }} files + } } +
+ +
+ @if (fileTreeStore.hasTreeData()) { + + + +
+ + +
+
+ + + +
+ + +
+
+
+ } @else { +
+ folder_open +

No trend data available

+

Start an analysis to see file trends

+
+ } +
+
+ + + } + + +
+ @let folderMetrics = fileTreeStore.selectedFolderMetrics(); @if + (fileTreeStore.hasSelectedFolder() && folderMetrics) { +
+
+

📁 {{ folderMetrics?.folderName || 'Unknown' }}

+

{{ folderMetrics?.folderPath || '' }}

+
+ + + + + + + + + + + + +
+

Files in Folder ({{ folderMetrics?.fileCount || 0 }})

+
+ @for (file of folderMetrics?.files || []; track file.filePath) { + + } +
+
+
+ } @else if (fileTreeStore.hasSelectedFile()) { @let filePath = + fileTreeStore.selectedFile(); +
+
+
+
+

{{ filePath || '' | fileName }}

+

{{ filePath || '' }}

+
+
+ +
+
+
+ + @let trend = fileTreeStore.selectedFileTrend(); @if (trend) { + + + + + + + + + + + + @if (trend.commits && trend.commits.length > 0) { + + } } + + +
+ } @else { +
+ insights +

Select a file to view trend analysis

+

+ Choose a file from the tree on the left to see its complexity and size + trends over time +

+
+ } +
+
+
diff --git a/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.ts b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.ts new file mode 100644 index 0000000..e0e63d6 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/trend-analysis.component.ts @@ -0,0 +1,138 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSliderModule } from '@angular/material/slider'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatTreeModule } from '@angular/material/tree'; + +import { FileNamePipe } from '../../ui/file-name.pipe'; +import { ResizerComponent } from '../../ui/resizer/resizer.component'; + +import { FlatFileTreeNode, FileTreeNode } from './file-tree-node.model'; +import { FileTreeStore } from './file-tree.store'; +import { HotspotsDialogComponent } from './hotspots/hotspots-dialog.component'; +import { FileTreeNodeComponent } from './trend-analysis-details/file-tree-node.component'; +import { FolderFileItemComponent } from './trend-analysis-details/folder-file-item.component'; +import { FolderTreeNodeComponent } from './trend-analysis-details/folder-tree-node.component'; +import { RecentCommitsTableComponent } from './trend-analysis-details/recent-commits-table.component'; +import { TrendChartComponent } from './trend-analysis-details/trend-chart.component'; +import { TrendMetricsCardComponent } from './trend-analysis-details/trend-metrics-card.component'; +import { XRayDialogComponent } from './x-ray/x-ray-dialog.component'; + +@Component({ + selector: 'app-trend-analysis', + standalone: true, + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatFormFieldModule, + MatInputModule, + MatProgressBarModule, + MatSliderModule, + MatToolbarModule, + MatTreeModule, + MatTabsModule, + MatIconModule, + MatExpansionModule, + MatSelectModule, + MatTooltipModule, + ResizerComponent, + FileTreeNodeComponent, + FolderTreeNodeComponent, + TrendChartComponent, + TrendMetricsCardComponent, + RecentCommitsTableComponent, + FolderFileItemComponent, + FileNamePipe, + ], + templateUrl: './trend-analysis.component.html', + styleUrl: './trend-analysis.component.css', +}) +export class TrendAnalysisComponent { + protected fileTreeStore = inject(FileTreeStore); + private dialog = inject(MatDialog); + + leftPanelWidth = signal(350); + showLeftPanel = signal(true); + + levelAccessor = (node: FlatFileTreeNode): number => { + return node?.level || 0; + }; + + trackByNode = (index: number, node: FlatFileTreeNode): string => { + return node?.fullPath || node?.name || `${index}`; + }; + + isExpandable = (node: FileTreeNode): boolean => { + return !!node?.children && node.children.length > 0; + }; + + isExpanded = (node: FileTreeNode): boolean => { + const fullPath = node?.fullPath || node?.name; + const expanded = this.fileTreeStore.expandedNodes(); + return expanded.has(fullPath); + }; + + // Tree node predicates + isFile = (_index: number, node: FileTreeNode): boolean => { + return node?.isFile === true; + }; + + isFolder = (_index: number, node: FileTreeNode): boolean => { + return node?.isFile === false; + }; + + selectNode(node: FileTreeNode): void { + if (node.isFile) { + this.fileTreeStore.selectFile(node.fullPath || node.name); + } else { + this.fileTreeStore.selectFolder(node.fullPath || node.name); + } + } + + selectFileFromPath(path: string): void { + this.fileTreeStore.expandPathToFile(path); + this.fileTreeStore.selectFile(path); + } + + clearSelection(): void { + this.fileTreeStore.clearSelections(); + } + + openXRayDialog(): void { + const filePath = this.fileTreeStore.selectedFile(); + if (filePath) { + this.dialog.open(XRayDialogComponent, { + data: { filePath }, + width: '90vw', + maxWidth: '900px', + maxHeight: '80vh', + autoFocus: false, + }); + } + } + + openHotspotsDialog(): void { + this.dialog.open(HotspotsDialogComponent, { + width: '90vw', + maxWidth: '900px', + maxHeight: '80vh', + autoFocus: false, + }); + } + + toggleLeftPanel(): void { + this.showLeftPanel.update((visible) => !visible); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/utils/index.ts b/apps/frontend/src/app/features/trend-analysis/utils/index.ts new file mode 100644 index 0000000..e015942 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/utils/index.ts @@ -0,0 +1,20 @@ +// Model exports +export { FileTreeNode } from '../file-tree-node.model'; + +// Tree building and manipulation utilities +export { + buildFileTree, + updateFolderFileCounts, + expandAllNodes, + filterFilesByScopes, + flattenTree, +} from './tree-utils'; + +// Tree accessor functions for MatTree +export { childrenAccessor, isExpandable, getNodePath } from './tree-accessors'; + +// Trend data aggregation utilities +export { aggregateTrendData } from './trend-utils'; + +// Metrics calculation utilities +export { calculateFolderMetrics } from './metrics-utils'; diff --git a/apps/frontend/src/app/features/trend-analysis/utils/metrics-utils.ts b/apps/frontend/src/app/features/trend-analysis/utils/metrics-utils.ts new file mode 100644 index 0000000..652ede5 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/utils/metrics-utils.ts @@ -0,0 +1,78 @@ +import { FileTrend } from '../../../model/trend-analysis-result'; + +import { aggregateTrendData } from './trend-utils'; + +/** + * Calculate comprehensive metrics for a collection of files in a folder + */ +export function calculateFolderMetrics( + filesInFolder: FileTrend[], + folderPath: string +) { + if (filesInFolder.length === 0) return null; + + const metrics = calculateFileMetrics(filesInFolder); + const aggregatedTrends = aggregateTrendData(filesInFolder); + + return { + folderName: extractFolderName(folderPath), + folderPath, + fileCount: filesInFolder.length, + ...metrics, + files: filesInFolder, + complexityTrend: aggregatedTrends.complexityTrend, + sizeTrend: aggregatedTrends.sizeTrend, + }; +} + +/** + * Extract folder name from path + */ +function extractFolderName(folderPath: string): string { + return folderPath.split('/').pop() || folderPath; +} + +/** + * Calculate all file metrics in a single iteration for optimal performance + * Computes complexity, size, and change metrics together + */ +function calculateFileMetrics(files: FileTrend[]) { + // Initialize accumulators + let totalComplexity = 0; + let totalSize = 0; + let totalChanges = 0; + let maxComplexity = -Infinity; + let minComplexity = Infinity; + + // Single pass through all files + for (const file of files) { + totalComplexity += file.averageComplexity; + totalSize += file.averageSize; + totalChanges += file.totalChanges; + + if (file.averageComplexity > maxComplexity) { + maxComplexity = file.averageComplexity; + } + if (file.averageComplexity < minComplexity) { + minComplexity = file.averageComplexity; + } + } + + const fileCount = files.length; + + return { + avgComplexity: roundToTwoDecimals(totalComplexity / fileCount), + maxComplexity: roundToTwoDecimals(maxComplexity), + minComplexity: roundToTwoDecimals(minComplexity), + avgSize: Math.round(totalSize / fileCount), + totalChanges, + avgChangeFreq: roundToTwoDecimals(totalChanges / fileCount), + }; +} + +/** + * Round a number to two decimal places + */ +function roundToTwoDecimals(value: number): number { + return Math.round(value * 100) / 100; +} diff --git a/apps/frontend/src/app/features/trend-analysis/utils/tree-accessors.ts b/apps/frontend/src/app/features/trend-analysis/utils/tree-accessors.ts new file mode 100644 index 0000000..c8f938a --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/utils/tree-accessors.ts @@ -0,0 +1,19 @@ +import { FileTreeNode } from '../file-tree-node.model'; + +/** + * MatTree accessor function to get children of a node + */ +export const childrenAccessor = (node: FileTreeNode): FileTreeNode[] => + node.children || []; + +/** + * MatTree accessor function to check if a node is expandable + */ +export const isExpandable = (node: FileTreeNode): boolean => + !!node.children && node.children.length > 0; + +/** + * Gets the full path of a node, falling back to name if fullPath is not available + */ +export const getNodePath = (node: FileTreeNode): string => + node.fullPath || node.name; diff --git a/apps/frontend/src/app/features/trend-analysis/utils/tree-utils.ts b/apps/frontend/src/app/features/trend-analysis/utils/tree-utils.ts new file mode 100644 index 0000000..381fb6f --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/utils/tree-utils.ts @@ -0,0 +1,154 @@ +import { FileTrend } from '../../../model/trend-analysis-result'; +import { FileTreeNode, FlatFileTreeNode } from '../file-tree-node.model'; + +import { getNodePath } from './tree-accessors'; + +/** + * Builds a file tree from a flat list of file trends + * Optionally filters files based on scopes + */ +export function buildFileTree( + files: FileTrend[], + scopes?: string[] +): FileTreeNode[] { + const filteredFiles = filterFilesByScopes(files, scopes); + const tree: FileTreeNode[] = []; + const pathMap = new Map(); + + // Build tree structure from filtered files + for (const file of filteredFiles) { + const parts = file.filePath.split('/'); + let currentPath = ''; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + const parentPath = currentPath; + currentPath = currentPath ? `${currentPath}/${part}` : part; + + // Skip if node already exists + if (pathMap.has(currentPath)) continue; + + // Create new node + const isFile = i === parts.length - 1; + const node: FileTreeNode = { + name: part, + fullPath: isFile ? file.filePath : currentPath, + isFile, + children: isFile ? undefined : [], + changeFreq: isFile ? file.changeFrequency : undefined, + avgComplexity: isFile ? Math.round(file.averageComplexity) : undefined, + isExpanded: false, + }; + + pathMap.set(currentPath, node); + + // Add to parent or root + if (parentPath) { + const parent = pathMap.get(parentPath); + if (parent?.children) { + parent.children.push(node); + } + } else { + tree.push(node); + } + } + } + + // Set file counts on all folder nodes + updateFolderFileCounts(tree); + return tree; +} + +/** + * Filters files based on provided scopes + * @param files - Array of files to filter + * @param scopes - Optional array of scope paths to filter by + * @returns Filtered array of files + */ +export function filterFilesByScopes( + files: FileTrend[], + scopes?: string[] +): FileTrend[] { + if (!scopes?.length) return files; + + return files.filter((file) => + scopes.some( + (scope) => + file.filePath.startsWith(scope + '/') || file.filePath === scope + ) + ); +} + +/** + * Recursively updates folder nodes with their file counts + * Mutates the tree by setting fileCount property on folder nodes + * @returns Total number of files in the tree + */ +export function updateFolderFileCounts(nodes: FileTreeNode[]): number { + if (!nodes?.length) return 0; + + let totalFiles = 0; + + for (const node of nodes) { + if (!node) continue; + + if (node.isFile) { + totalFiles++; + } else if (node.children?.length) { + const childCount = updateFolderFileCounts(node.children); + node.fileCount = childCount; + totalFiles += childCount; + } + } + + return totalFiles; +} + +/** + * Returns a set of all expanded node paths in the tree + */ +export function expandAllNodes(tree: FileTreeNode[]): Set { + const expanded = new Set(); + + const collectNodes = (nodes: FileTreeNode[]) => { + for (const node of nodes) { + if (!node.isFile && node.children?.length) { + expanded.add(getNodePath(node)); + collectNodes(node.children); + } + } + }; + + collectNodes(tree); + return expanded; +} + +export function flattenTree( + nodes: FileTreeNode[], + expandedNodes: Set, + level = 0 +): FlatFileTreeNode[] { + if (!nodes || !Array.isArray(nodes)) return []; + + const result: FlatFileTreeNode[] = []; + + for (const node of nodes) { + if (!node) continue; + + // Add current node with level + const flatNode: FlatFileTreeNode = { ...node, level }; + result.push(flatNode); + + // Add children if expanded + if ( + !node.isFile && + node.children && + Array.isArray(node.children) && + expandedNodes.has(getNodePath(node)) + ) { + result.push(...flattenTree(node.children, expandedNodes, level + 1)); + } + } + + return result; +} diff --git a/apps/frontend/src/app/features/trend-analysis/utils/trend-utils.ts b/apps/frontend/src/app/features/trend-analysis/utils/trend-utils.ts new file mode 100644 index 0000000..89febca --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/utils/trend-utils.ts @@ -0,0 +1,98 @@ +import { FileTrend } from '../../../model/trend-analysis-result'; + +/** + * Aggregates trend data from multiple files into a single trend + * Calculates average complexity and total size for each date + * Optimized to use a single pass through the data + */ +export function aggregateTrendData(files: FileTrend[]) { + if (files.length === 0) { + return { complexityTrend: [], sizeTrend: [] }; + } + + // Single pass to collect all metrics by date + const dateMetrics = new Map< + string, + { + commit: string; + totalComplexity: number; + complexityCount: number; + totalLines: number; + } + >(); + + // Process all files and their trends in a single pass + for (const file of files) { + // Process complexity trends + for (const trend of file.complexityTrend) { + if (trend.complexity === undefined) continue; + + const metrics = dateMetrics.get(trend.date); + if (metrics) { + metrics.totalComplexity += trend.complexity; + metrics.complexityCount++; + } else { + dateMetrics.set(trend.date, { + commit: trend.commit, + totalComplexity: trend.complexity, + complexityCount: 1, + totalLines: 0, + }); + } + } + + // Process size trends + for (const trend of file.sizeTrend) { + if (trend.lines === undefined) continue; + + const metrics = dateMetrics.get(trend.date); + if (metrics) { + metrics.totalLines += trend.lines; + // Update commit if not set (in case size trend appears before complexity trend) + if (!metrics.commit) { + metrics.commit = trend.commit; + } + } else { + dateMetrics.set(trend.date, { + commit: trend.commit, + totalComplexity: 0, + complexityCount: 0, + totalLines: trend.lines, + }); + } + } + } + + // Sort dates once + const sortedDates = Array.from(dateMetrics.keys()).sort(); + + // Pre-allocate arrays for better performance + const complexityTrend = new Array(sortedDates.length); + const sizeTrend = new Array(sortedDates.length); + + // Build both trends in a single pass + for (let i = 0; i < sortedDates.length; i++) { + const date = sortedDates[i]; + const metrics = dateMetrics.get(date); + if (!metrics) continue; // Should not happen, but handle gracefully + + complexityTrend[i] = { + date, + complexity: + metrics.complexityCount > 0 + ? Math.round( + (metrics.totalComplexity / metrics.complexityCount) * 100 + ) / 100 + : 0, + commit: metrics.commit, + }; + + sizeTrend[i] = { + date, + lines: metrics.totalLines, + commit: metrics.commit, + }; + } + + return { complexityTrend, sizeTrend }; +} diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray-dialog.component.ts b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray-dialog.component.ts new file mode 100644 index 0000000..2a444ea --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray-dialog.component.ts @@ -0,0 +1,100 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { + MatDialogRef, + MAT_DIALOG_DATA, + MatDialogModule, +} from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; + +import { FileNamePipe } from '../../../ui/file-name.pipe'; + +import { XRayComponent } from './x-ray.component'; +import { XRayStore } from './x-ray.store'; + +@Component({ + selector: 'app-x-ray-dialog', + standalone: true, + imports: [ + CommonModule, + MatDialogModule, + MatButtonModule, + MatIconModule, + FileNamePipe, + XRayComponent, + ], + providers: [XRayStore], + template: ` +
+
+
+ analytics + X-Ray Code Analysis + {{ + data.filePath | fileName + }} +
+ +
+ +
+ +
+
+ `, + styles: [ + ` + .x-ray-dialog { + width: 100%; + max-width: 900px; + max-height: 80vh; + } + + .dialog-header { + display: flex; + justify-content: space-between; + align-items: center; + margin: 0; + padding: 16px 24px; + border-bottom: 1px solid #e0e0e0; + } + + .title-content { + display: flex; + align-items: center; + gap: 8px; + font-size: 18px; + font-weight: 500; + } + + .file-name { + font-size: 14px; + font-weight: 400; + color: rgba(0, 0, 0, 0.6); + margin-left: 8px; + } + + .close-button { + margin-left: auto; + } + + .dialog-content { + padding: 0; + margin: 0; + max-height: 70vh; + overflow-y: auto; + } + `, + ], +}) +export class XRayDialogComponent { + private dialogRef = inject(MatDialogRef); + protected data = inject<{ filePath: string }>(MAT_DIALOG_DATA); + + close(): void { + this.dialogRef.close(); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.css b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.css new file mode 100644 index 0000000..5f14a6a --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.css @@ -0,0 +1,151 @@ +.x-ray-container { + background: #fafafa; + border-radius: 8px; + overflow: hidden; +} + +.loading-container { + text-align: center; + padding: 48px; + background: white; +} + +.error-container { + display: flex; + align-items: center; + gap: 12px; + padding: 24px; + background: #ffebee; + color: #c62828; + border-radius: 8px; + margin: 16px; +} + +.metrics-tabs { + background: white; +} + +.tab-content { + padding: 24px; +} + +.metrics-cards { + display: flex; + flex-direction: column; + gap: 16px; +} + +.metric-card { + background: white; + border: 1px solid #e0e0e0; + border-radius: 12px; + padding: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.metric-card:hover { + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + transform: translateY(-2px); +} + +.card-header { + margin-bottom: 16px; +} + +.card-header h5 { + margin: 0; + font-size: 16px; + font-weight: 500; + color: #333; + font-family: monospace; +} + +.metrics-row { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; +} + +.metric-item { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: #f8f9fa; + border-radius: 8px; + transition: all 0.2s; +} + +.metric-item:hover { + background: #e9ecef; +} +.metric-item.warning { + background: #fff3e0; + border-left: 4px solid #ff9800; +} +.metric-item.warning:hover { + background: #ffe0b2; +} + +.metric-icon { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: #e3f2fd; + border-radius: 50%; + color: #1976d2; +} + +.metric-item.warning .metric-icon { + background: #fff3e0; + color: #f57c00; +} + +.metric-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; +} +.metric-label-row { + display: flex; + align-items: center; + gap: 6px; + line-height: 1; +} + +.ref-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + border-radius: 50%; + color: #909090; + text-decoration: none; + transition: color 0.15s ease, transform 0.15s ease; +} +.ref-icon .mat-icon { + font-size: 18px; + width: 18px; + height: 18px; + line-height: 18px; +} +.ref-icon:hover { + color: #1976d2; + transform: translateY(-1px); +} + +.metric-label { + font-size: 14px; + font-weight: 500; + color: #666; +} +.metric-value { + font-size: 18px; + font-weight: 600; + color: #333; +} diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.html b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.html new file mode 100644 index 0000000..cb3cc1a --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.html @@ -0,0 +1,68 @@ +
+ @if (store.error()) { +
+ error +

{{ store.error() }}

+
+ } @if (store.vm(); as vm) { + + @for (tab of vm.tabs; track tab.id) { + + + {{ tab.icon || 'label' }} + {{ tab.title }} + +
+
+ @for (card of tab.cards; track card.title) { +
+
+
{{ card.title }}
+
+
+ @for (metric of card.metrics; track metric.label) { +
+
+ {{ metric.icon || 'label' }} +
+
+
+ {{ metric.label }} + @if (metric.refLink) { + + help_outline + + } +
+ {{ metric.value }} +
+
+ } +
+
+ } +
+
+
+ } +
+ } @else if (store.loading()) { +
+ +

Loading metrics schema...

+
+ } +
diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.ts b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.ts new file mode 100644 index 0000000..b6b32f7 --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.component.ts @@ -0,0 +1,38 @@ +import { CommonModule } from '@angular/common'; +import { afterNextRender, Component, inject, input } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatIconModule } from '@angular/material/icon'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +import { XRayStore } from './x-ray.store'; + +@Component({ + selector: 'app-x-ray', + standalone: true, + imports: [ + CommonModule, + MatCardModule, + MatProgressSpinnerModule, + MatTabsModule, + MatIconModule, + MatChipsModule, + MatTooltipModule, + ], + providers: [XRayStore], + templateUrl: './x-ray.component.html', + styleUrls: ['./x-ray.component.css'], +}) +export class XRayComponent { + store = inject(XRayStore); + + filePath = input.required(); + + constructor() { + afterNextRender(() => { + this.store.load(this.filePath()); + }); + } +} diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.store.ts b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.store.ts new file mode 100644 index 0000000..5f00b2e --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.store.ts @@ -0,0 +1,40 @@ +import { HttpClient } from '@angular/common/http'; +import { inject } from '@angular/core'; +import { patchState, signalStore, withMethods, withState } from '@ngrx/signals'; +import { firstValueFrom, forkJoin } from 'rxjs'; + +import { XRayVM, buildXRayViewModel } from './x-ray.view-model'; + +export const XRayStore = signalStore( + withState({ + loading: false as boolean, + error: null as string | null, + vm: null as XRayVM | null, + }), + withMethods((store, http = inject(HttpClient)) => ({ + async load(filePath: string): Promise { + patchState(store, { loading: true, error: null }); + + try { + const result$ = http.get( + `/api/x-ray?file=${encodeURIComponent(filePath)}` + ); + const schemaDefault$ = http.get('/api/x-ray/schema'); + const { result, schema } = await firstValueFrom( + forkJoin({ result: result$, schema: schemaDefault$ }) + ); + const vm = buildXRayViewModel(result, schema); + patchState(store, { vm, loading: false, error: null }); + } catch (error: unknown) { + const errorMessage = + error instanceof Error + ? error.message + : 'Failed to load X-Ray analysis'; + patchState(store, { + error: errorMessage, + loading: false, + }); + } + }, + })) +); diff --git a/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.view-model.ts b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.view-model.ts new file mode 100644 index 0000000..be48d2e --- /dev/null +++ b/apps/frontend/src/app/features/trend-analysis/x-ray/x-ray.view-model.ts @@ -0,0 +1,186 @@ +export type MetricVM = { + label: string; + icon?: string; + value: number | string | undefined; + warn: boolean; + tooltip?: string; + refLink?: string; +}; + +export type CardVM = { + title: string; + metrics: MetricVM[]; +}; + +export type TabVM = { + id: string; + title: string; + icon?: string; + cards: CardVM[]; +}; + +export type XRayVM = { + tabs: TabVM[]; +}; + +type Threshold = { + warnGte?: number; + warnGt?: number; + warnLte?: number; + warnLt?: number; +}; + +type MetricDescriptor = { + label: string; + icon?: string; + path: string; + tooltip?: string; + refLink?: string; + threshold?: Threshold; +}; + +type GroupDescriptor = { + title: string; + path: string; + metrics?: MetricDescriptor[]; +}; + +type BaseTabDescriptor = { + id: string; + title: string; + icon?: string; + hideIfEmpty?: boolean; +}; + +type GroupTabDescriptor = BaseTabDescriptor & { + groups: GroupDescriptor[]; +}; + +type CollectionTabDescriptor = BaseTabDescriptor & { + collection: string; + metrics?: MetricDescriptor[]; + itemTitle?: string; +}; + +type TabDescriptor = GroupTabDescriptor | CollectionTabDescriptor; + +type UISchema = { + tabs: TabDescriptor[]; +}; + +type SchemaResponse = + | { + uiSchema?: UISchema; + jsonSchema?: Record; + } + | null + | undefined; + +export function buildXRayViewModel( + result: unknown, + schemaResponse: unknown +): XRayVM { + const schema = schemaResponse as SchemaResponse; + const ui: UISchema = schema?.uiSchema ?? + (schema?.jsonSchema?.['x-ui'] as UISchema | undefined) ?? { tabs: [] }; + + const tabs: TabVM[] = []; + + for (const tab of ui.tabs || []) { + const cards: CardVM[] = []; + + if ('groups' in tab && Array.isArray(tab.groups)) { + for (const group of tab.groups) { + const basePath = group.path; + const metrics: MetricVM[] = (group.metrics ?? []).map( + (m: MetricDescriptor): MetricVM => { + const value = getValueByPath( + result, + `${basePath}.${m.path}` + ); + return { + label: m.label, + icon: m.icon, + value, + warn: isWarn(m, value), + tooltip: m.tooltip, + refLink: m.refLink, + }; + } + ); + cards.push({ title: group.title, metrics }); + } + } else if ('collection' in tab && typeof tab.collection === 'string') { + const entries = getCollectionEntries(result, tab.collection); + for (const { key, value } of entries) { + const metrics: MetricVM[] = (tab.metrics ?? []).map( + (m: MetricDescriptor): MetricVM => { + const v = getValueByPath( + value, + m.path + ); + return { + label: m.label, + icon: m.icon, + value: v, + warn: isWarn(m, v), + tooltip: m.tooltip, + refLink: m.refLink, + }; + } + ); + const title = (tab.itemTitle || '{{key}}').replace('{{key}}', key); + cards.push({ title, metrics }); + } + } + + const rawTitle: string = tab.title || ''; + const titleWithCount = rawTitle.includes('{{count}}') + ? rawTitle.replace('{{count}}', String(cards.length)) + : rawTitle; + + if (tab.hideIfEmpty && cards.length === 0) { + continue; + } + + tabs.push({ id: tab.id, title: titleWithCount, icon: tab.icon, cards }); + } + + return { tabs }; +} + +function getCollectionEntries( + result: unknown, + collectionPath: string +): Array<{ key: string; value: unknown }> { + const obj = getValueByPath>(result, collectionPath); + if (!obj) return []; + return Object.entries(obj).map(([key, value]) => ({ key, value })); +} + +function getValueByPath( + obj: unknown, + path: string +): T | undefined { + const parts = path.split('.'); + let current: unknown = obj; + for (const part of parts) { + if (current && typeof current === 'object') { + const rec = current as Record; + current = rec[part]; + } else { + return undefined; + } + } + return current as T | undefined; +} + +function isWarn(metric: MetricDescriptor, value: unknown): boolean { + if (typeof value !== 'number' || !metric?.threshold) return false; + const t = metric.threshold; + if (t.warnGte !== undefined && value >= t.warnGte) return true; + if (t.warnGt !== undefined && value > t.warnGt) return true; + if (t.warnLte !== undefined && value <= t.warnLte) return true; + if (t.warnLt !== undefined && value < t.warnLt) return true; + return false; +} diff --git a/apps/frontend/src/app/model/trend-analysis-result.ts b/apps/frontend/src/app/model/trend-analysis-result.ts new file mode 100644 index 0000000..fbc609f --- /dev/null +++ b/apps/frontend/src/app/model/trend-analysis-result.ts @@ -0,0 +1,68 @@ +export interface CommitMetric { + commitHash: string; + date: string; + author: string; + message: string; + linesAdded: number; + linesRemoved: number; + totalLines: number; + complexity: number; +} + +export interface TrendPoint { + commit: string; + date: string; + complexity?: number; + lines?: number; +} + +export interface FileTrend { + filePath: string; + changeFrequency: number; + averageComplexity: number; + averageSize: number; + totalChanges: number; + commits: CommitMetric[]; + complexityTrend: TrendPoint[]; + sizeTrend: TrendPoint[]; +} + +export interface TrendAnalysisResult { + files: FileTrend[]; + summary: { + totalProcessingTimeMs: number; + commitsAnalyzed: number; + filesAnalyzed: number; + commitHashes: string[]; + }; +} + +export interface TrendAnalysisProgress { + type: + | 'progress' + | 'commit_complete' + | 'file_analyzed' + | 'complete' + | 'error' + | 'final_result' + | 'initial_files'; + message: string; + progress: number; + commitsProcessed?: number; + totalCommits?: number; + filesAnalyzed?: number; + currentCommit?: string; + currentAuthor?: string; + processingTimeMs?: number; + data?: TrendAnalysisResult; +} + +export const initTrendAnalysisResult: TrendAnalysisResult = { + files: [], + summary: { + totalProcessingTimeMs: 0, + commitsAnalyzed: 0, + filesAnalyzed: 0, + commitHashes: [], + }, +}; diff --git a/apps/frontend/src/app/shell/nav/nav.component.css b/apps/frontend/src/app/shell/nav/nav.component.css deleted file mode 100644 index 5a5c840..0000000 --- a/apps/frontend/src/app/shell/nav/nav.component.css +++ /dev/null @@ -1,48 +0,0 @@ -.sidenav-container { - height: 100%; -} - -/* .sidenav { - width: 350px; -} */ - -.sidenav .mat-toolbar { - background: inherit; -} - -.mat-toolbar.mat-primary { - position: sticky; - top: 0; - z-index: 1; -} - -.active { - background-color: navy; -} - -.app-title { - padding-left: 15px; - margin-right: 20px; -} - -.flex-spacer { - flex: 1 1 auto; -} - -.sidenav-container { - display: flex; - flex-direction: row; - height: 100vh; -} - -mat-sidenav { - flex-shrink: 0; -} - -mat-sidenav-content { - flex-grow: 1; -} - -mat-nav-list { - overflow: hidden; -} diff --git a/apps/frontend/src/app/shell/nav/nav.component.html b/apps/frontend/src/app/shell/nav/nav.component.html index f585354..eeb900d 100644 --- a/apps/frontend/src/app/shell/nav/nav.component.html +++ b/apps/frontend/src/app/shell/nav/nav.component.html @@ -1,12 +1,12 @@ - + Filter @@ -16,18 +16,19 @@ - + - @if (isHandset()) { - } Detective @@ -40,6 +41,9 @@ + + diff --git a/apps/frontend/src/app/shell/nav/nav.component.ts b/apps/frontend/src/app/shell/nav/nav.component.ts index c9ef77b..b50c9c9 100644 --- a/apps/frontend/src/app/shell/nav/nav.component.ts +++ b/apps/frontend/src/app/shell/nav/nav.component.ts @@ -1,6 +1,5 @@ import { BreakpointObserver } from '@angular/cdk/layout'; -import { AsyncPipe } from '@angular/common'; -import { Component, computed, inject, signal } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -10,14 +9,12 @@ import { MatToolbarModule } from '@angular/material/toolbar'; import { RouterModule } from '@angular/router'; import { map, shareReplay } from 'rxjs/operators'; -import { CouplingComponent } from '../../features/coupling/coupling.component'; import { ResizerComponent } from '../../ui/resizer/resizer.component'; import { FilterTreeComponent } from '../filter-tree/filter-tree.component'; @Component({ selector: 'app-nav', templateUrl: './nav.component.html', - styleUrl: './nav.component.css', standalone: true, imports: [ MatToolbarModule, @@ -25,9 +22,7 @@ import { FilterTreeComponent } from '../filter-tree/filter-tree.component'; MatSidenavModule, MatListModule, MatIconModule, - AsyncPipe, FilterTreeComponent, - CouplingComponent, RouterModule, ResizerComponent, ], @@ -42,8 +37,4 @@ export class NavComponent { isHandset = toSignal(this.isHandset$); sidenavWidth = signal(350); - - contentMarginLeft = computed(() => - this.isHandset() ? 0 : this.sidenavWidth() - ); } diff --git a/apps/frontend/src/app/ui/file-icon.pipe.ts b/apps/frontend/src/app/ui/file-icon.pipe.ts new file mode 100644 index 0000000..a843507 --- /dev/null +++ b/apps/frontend/src/app/ui/file-icon.pipe.ts @@ -0,0 +1,30 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'fileIcon', + standalone: true, +}) +export class FileIconPipe implements PipeTransform { + transform(filename: string): string { + const ext = filename.split('.').pop()?.toLowerCase(); + switch (ext) { + case 'ts': + case 'tsx': + return 'code'; + case 'js': + case 'jsx': + return 'javascript'; + case 'html': + return 'html'; + case 'css': + case 'scss': + return 'style'; + case 'json': + return 'data_object'; + case 'md': + return 'description'; + default: + return 'insert_drive_file'; + } + } +} diff --git a/apps/frontend/src/app/ui/file-name.pipe.ts b/apps/frontend/src/app/ui/file-name.pipe.ts new file mode 100644 index 0000000..6a9f14a --- /dev/null +++ b/apps/frontend/src/app/ui/file-name.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'fileName', + standalone: true, +}) +export class FileNamePipe implements PipeTransform { + transform(filePath: string): string { + return filePath.split('/').pop() || filePath; + } +} diff --git a/package-lock.json b/package-lock.json index 40c5bc1..b5c12ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,12 @@ "@angular/platform-browser": "~18.2.0", "@angular/platform-browser-dynamic": "~18.2.0", "@angular/router": "~18.2.0", + "@jscpd/core": "^4.0.1", + "@modelcontextprotocol/sdk": "^1.17.3", "@ngrx/operators": "^18.0.2", "@ngrx/signals": "^18.0.2", "@softarc/sheriff-core": "^0.17.1", + "@types/three": "0.180.0", "axios": "^1.6.0", "chart.js": "^4.4.4", "chartjs-chart-treemap": "^3.1.0", @@ -33,12 +36,19 @@ "cytoscape-qtip": "^2.8.0", "cytoscape-spread": "^3.0.0", "d3": "^7.9.0", + "echarts": "^6.0.0", "express": "^4.18.1", "fast-glob": "^3.3.2", + "isomorphic-git": "^1.33.0", + "jscpd": "^4.0.5", "micromatch": "^4.0.8", "qtip2": "^3.0.3", "rxjs": "~7.8.0", + "three": "0.180.0", + "ts-morph": "^26.0.0", "tslib": "^2.3.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.5", "zone.js": "~0.14.3" }, "devDependencies": { @@ -53,6 +63,7 @@ "@angular/language-service": "~18.2.0", "@commitlint/cli": "^19.4.1", "@commitlint/config-conventional": "^19.4.1", + "@modelcontextprotocol/inspector": "^0.16.5", "@nx/angular": "19.6.4", "@nx/eslint": "19.6.4", "@nx/eslint-plugin": "19.6.4", @@ -106,6 +117,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1100,7 +1125,6 @@ "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==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1110,7 +1134,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1175,7 +1198,6 @@ "version": "7.25.6", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.25.6" @@ -2824,7 +2846,6 @@ "version": "7.25.6", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -2842,6 +2863,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@commitlint/cli": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.5.0.tgz", @@ -3162,6 +3193,12 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", @@ -3768,6 +3805,48 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -4074,6 +4153,27 @@ "node": ">=18" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5023,6 +5123,89 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jscpd/core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.1.tgz", + "integrity": "sha512-6Migc68Z8p7q5xqW1wbF3SfIbYHPQoiLHPbJb1A1Z1H9DwImwopFkYflqRDpuamLd0Jfg2jx3ZBmHQt21NbD1g==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/core/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/@jscpd/finder": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.1.tgz", + "integrity": "sha512-TcCT28686GeLl87EUmrBXYmuOFELVMDwyjKkcId+qjNS1zVWRd53Xd5xKwEDzkCEgen/vCs+lorLLToolXp5oQ==", + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.1", + "@jscpd/tokenizer": "4.0.1", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/finder/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.1.tgz", + "integrity": "sha512-M9fFETNvXXuy4fWv0M2oMluxwrQUBtubxCHaWw21lb2G8A6SE19moe3dUkluZ/3V4BccywfeF9lSEUg84heLww==", + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.1.tgz", + "integrity": "sha512-l/CPeEigadYcQUsUxf1wdCBfNjyAxYcQU04KciFNmSZAMY+ykJ8fZsiuyfjb+oOuDgsIPZZ9YvbvsCr6NBXueg==", + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.1", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, "node_modules/@jsonjoy.com/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", @@ -5193,942 +5376,999 @@ "win32" ] }, - "node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.2.8.tgz", - "integrity": "sha512-6G1qTo1HWvRcN5fzE+SZgvgzSPoq5YqNx8hFL8BttJmnd3wj4SUOFiikAsXhdVrzSK+Zuzg6pipkiLH1m+pbtw==", - "dev": true, - "dependencies": { - "@module-federation/sdk": "0.2.8" - } - }, - "node_modules/@module-federation/dts-plugin": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.2.8.tgz", - "integrity": "sha512-qY1Wbqo0yu9nh6KR8K19t5T4tYtlUbmcNdcaCweISCyAbH99TrhpQkJ89NY0TLtnxQ6uayIYayqAWS7vzyDXVw==", + "node_modules/@modelcontextprotocol/inspector": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.16.5.tgz", + "integrity": "sha512-3viCtcLn1p6ZYZG8L9DxnNx2XUw6damCZM37nBByKAd74stH7nZa2VxFAwLpUCjKylYqawCRthzYSyeSlWGf1g==", "dev": true, "license": "MIT", + "workspaces": [ + "client", + "server", + "cli" + ], "dependencies": { - "@module-federation/managers": "0.2.8", - "@module-federation/sdk": "0.2.8", - "@module-federation/third-party-dts-extractor": "0.2.8", - "adm-zip": "^0.5.10", - "ansi-colors": "^4.1.3", - "axios": "^1.6.7", - "chalk": "3.0.0", - "fs-extra": "9.1.0", - "isomorphic-ws": "5.0.0", - "koa": "2.11.0", - "lodash.clonedeepwith": "4.5.0", - "log4js": "6.9.1", - "node-schedule": "2.1.1", - "rambda": "^9.1.0", - "ws": "8.17.1" + "@modelcontextprotocol/inspector-cli": "^0.16.5", + "@modelcontextprotocol/inspector-client": "^0.16.5", + "@modelcontextprotocol/inspector-server": "^0.16.5", + "@modelcontextprotocol/sdk": "^1.17.3", + "concurrently": "^9.2.0", + "open": "^10.2.0", + "shell-quote": "^1.8.3", + "spawn-rx": "^5.1.2", + "ts-node": "^10.9.2", + "zod": "^3.25.76" }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24" + "bin": { + "mcp-inspector": "cli/build/cli.js" }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } + "engines": { + "node": ">=22.7.5" } }, - "node_modules/@module-federation/dts-plugin/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==", + "node_modules/@modelcontextprotocol/inspector-cli": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-cli/-/inspector-cli-0.16.5.tgz", + "integrity": "sha512-6Flp9goLJutjUZTx6clDo4x/6TA7BwqeTGSYcC8ZeP8IEKjx+EZpvpYPVAV++rkHJYa0F5PViy02CMoGBGkzkg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@modelcontextprotocol/sdk": "^1.17.3", + "commander": "^13.1.0", + "spawn-rx": "^5.1.2" }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "bin": { + "mcp-inspector-cli": "build/cli.js" } }, - "node_modules/@module-federation/dts-plugin/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@modelcontextprotocol/inspector-cli/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@module-federation/dts-plugin/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==", + "node_modules/@modelcontextprotocol/inspector-client": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.16.5.tgz", + "integrity": "sha512-KjgtTRdFSDt964a9KtmF3aRpi4ntd+6wn3e4WUa5vcK7H8f34rq4wfIGF22dd/xoKbALP3zVVAsPqVN94SCGmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.3", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.3", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.3", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.1", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-tooltip": "^1.1.8", + "ajv": "^6.12.6", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.4", + "lucide-react": "^0.523.0", + "pkce-challenge": "^4.1.0", + "prismjs": "^1.30.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-simple-code-editor": "^0.14.1", + "serve-handler": "^6.1.6", + "tailwind-merge": "^2.5.3", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.25.76" + }, + "bin": { + "mcp-inspector-client": "bin/start.js" + } + }, + "node_modules/@modelcontextprotocol/inspector-client/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, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@module-federation/dts-plugin/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==", + "node_modules/@modelcontextprotocol/inspector-client/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, "license": "MIT" }, - "node_modules/@module-federation/dts-plugin/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==", + "node_modules/@modelcontextprotocol/inspector-client/node_modules/pkce-challenge": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz", + "integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=16.20.0" } }, - "node_modules/@module-federation/dts-plugin/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==", + "node_modules/@modelcontextprotocol/inspector-server": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.16.5.tgz", + "integrity": "sha512-mWKrEpimfNdSFOxMJGj3cYN1PxHANW9vjpek+tR0TLiP7ogq7DLV4bbsa+lPPGwOVogUIyUiXbffY8M1YHRZVA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@modelcontextprotocol/sdk": "^1.17.3", + "cors": "^2.8.5", + "express": "^5.1.0", + "ws": "^8.18.0", + "zod": "^3.25.76" }, - "engines": { - "node": ">=8" + "bin": { + "mcp-inspector-server": "build/index.js" } }, - "node_modules/@module-federation/enhanced": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.2.8.tgz", - "integrity": "sha512-6fGM/GiKw6LZiBe6DF8Petz6ih/Yyf3q2htLrx+hrWoDWfWEoWlLvoCUsVkY2UgMCLKid7Fm3Auc4w8A4aRjvQ==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.2.8", - "@module-federation/dts-plugin": "0.2.8", - "@module-federation/managers": "0.2.8", - "@module-federation/manifest": "0.2.8", - "@module-federation/rspack": "0.2.8", - "@module-federation/runtime-tools": "0.2.8", - "@module-federation/sdk": "0.2.8", - "btoa": "^1.2.1", - "upath": "2.0.1" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24", - "webpack": "^5.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vue-tsc": { - "optional": true - }, - "webpack": { - "optional": true - } + "engines": { + "node": ">= 0.6" } }, - "node_modules/@module-federation/managers": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.2.8.tgz", - "integrity": "sha512-S5GXqt2Vrs1+uNXHw7UzZ7m3fs8H3nxNsNGQ0j5+HiT5yA7uRTY1AZJZCGAHzG6XImJ1DzL/SW1acM2Hwj0aAw==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/sdk": "0.2.8", - "find-pkg": "2.0.0", - "fs-extra": "9.1.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@module-federation/manifest": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.2.8.tgz", - "integrity": "sha512-kw4PeAldkOuGCWfCnDzZwPHUx5qv9+WztY5+TEbsgXc5E+/e2NDA6Gg3eT8zUGeexeGdab3f+DuN9ZClZJYVGA==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "0.2.8", - "@module-federation/managers": "0.2.8", - "@module-federation/sdk": "0.2.8", - "chalk": "3.0.0", - "find-pkg": "2.0.0" + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@module-federation/manifest/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==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/inspector-server/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/inspector-server/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@module-federation/manifest/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/@module-federation/manifest/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==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.8" } }, - "node_modules/@module-federation/manifest/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==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/@module-federation/manifest/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==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@module-federation/manifest/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==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/@module-federation/rspack": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.2.8.tgz", - "integrity": "sha512-5Bofm3cY7OOwO2DT5TevITd+HAA03zsY1wwsMb1BP6NkS/ukUtsjuRo2Anua0RkHBEIx+Dv5rpqOn7qSlOm1Fg==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.2.8", - "@module-federation/dts-plugin": "0.2.8", - "@module-federation/managers": "0.2.8", - "@module-federation/manifest": "0.2.8", - "@module-federation/runtime-tools": "0.2.8", - "@module-federation/sdk": "0.2.8" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24" + "mime-db": "^1.54.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vue-tsc": { - "optional": true - } + "engines": { + "node": ">= 0.6" } }, - "node_modules/@module-federation/runtime": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.2.8.tgz", - "integrity": "sha512-8xmA/+z1zD09F5qU8VnSWLExqTCVWoHOguXsCX79kkqp7i0c+D2YaebWzlQ2kku+DU+0VIzXpQ3BBcumZ3v3wQ==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", - "dependencies": { - "@module-federation/sdk": "0.2.8" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@module-federation/runtime-tools": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.2.8.tgz", - "integrity": "sha512-RSNtyhcNvnTQIdzRUIOGue6WQA/9mL9cY/n0dEd357L/lmLCvfHiZbowlkacckDzyApariUHxzkHrU2Q6kzoew==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@module-federation/runtime": "0.2.8", - "@module-federation/webpack-bundler-runtime": "0.2.8" + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@module-federation/sdk": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.2.8.tgz", - "integrity": "sha512-eGMnJxdRDgt6dtMv8gkAlzEbTPWVHb3AHUNUG0w56wcbIF0RHC6kmvpHpSQyq4DVGWv3U4g/ZiH5BvBlqEelDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@module-federation/third-party-dts-extractor": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.2.8.tgz", - "integrity": "sha512-VGXvdsRlljbFUfGeA448CxR7i6fLWJN07ViRuNXYYXc19e4bQVhBHzrf7eCv9ahcf/tA/8YYCS2h11ixbD691A==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "dev": true, "license": "MIT", "dependencies": { - "find-pkg": "2.0.0", - "fs-extra": "9.1.0", - "resolve": "1.22.8" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.2.8.tgz", - "integrity": "sha512-tiW1kD/V3QNul1/O3Y3lwQv/r4sUU4jvWZykrLvHYt2vuoGe1d4tHnSIFEVEAi9FSpuDwdRK2+NaWBr92gIS7Q==", + "node_modules/@modelcontextprotocol/inspector-server/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.2.8", - "@module-federation/sdk": "0.2.8" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], + "node_modules/@modelcontextprotocol/inspector-server/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], + "node_modules/@modelcontextprotocol/inspector-server/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], + "node_modules/@modelcontextprotocol/inspector-server/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "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/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], + "node_modules/@modelcontextprotocol/inspector/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], + "node_modules/@modelcontextprotocol/inspector/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, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "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/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", + "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", - "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "@emnapi/core": "^1.1.0", - "@emnapi/runtime": "^1.1.0", - "@tybys/wasm-util": "^0.9.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@ngrx/operators": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-18.0.2.tgz", - "integrity": "sha512-4dyNkVKBw9ZqECMJu6bT2t21p3ANaMC+ZkcfiBfv5OUEFtB9HhN5uCem9tcgYeNBK8XQFA7/1tzd1T3KaiBY8A==", + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@ngrx/signals": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/@ngrx/signals/-/signals-18.0.2.tgz", - "integrity": "sha512-FXmcY2cmkbhZtg9k8Ntq69SyelGmmb6fWtdButH4T8GGFH0o3f1FZTR829j4ynphy8SzuDhD/pzrnpWcV481oQ==", + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, - "peerDependencies": { - "@angular/core": "^18.0.0", - "rxjs": "^6.5.3 || ^7.4.0" + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" }, - "peerDependenciesMeta": { - "rxjs": { - "optional": true - } + "engines": { + "node": ">= 0.6" } }, - "node_modules/@ngtools/webpack": { - "version": "18.2.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.6.tgz", - "integrity": "sha512-7HwOPE1EOgcHnpt4brSiT8G2CcXB50G0+CbCBaKGy4LYCG3Y3mrlzF5Fup9HvMJ6Tzqd62RqzpKKYBiGUT7hxg==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, - "peerDependencies": { - "@angular/compiler-cli": "^18.0.0", - "typescript": ">=5.4 <5.6", - "webpack": "^5.54.0" + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "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==", + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">= 0.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==", + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.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==", + "node_modules/@modelcontextprotocol/sdk/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==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, "engines": { - "node": ">= 8" + "node": ">= 0.8" } }, - "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", - "dev": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/@npmcli/agent/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, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 14" + "node": ">= 0.6" } }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "license": "ISC", + "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", "dependencies": { - "semver": "^7.3.5" + "side-channel": "^1.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@npmcli/git": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", - "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", - "dev": true, - "license": "ISC", + "node_modules/@modelcontextprotocol/sdk/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "ini": "^4.1.3", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^4.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/@npmcli/git/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, - "license": "ISC", + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">= 18" } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", - "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", - "dev": true, - "license": "ISC", + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 18" } }, - "node_modules/@npmcli/package-json": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", - "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", - "dev": true, - "license": "ISC", + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { - "@npmcli/git": "^5.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^7.0.0", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", - "semver": "^7.5.3" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/@module-federation/bridge-react-webpack-plugin": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.2.8.tgz", + "integrity": "sha512-6G1qTo1HWvRcN5fzE+SZgvgzSPoq5YqNx8hFL8BttJmnd3wj4SUOFiikAsXhdVrzSK+Zuzg6pipkiLH1m+pbtw==", "dev": true, - "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@module-federation/sdk": "0.2.8" } }, - "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@module-federation/dts-plugin": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.2.8.tgz", + "integrity": "sha512-qY1Wbqo0yu9nh6KR8K19t5T4tYtlUbmcNdcaCweISCyAbH99TrhpQkJ89NY0TLtnxQ6uayIYayqAWS7vzyDXVw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "@module-federation/managers": "0.2.8", + "@module-federation/sdk": "0.2.8", + "@module-federation/third-party-dts-extractor": "0.2.8", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.6.7", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.11.0", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.17.1" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } } }, - "node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", - "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "node_modules/@module-federation/dts-plugin/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, - "license": "ISC", + "license": "MIT", "dependencies": { - "which": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "node_modules/@module-federation/dts-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@npmcli/redact": { + "node_modules/@module-federation/dts-plugin/node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", - "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", - "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", - "which": "^4.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/@npmcli/run-script/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "node_modules/@module-federation/dts-plugin/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, - "license": "ISC", - "engines": { - "node": ">=16" - } + "license": "MIT" }, - "node_modules/@npmcli/run-script/node_modules/which": { + "node_modules/@module-federation/dts-plugin/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, + "license": "MIT", "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@nrwl/angular": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/angular/-/angular-19.6.4.tgz", - "integrity": "sha512-pQ8pzKiIjuR83Mp9HagDlbNXjnwxJTe7556Nlmeu16Hdr0qH2ryFIaLzLc+u2M770GEF0RS6Utr75lOMfwYVDw==", + "node_modules/@module-federation/dts-plugin/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, "license": "MIT", "dependencies": { - "@nx/angular": "19.6.4", - "tslib": "^2.3.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@nrwl/devkit": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.6.4.tgz", - "integrity": "sha512-jpr+T5/+21W/fwAMB6zDLZKO+ReYAfOOMIeM8CpeBi/r9nWmjGXaXN9YKwEOYS1fath62Y5ldGK4yZUttv1tkw==", + "node_modules/@module-federation/enhanced": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.2.8.tgz", + "integrity": "sha512-6fGM/GiKw6LZiBe6DF8Petz6ih/Yyf3q2htLrx+hrWoDWfWEoWlLvoCUsVkY2UgMCLKid7Fm3Auc4w8A4aRjvQ==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "19.6.4" - } - }, - "node_modules/@nrwl/eslint-plugin-nx": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-19.6.4.tgz", - "integrity": "sha512-xFzHv6EoMs5+waa75j5B2q+d35onzIo23KeDGoFZVhI0+xnMuIFA0vpDpO3ciRNpF4G8XeSXsf+hUQ70nFRcNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/eslint-plugin": "19.6.4" - } - }, - "node_modules/@nrwl/express": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/express/-/express-19.6.4.tgz", - "integrity": "sha512-fTxKGBVvh1GhH9iDG++XEzHW6cksaasCc6bkFrXyvI6Oti6tbjO/yvmqmZdF+XKIzI5Ht4JJj/2ESe0vflgEDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/express": "19.6.4" - } - }, - "node_modules/@nrwl/jest": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/jest/-/jest-19.6.4.tgz", - "integrity": "sha512-S4OnPESjz79hRpqMqDx/5wQUH31HABcBz8ztxs/gbwvnjQZsSkmg0mAlX9o/7xih+xAluys8srCvFPPiND9sQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/jest": "19.6.4" - } - }, - "node_modules/@nrwl/js": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-19.6.4.tgz", - "integrity": "sha512-yjxxSRST/lWylw4SO1qILg9oiKlBbcVst1nVFZzneTFo2oNuFVSEhluVh5tZ0bz3GZ1RPtHxjiBuWDwQ/QmmAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/js": "19.6.4" - } - }, - "node_modules/@nrwl/node": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/node/-/node-19.6.4.tgz", - "integrity": "sha512-+W6HOwdgHLHmmG9NsLA6/WXI1+AEWDl5gd2QGRM5H/mcyX5rwAPaFYDmyys1TsJbOiUEqemye4E4sogtsPBLiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/node": "19.6.4" - } - }, - "node_modules/@nrwl/tao": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.6.4.tgz", - "integrity": "sha512-1J8cD+MFzsmboiGe03VlQZ8gt64k/TaYYPZivnnhOJolPPs75nz1JyJX55uWcKKRy/b7FZNKWIu/6Wp9JDhJrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "nx": "19.6.4", - "tslib": "^2.3.0" + "@module-federation/bridge-react-webpack-plugin": "0.2.8", + "@module-federation/dts-plugin": "0.2.8", + "@module-federation/managers": "0.2.8", + "@module-federation/manifest": "0.2.8", + "@module-federation/rspack": "0.2.8", + "@module-federation/runtime-tools": "0.2.8", + "@module-federation/sdk": "0.2.8", + "btoa": "^1.2.1", + "upath": "2.0.1" }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nrwl/web": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-19.6.4.tgz", - "integrity": "sha512-d/NpEUz1/JxNAk/19rqFB3S43g3nXutQKzbA0+RoO9nKW/spxxEAR6zJRM4zMJbTbKNbSHefChjXplC3SIF4jQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/web": "19.6.4" - } - }, - "node_modules/@nrwl/webpack": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/webpack/-/webpack-19.6.4.tgz", - "integrity": "sha512-37xm+TVOg+fcEy/tmAGFLGcf2DzOBxd69Q45aILeBm1ff1x+WSr+KjTxwrOR07VqMaqRGOQzQ+Amvu1SUTQeBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/webpack": "19.6.4" + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@nrwl/workspace": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-19.6.4.tgz", - "integrity": "sha512-jjqtRjm5AP39s8oPUEehA6O0Zew6BeTi+Uhjb+ggqbFKH9R9hoSITnbJY670WlO4W8yWYxT8isPf1TNdSHkmBw==", + "node_modules/@module-federation/managers": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.2.8.tgz", + "integrity": "sha512-S5GXqt2Vrs1+uNXHw7UzZ7m3fs8H3nxNsNGQ0j5+HiT5yA7uRTY1AZJZCGAHzG6XImJ1DzL/SW1acM2Hwj0aAw==", "dev": true, "license": "MIT", "dependencies": { - "@nx/workspace": "19.6.4" + "@module-federation/sdk": "0.2.8", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" } }, - "node_modules/@nx/angular": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-19.6.4.tgz", - "integrity": "sha512-0nffEXCR8ic9A6N1amM67MN/PnUr3OHs+bJz/PLMANddzJEkWAvmV42lyXdxqs0dlrFvjewsfgE5RS3jb0LuWw==", + "node_modules/@module-federation/manifest": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.2.8.tgz", + "integrity": "sha512-kw4PeAldkOuGCWfCnDzZwPHUx5qv9+WztY5+TEbsgXc5E+/e2NDA6Gg3eT8zUGeexeGdab3f+DuN9ZClZJYVGA==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/enhanced": "~0.2.3", - "@nrwl/angular": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/eslint": "19.6.4", - "@nx/js": "19.6.4", - "@nx/web": "19.6.4", - "@nx/webpack": "19.6.4", - "@nx/workspace": "19.6.4", - "@phenomnomnominal/tsquery": "~5.0.1", - "@typescript-eslint/type-utils": "^7.16.0", - "chalk": "^4.1.0", - "find-cache-dir": "^3.3.2", - "magic-string": "~0.30.2", - "minimatch": "9.0.3", - "piscina": "^4.4.0", - "semver": "^7.5.3", - "tslib": "^2.3.0", - "webpack": "^5.88.0", - "webpack-merge": "^5.8.0" - }, - "peerDependencies": { - "@angular-devkit/build-angular": ">= 16.0.0 < 19.0.0", - "@angular-devkit/core": ">= 16.0.0 < 19.0.0", - "@angular-devkit/schematics": ">= 16.0.0 < 19.0.0", - "@schematics/angular": ">= 16.0.0 < 19.0.0", - "rxjs": "^6.5.3 || ^7.5.0" + "@module-federation/dts-plugin": "0.2.8", + "@module-federation/managers": "0.2.8", + "@module-federation/sdk": "0.2.8", + "chalk": "3.0.0", + "find-pkg": "2.0.0" } }, - "node_modules/@nx/angular/node_modules/ansi-styles": { + "node_modules/@module-federation/manifest/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==", @@ -6144,10 +6384,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/angular/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@module-federation/manifest/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "license": "MIT", "dependencies": { @@ -6155,13 +6395,10 @@ "supports-color": "^7.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/@nx/angular/node_modules/color-convert": { + "node_modules/@module-federation/manifest/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==", @@ -6174,14 +6411,14 @@ "node": ">=7.0.0" } }, - "node_modules/@nx/angular/node_modules/color-name": { + "node_modules/@module-federation/manifest/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, "license": "MIT" }, - "node_modules/@nx/angular/node_modules/has-flag": { + "node_modules/@module-federation/manifest/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==", @@ -6191,7 +6428,7 @@ "node": ">=8" } }, - "node_modules/@nx/angular/node_modules/supports-color": { + "node_modules/@module-federation/manifest/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==", @@ -6204,95 +6441,1127 @@ "node": ">=8" } }, - "node_modules/@nx/angular/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "node_modules/@module-federation/rspack": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.2.8.tgz", + "integrity": "sha512-5Bofm3cY7OOwO2DT5TevITd+HAA03zsY1wwsMb1BP6NkS/ukUtsjuRo2Anua0RkHBEIx+Dv5rpqOn7qSlOm1Fg==", "dev": true, "license": "MIT", "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" + "@module-federation/bridge-react-webpack-plugin": "0.2.8", + "@module-federation/dts-plugin": "0.2.8", + "@module-federation/managers": "0.2.8", + "@module-federation/manifest": "0.2.8", + "@module-federation/runtime-tools": "0.2.8", + "@module-federation/sdk": "0.2.8" }, - "engines": { - "node": ">=10.0.0" + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } } }, - "node_modules/@nx/devkit": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.6.4.tgz", - "integrity": "sha512-mBitFwb/gcz8MR7STt7KQG0vf+QcsasDXiSYcf3OWpc6lGE5wn1q5jg6Iabp49Bd/mdHXVLQnP1aV5A+QqFIOQ==", + "node_modules/@module-federation/runtime": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.2.8.tgz", + "integrity": "sha512-8xmA/+z1zD09F5qU8VnSWLExqTCVWoHOguXsCX79kkqp7i0c+D2YaebWzlQ2kku+DU+0VIzXpQ3BBcumZ3v3wQ==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/devkit": "19.6.4", - "ejs": "^3.1.7", - "enquirer": "~2.3.6", - "ignore": "^5.0.4", - "minimatch": "9.0.3", - "semver": "^7.5.3", - "tmp": "~0.2.1", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" - }, - "peerDependencies": { - "nx": ">= 17 <= 20" + "@module-federation/sdk": "0.2.8" } }, - "node_modules/@nx/eslint": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-19.6.4.tgz", - "integrity": "sha512-FuuvZ4AHQrD6+TxmB4uy79rUKLBaUyn4XZrFKqJQeBWmchPbpYXbuNXRbVJKtbzVf+WfD4bukcXOD8044oY4rA==", + "node_modules/@module-federation/runtime-tools": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.2.8.tgz", + "integrity": "sha512-RSNtyhcNvnTQIdzRUIOGue6WQA/9mL9cY/n0dEd357L/lmLCvfHiZbowlkacckDzyApariUHxzkHrU2Q6kzoew==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "19.6.4", - "@nx/js": "19.6.4", - "@nx/linter": "19.6.4", - "semver": "^7.5.3", - "tslib": "^2.3.0", - "typescript": "~5.4.2" - }, - "peerDependencies": { - "@zkochan/js-yaml": "0.0.7", - "eslint": "^8.0.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "@zkochan/js-yaml": { - "optional": true - } + "@module-federation/runtime": "0.2.8", + "@module-federation/webpack-bundler-runtime": "0.2.8" } }, - "node_modules/@nx/eslint-plugin": { + "node_modules/@module-federation/sdk": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.2.8.tgz", + "integrity": "sha512-eGMnJxdRDgt6dtMv8gkAlzEbTPWVHb3AHUNUG0w56wcbIF0RHC6kmvpHpSQyq4DVGWv3U4g/ZiH5BvBlqEelDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@module-federation/third-party-dts-extractor": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.2.8.tgz", + "integrity": "sha512-VGXvdsRlljbFUfGeA448CxR7i6fLWJN07ViRuNXYYXc19e4bQVhBHzrf7eCv9ahcf/tA/8YYCS2h11ixbD691A==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-pkg": "2.0.0", + "fs-extra": "9.1.0", + "resolve": "1.22.8" + } + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.2.8.tgz", + "integrity": "sha512-tiW1kD/V3QNul1/O3Y3lwQv/r4sUU4jvWZykrLvHYt2vuoGe1d4tHnSIFEVEAi9FSpuDwdRK2+NaWBr92gIS7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.2.8", + "@module-federation/sdk": "0.2.8" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", + "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emnapi/core": "^1.1.0", + "@emnapi/runtime": "^1.1.0", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@ngrx/operators": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-18.0.2.tgz", + "integrity": "sha512-4dyNkVKBw9ZqECMJu6bT2t21p3ANaMC+ZkcfiBfv5OUEFtB9HhN5uCem9tcgYeNBK8XQFA7/1tzd1T3KaiBY8A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngrx/signals": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/signals/-/signals-18.0.2.tgz", + "integrity": "sha512-FXmcY2cmkbhZtg9k8Ntq69SyelGmmb6fWtdButH4T8GGFH0o3f1FZTR829j4ynphy8SzuDhD/pzrnpWcV481oQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + }, + "peerDependenciesMeta": { + "rxjs": { + "optional": true + } + } + }, + "node_modules/@ngtools/webpack": { + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.6.tgz", + "integrity": "sha512-7HwOPE1EOgcHnpt4brSiT8G2CcXB50G0+CbCBaKGy4LYCG3Y3mrlzF5Fup9HvMJ6Tzqd62RqzpKKYBiGUT7hxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/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, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@nrwl/angular": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/angular/-/angular-19.6.4.tgz", + "integrity": "sha512-pQ8pzKiIjuR83Mp9HagDlbNXjnwxJTe7556Nlmeu16Hdr0qH2ryFIaLzLc+u2M770GEF0RS6Utr75lOMfwYVDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/angular": "19.6.4", + "tslib": "^2.3.0" + } + }, + "node_modules/@nrwl/devkit": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.6.4.tgz", + "integrity": "sha512-jpr+T5/+21W/fwAMB6zDLZKO+ReYAfOOMIeM8CpeBi/r9nWmjGXaXN9YKwEOYS1fath62Y5ldGK4yZUttv1tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/devkit": "19.6.4" + } + }, + "node_modules/@nrwl/eslint-plugin-nx": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-19.6.4.tgz", + "integrity": "sha512-xFzHv6EoMs5+waa75j5B2q+d35onzIo23KeDGoFZVhI0+xnMuIFA0vpDpO3ciRNpF4G8XeSXsf+hUQ70nFRcNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/eslint-plugin": "19.6.4" + } + }, + "node_modules/@nrwl/express": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/express/-/express-19.6.4.tgz", + "integrity": "sha512-fTxKGBVvh1GhH9iDG++XEzHW6cksaasCc6bkFrXyvI6Oti6tbjO/yvmqmZdF+XKIzI5Ht4JJj/2ESe0vflgEDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/express": "19.6.4" + } + }, + "node_modules/@nrwl/jest": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/jest/-/jest-19.6.4.tgz", + "integrity": "sha512-S4OnPESjz79hRpqMqDx/5wQUH31HABcBz8ztxs/gbwvnjQZsSkmg0mAlX9o/7xih+xAluys8srCvFPPiND9sQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/jest": "19.6.4" + } + }, + "node_modules/@nrwl/js": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-19.6.4.tgz", + "integrity": "sha512-yjxxSRST/lWylw4SO1qILg9oiKlBbcVst1nVFZzneTFo2oNuFVSEhluVh5tZ0bz3GZ1RPtHxjiBuWDwQ/QmmAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/js": "19.6.4" + } + }, + "node_modules/@nrwl/node": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/node/-/node-19.6.4.tgz", + "integrity": "sha512-+W6HOwdgHLHmmG9NsLA6/WXI1+AEWDl5gd2QGRM5H/mcyX5rwAPaFYDmyys1TsJbOiUEqemye4E4sogtsPBLiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/node": "19.6.4" + } + }, + "node_modules/@nrwl/tao": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.6.4.tgz", + "integrity": "sha512-1J8cD+MFzsmboiGe03VlQZ8gt64k/TaYYPZivnnhOJolPPs75nz1JyJX55uWcKKRy/b7FZNKWIu/6Wp9JDhJrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "nx": "19.6.4", + "tslib": "^2.3.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/web": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-19.6.4.tgz", + "integrity": "sha512-d/NpEUz1/JxNAk/19rqFB3S43g3nXutQKzbA0+RoO9nKW/spxxEAR6zJRM4zMJbTbKNbSHefChjXplC3SIF4jQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/web": "19.6.4" + } + }, + "node_modules/@nrwl/webpack": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/webpack/-/webpack-19.6.4.tgz", + "integrity": "sha512-37xm+TVOg+fcEy/tmAGFLGcf2DzOBxd69Q45aILeBm1ff1x+WSr+KjTxwrOR07VqMaqRGOQzQ+Amvu1SUTQeBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/webpack": "19.6.4" + } + }, + "node_modules/@nrwl/workspace": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-19.6.4.tgz", + "integrity": "sha512-jjqtRjm5AP39s8oPUEehA6O0Zew6BeTi+Uhjb+ggqbFKH9R9hoSITnbJY670WlO4W8yWYxT8isPf1TNdSHkmBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/workspace": "19.6.4" + } + }, + "node_modules/@nx/angular": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-19.6.4.tgz", + "integrity": "sha512-0nffEXCR8ic9A6N1amM67MN/PnUr3OHs+bJz/PLMANddzJEkWAvmV42lyXdxqs0dlrFvjewsfgE5RS3jb0LuWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@module-federation/enhanced": "~0.2.3", + "@nrwl/angular": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/eslint": "19.6.4", + "@nx/js": "19.6.4", + "@nx/web": "19.6.4", + "@nx/webpack": "19.6.4", + "@nx/workspace": "19.6.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "@typescript-eslint/type-utils": "^7.16.0", + "chalk": "^4.1.0", + "find-cache-dir": "^3.3.2", + "magic-string": "~0.30.2", + "minimatch": "9.0.3", + "piscina": "^4.4.0", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "webpack": "^5.88.0", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">= 16.0.0 < 19.0.0", + "@angular-devkit/core": ">= 16.0.0 < 19.0.0", + "@angular-devkit/schematics": ">= 16.0.0 < 19.0.0", + "@schematics/angular": ">= 16.0.0 < 19.0.0", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@nx/angular/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nx/angular/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, + "license": "MIT", + "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/@nx/angular/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nx/angular/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, + "license": "MIT" + }, + "node_modules/@nx/angular/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/angular/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/angular/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@nx/devkit": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.6.4.tgz", + "integrity": "sha512-mBitFwb/gcz8MR7STt7KQG0vf+QcsasDXiSYcf3OWpc6lGE5wn1q5jg6Iabp49Bd/mdHXVLQnP1aV5A+QqFIOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nrwl/devkit": "19.6.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "minimatch": "9.0.3", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 17 <= 20" + } + }, + "node_modules/@nx/eslint": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-19.6.4.tgz", + "integrity": "sha512-FuuvZ4AHQrD6+TxmB4uy79rUKLBaUyn4XZrFKqJQeBWmchPbpYXbuNXRbVJKtbzVf+WfD4bukcXOD8044oY4rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/devkit": "19.6.4", + "@nx/js": "19.6.4", + "@nx/linter": "19.6.4", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "typescript": "~5.4.2" + }, + "peerDependencies": { + "@zkochan/js-yaml": "0.0.7", + "eslint": "^8.0.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "@zkochan/js-yaml": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin": { "version": "19.6.4", "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-19.6.4.tgz", "integrity": "sha512-H31iaVpZF8T1NPgIlrS0HyyHKzv5NMjsknuxSxt6/ao10dTEe0Xx7N9MoSryWkHjWv6JHn7da5Okp09ELOaaeg==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/eslint-plugin-nx": "19.6.4", + "@nrwl/eslint-plugin-nx": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/js": "19.6.4", + "@typescript-eslint/type-utils": "^7.16.0", + "@typescript-eslint/utils": "^7.16.0", + "chalk": "^4.1.0", + "confusing-browser-globals": "^1.0.9", + "jsonc-eslint-parser": "^2.1.0", + "semver": "^7.5.3", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.13.2 || ^7.0.0", + "eslint-config-prettier": "^9.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nx/eslint-plugin/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, + "license": "MIT", + "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/@nx/eslint-plugin/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nx/eslint-plugin/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, + "license": "MIT" + }, + "node_modules/@nx/eslint-plugin/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/eslint-plugin/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/eslint/node_modules/@nx/linter": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-19.6.4.tgz", + "integrity": "sha512-u0FLxF6fjh2hPZS269I5oz3gTbGfWJG8zZfm3gn0HxUpqUtvX1ccf4jaF62yHGPg8POeQuKggPY2iiMlaktlng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/eslint": "19.6.4" + } + }, + "node_modules/@nx/eslint/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==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@nx/express": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/express/-/express-19.6.4.tgz", + "integrity": "sha512-BMj/wmv3cPNd2YFu1VoIBwKiD7UX4rLjDMMLMOJxm7ATZVNlHn7ecjhjTg60G7XqRK57BiWMWSmW58KetfxW8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nrwl/express": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/node": "19.6.4", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "express": "^4.18.1" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, + "node_modules/@nx/jest": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-19.6.4.tgz", + "integrity": "sha512-IJm8O9H4a6tyCtV6Rg9tuoiXZTYwBZ50eiKCe9enuBj+Pe6K1wEfsAhJQBNYQXc4Vt5vdjyEgn4YZWhmBrNxVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@nrwl/jest": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/js": "19.6.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "chalk": "^4.1.0", + "identity-obj-proxy": "3.0.0", + "jest-config": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-util": "^29.4.1", + "minimatch": "9.0.3", + "resolve.exports": "1.1.0", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "node_modules/@nx/jest/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nx/jest/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, + "license": "MIT", + "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/@nx/jest/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nx/jest/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, + "license": "MIT" + }, + "node_modules/@nx/jest/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/jest/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/js": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-19.6.4.tgz", + "integrity": "sha512-Z09whMis90on3/5V0RIG1On5bRlNI4q2SGZQmj530+BqRTNdKNqxuYdpGf3oNbw5cgLVGiHcWtHzZROaAzpc4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "19.6.4", "@nx/devkit": "19.6.4", - "@nx/js": "19.6.4", - "@typescript-eslint/type-utils": "^7.16.0", - "@typescript-eslint/utils": "^7.16.0", + "@nx/workspace": "19.6.4", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", "chalk": "^4.1.0", - "confusing-browser-globals": "^1.0.9", - "jsonc-eslint-parser": "^2.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "jsonc-parser": "3.2.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.13.2 || ^7.0.0", - "eslint-config-prettier": "^9.0.0" + "verdaccio": "^5.0.4" }, "peerDependenciesMeta": { - "eslint-config-prettier": { + "verdaccio": { "optional": true } } }, - "node_modules/@nx/eslint-plugin/node_modules/ansi-styles": { + "node_modules/@nx/js/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==", @@ -6308,7 +7577,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/eslint-plugin/node_modules/chalk": { + "node_modules/@nx/js/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/@nx/js/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -6321,247 +7602,546 @@ "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nx/js/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/js/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nx/js/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, + "license": "MIT" + }, + "node_modules/@nx/js/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/js/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/@nx/js/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/js/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/js/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nx/js/node_modules/npm-package-arg": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@nx/js/node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/js/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@nx/js/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/js/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, + "license": "ISC" + }, + "node_modules/@nx/js/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, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@nx/js/node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@nx/js/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@nx/eslint-plugin/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==", + "node_modules/@nx/linter": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-19.8.2.tgz", + "integrity": "sha512-5DIx/TmUaxZTuVyeDWJ/Vxj+44IQz6maghUKikOKqete6KCM0rWtRJUHCA8DAeE5kSXss7IZnJXv+KAK4uj25A==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@nx/eslint": "19.8.2" } }, - "node_modules/@nx/eslint-plugin/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==", + "node_modules/@nx/linter/node_modules/@nrwl/devkit": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.8.2.tgz", + "integrity": "sha512-2l3Jb7loE8BnTKn6bl4MK0fKIQLAkl+OMBwo/+GedaqfDfQev+UEgBio38eOEdDHYDHH0lwhGdVQI/DpV4qicA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@nx/devkit": "19.8.2" + } }, - "node_modules/@nx/eslint-plugin/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==", + "node_modules/@nx/linter/node_modules/@nrwl/js": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-19.8.2.tgz", + "integrity": "sha512-S6O7tbb7X75Jov/Hz0LtiywxLqm6YhATeO7CEB6TRHxuJjWvV+y5tCiO2n8iZFrZLu6d9cBJdPCfHaguptXUHg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@nx/js": "19.8.2" } }, - "node_modules/@nx/eslint-plugin/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==", + "node_modules/@nx/linter/node_modules/@nrwl/tao": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.8.2.tgz", + "integrity": "sha512-WvGvFjCy/dSpviLJE8YKcSqpTVpX78UFUhYGgd0OxNlnz0I52HDsZekVWJnyCuU0NDGH6BNmS77R79zj+WzxvQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "nx": "19.8.2", + "tslib": "^2.3.0" }, - "engines": { - "node": ">=8" + "bin": { + "tao": "index.js" } }, - "node_modules/@nx/eslint/node_modules/@nx/linter": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-19.6.4.tgz", - "integrity": "sha512-u0FLxF6fjh2hPZS269I5oz3gTbGfWJG8zZfm3gn0HxUpqUtvX1ccf4jaF62yHGPg8POeQuKggPY2iiMlaktlng==", + "node_modules/@nx/linter/node_modules/@nrwl/workspace": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-19.8.2.tgz", + "integrity": "sha512-4yc1sDoQbEIgVBp6nd+ThozQayFznJFHzQ9s26Hw1BB4t+Juu/daHEh30mkFI3eFJqd0GAnBPqSOKQNGhDGobg==", "dev": true, "license": "MIT", "dependencies": { - "@nx/eslint": "19.6.4" + "@nx/workspace": "19.8.2" } }, - "node_modules/@nx/eslint/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==", + "node_modules/@nx/linter/node_modules/@nx/devkit": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.8.2.tgz", + "integrity": "sha512-SoCPy24hkzyrANbZhc3/40uWXnOIISC0jk49BcapC9Zykv9/8lCxiaNtB68b00QKEFISkxOeA703D7GCC4sA0Q==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "dependencies": { + "@nrwl/devkit": "19.8.2", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "minimatch": "9.0.3", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" }, - "engines": { - "node": ">=14.17" + "peerDependencies": { + "nx": ">= 17 <= 20" } }, - "node_modules/@nx/express": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/express/-/express-19.6.4.tgz", - "integrity": "sha512-BMj/wmv3cPNd2YFu1VoIBwKiD7UX4rLjDMMLMOJxm7ATZVNlHn7ecjhjTg60G7XqRK57BiWMWSmW58KetfxW8A==", + "node_modules/@nx/linter/node_modules/@nx/eslint": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-19.8.2.tgz", + "integrity": "sha512-wXgu4b26dYzMXs6MBdxpS5syYz19Ll71CgT7bytj2wqtyvz5mDwMZ8WBe69BNHs9XVa+of4iVU7tmuj4XvZ9lQ==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/express": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/node": "19.6.4", - "tslib": "^2.3.0" + "@nx/devkit": "19.8.2", + "@nx/js": "19.8.2", + "@nx/linter": "19.8.2", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "typescript": "~5.4.2" }, "peerDependencies": { - "express": "^4.18.1" + "@zkochan/js-yaml": "0.0.7", + "eslint": "^8.0.0 || ^9.0.0" }, "peerDependenciesMeta": { - "express": { + "@zkochan/js-yaml": { "optional": true } } }, - "node_modules/@nx/jest": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-19.6.4.tgz", - "integrity": "sha512-IJm8O9H4a6tyCtV6Rg9tuoiXZTYwBZ50eiKCe9enuBj+Pe6K1wEfsAhJQBNYQXc4Vt5vdjyEgn4YZWhmBrNxVQ==", + "node_modules/@nx/linter/node_modules/@nx/js": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-19.8.2.tgz", + "integrity": "sha512-Ymoful766lTPTj+bUP2+8wcKq9RmTf7cXWxbx2fQGqsdicd06NnzX0SXFUYcIU35SbaVrmeWe0rTYN7iAj2h+Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/reporters": "^29.4.1", - "@jest/test-result": "^29.4.1", - "@nrwl/jest": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/js": "19.6.4", - "@phenomnomnominal/tsquery": "~5.0.1", + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "19.8.2", + "@nx/devkit": "19.8.2", + "@nx/workspace": "19.8.2", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", "chalk": "^4.1.0", - "identity-obj-proxy": "3.0.0", - "jest-config": "^29.4.1", - "jest-resolve": "^29.4.1", - "jest-util": "^29.4.1", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "jsonc-parser": "3.2.0", "minimatch": "9.0.3", - "resolve.exports": "1.1.0", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", "semver": "^7.5.3", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } } }, - "node_modules/@nx/jest/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==", + "node_modules/@nx/linter/node_modules/@nx/nx-darwin-arm64": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.8.2.tgz", + "integrity": "sha512-O06sOObpaF3UQrx6R5s0kFOrhrk/N20rKhOMaD5Qxw6lmVr6TGGH1epGpD8ES7ZPS+p7FUtU9/FPHwY02BZfBg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/linter/node_modules/@nx/nx-darwin-x64": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.8.2.tgz", + "integrity": "sha512-hRFA7xpnIeMUF5FiDh681fxSx/EzkFYZ+UE/XBfzbc+T1neRy7NB2vMEa/WMsN0+Y5+NXtibx1akEDD6VOqeJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/linter/node_modules/@nx/nx-freebsd-x64": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.8.2.tgz", + "integrity": "sha512-GwZUtUQJt2LrZFB9r29ZYQ9I2r76pg+Lwj7vgrFAq+UHcLejHYyLvhDPoRfKWdASdegI3M5jbh8Cvamd+sgbNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.8.2.tgz", + "integrity": "sha512-+OtoU5tXOLRv0ufy8ifD6EHn+VOjnC8mFIaaBO/cb/YEW1MTZq1RqKd4e1O9sjAloTe4X3mydw/Ue333+FqIww==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm64-gnu": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.8.2.tgz", + "integrity": "sha512-rH7WSvoh1nvYmQs3cd4nBDPilEYIGTUOZF2eXPBqSu1K6938tu1Uf1zXzqRK7o016GoVepiD0VRVYWD3R82nRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 10" } }, - "node_modules/@nx/jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm64-musl": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.8.2.tgz", + "integrity": "sha512-a7vuWDOcqHL0S0gQYYz8DDRmNFs4NOd7A+BTgBRPX54r0pS82tKF2ZsP48TAr9WHyjsTPis5LlFw8VhLrjzdLA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 10" } }, - "node_modules/@nx/jest/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==", + "node_modules/@nx/linter/node_modules/@nx/nx-linux-x64-gnu": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.8.2.tgz", + "integrity": "sha512-3h4dmIi5Muym18dsiiXQBygPlSAHZNe3PaYo8mLsUsvuAt2ye0XUDcAlHWXOt/FeuVDG1NEGI05vZJvbIIGikQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=7.0.0" + "node": ">= 10" } }, - "node_modules/@nx/jest/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==", + "node_modules/@nx/linter/node_modules/@nx/nx-linux-x64-musl": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.8.2.tgz", + "integrity": "sha512-LbOC3rbnREh7DbFYdZDuAEDmJsdQDLEjUzacwXDHMb/XlTL3YpWoXohd+zSVHM4nvd8o7QFuZNC4a4zYXwA+wg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@nx/jest/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==", + "node_modules/@nx/linter/node_modules/@nx/nx-win32-arm64-msvc": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.8.2.tgz", + "integrity": "sha512-ZkSZBxGrGXDqwRxC4WyHR3sAUIH6akk1rTDvqTr1nKPribs53cqEms20i7qF1at3o99xL3YairOcnt7JxNWDWA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/@nx/jest/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==", + "node_modules/@nx/linter/node_modules/@nx/nx-win32-x64-msvc": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.8.2.tgz", + "integrity": "sha512-rRt+XIZk+ctxhFORWvugqmS07xi52eRS4QpTq8b24ZJKk1Zw0L5opsXAdzughhBzfIpSx4rxnknFlI78DcRPxA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/@nx/js": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/js/-/js-19.6.4.tgz", - "integrity": "sha512-Z09whMis90on3/5V0RIG1On5bRlNI4q2SGZQmj530+BqRTNdKNqxuYdpGf3oNbw5cgLVGiHcWtHzZROaAzpc4g==", + "node_modules/@nx/linter/node_modules/@nx/workspace": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-19.8.2.tgz", + "integrity": "sha512-oJ8f4ZdwXspoGVzpeHNr5SMdAlEe4h72BE75ztNtNdYIl0GsmjH03g7KeBoDI97DwdKuQLoVZ5nWE/MyABLwOg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.23.2", - "@babel/plugin-proposal-decorators": "^7.22.7", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@nrwl/js": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/workspace": "19.6.4", - "babel-plugin-const-enum": "^1.0.1", - "babel-plugin-macros": "^2.8.0", - "babel-plugin-transform-typescript-metadata": "^0.3.1", + "@nrwl/workspace": "19.8.2", + "@nx/devkit": "19.8.2", "chalk": "^4.1.0", - "columnify": "^1.6.0", - "detect-port": "^1.5.1", - "fast-glob": "3.2.7", - "fs-extra": "^11.1.0", - "ignore": "^5.0.4", - "js-tokens": "^4.0.0", - "jsonc-parser": "3.2.0", - "minimatch": "9.0.3", - "npm-package-arg": "11.0.1", - "npm-run-path": "^4.0.1", - "ora": "5.3.0", - "semver": "^7.5.3", - "source-map-support": "0.5.19", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "verdaccio": "^5.0.4" - }, - "peerDependenciesMeta": { - "verdaccio": { - "optional": true - } + "enquirer": "~2.3.6", + "nx": "19.8.2", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" } }, - "node_modules/@nx/js/node_modules/ansi-styles": { + "node_modules/@nx/linter/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==", @@ -6577,7 +8157,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/js/node_modules/babel-plugin-macros": { + "node_modules/@nx/linter/node_modules/babel-plugin-macros": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", @@ -6589,7 +8169,7 @@ "resolve": "^1.12.0" } }, - "node_modules/@nx/js/node_modules/chalk": { + "node_modules/@nx/linter/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -6606,7 +8186,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nx/js/node_modules/cli-cursor": { + "node_modules/@nx/linter/node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", @@ -6619,7 +8199,7 @@ "node": ">=8" } }, - "node_modules/@nx/js/node_modules/color-convert": { + "node_modules/@nx/linter/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==", @@ -6632,14 +8212,14 @@ "node": ">=7.0.0" } }, - "node_modules/@nx/js/node_modules/color-name": { + "node_modules/@nx/linter/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, "license": "MIT" }, - "node_modules/@nx/js/node_modules/cosmiconfig": { + "node_modules/@nx/linter/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", @@ -6656,7 +8236,24 @@ "node": ">=8" } }, - "node_modules/@nx/js/node_modules/fast-glob": { + "node_modules/@nx/linter/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/linter/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, + "license": "MIT" + }, + "node_modules/@nx/linter/node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", @@ -6673,39 +8270,73 @@ "node": ">=8" } }, - "node_modules/@nx/js/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/@nx/linter/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, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/linter/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">=14.14" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nx/js/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==", + "node_modules/@nx/linter/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, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/@nx/js/node_modules/jsonc-parser": { + "node_modules/@nx/linter/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/linter/node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true, "license": "MIT" }, - "node_modules/@nx/js/node_modules/npm-package-arg": { + "node_modules/@nx/linter/node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@nx/linter/node_modules/npm-package-arg": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", @@ -6721,7 +8352,97 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@nx/js/node_modules/ora": { + "node_modules/@nx/linter/node_modules/nx": { + "version": "19.8.2", + "resolved": "https://registry.npmjs.org/nx/-/nx-19.8.2.tgz", + "integrity": "sha512-NE88CbEZj8hCrUKiYzL1sB6O1tmgu/OjvTp3pJOoROMvo0kE7N4XT3TiKAge+E6wVRXf/zU55cH1G2u0djpZhA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@napi-rs/wasm-runtime": "0.2.4", + "@nrwl/tao": "19.8.2", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.7", + "axios": "^1.7.4", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "front-matter": "^4.0.2", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "jsonc-parser": "3.2.0", + "lines-and-columns": "2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "19.8.2", + "@nx/nx-darwin-x64": "19.8.2", + "@nx/nx-freebsd-x64": "19.8.2", + "@nx/nx-linux-arm-gnueabihf": "19.8.2", + "@nx/nx-linux-arm64-gnu": "19.8.2", + "@nx/nx-linux-arm64-musl": "19.8.2", + "@nx/nx-linux-x64-gnu": "19.8.2", + "@nx/nx-linux-x64-musl": "19.8.2", + "@nx/nx-win32-arm64-msvc": "19.8.2", + "@nx/nx-win32-x64-msvc": "19.8.2" + }, + "peerDependencies": { + "@swc-node/register": "^1.8.0", + "@swc/core": "^1.3.85" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nx/linter/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/linter/node_modules/ora": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", @@ -6744,7 +8465,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nx/js/node_modules/proc-log": { + "node_modules/@nx/linter/node_modules/proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", @@ -6754,7 +8475,7 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nx/js/node_modules/restore-cursor": { + "node_modules/@nx/linter/node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", @@ -6768,14 +8489,14 @@ "node": ">=8" } }, - "node_modules/@nx/js/node_modules/signal-exit": { + "node_modules/@nx/linter/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, "license": "ISC" }, - "node_modules/@nx/js/node_modules/source-map": { + "node_modules/@nx/linter/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==", @@ -6785,7 +8506,7 @@ "node": ">=0.10.0" } }, - "node_modules/@nx/js/node_modules/source-map-support": { + "node_modules/@nx/linter/node_modules/source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", @@ -6796,169 +8517,67 @@ "source-map": "^0.6.0" } }, - "node_modules/@nx/js/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==", + "node_modules/@nx/linter/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, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/@nx/linter": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-19.8.2.tgz", - "integrity": "sha512-5DIx/TmUaxZTuVyeDWJ/Vxj+44IQz6maghUKikOKqete6KCM0rWtRJUHCA8DAeE5kSXss7IZnJXv+KAK4uj25A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/eslint": "19.8.2" - } - }, - "node_modules/@nx/linter/node_modules/@nrwl/devkit": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.8.2.tgz", - "integrity": "sha512-2l3Jb7loE8BnTKn6bl4MK0fKIQLAkl+OMBwo/+GedaqfDfQev+UEgBio38eOEdDHYDHH0lwhGdVQI/DpV4qicA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/devkit": "19.8.2" - } - }, - "node_modules/@nx/linter/node_modules/@nrwl/js": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-19.8.2.tgz", - "integrity": "sha512-S6O7tbb7X75Jov/Hz0LtiywxLqm6YhATeO7CEB6TRHxuJjWvV+y5tCiO2n8iZFrZLu6d9cBJdPCfHaguptXUHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/js": "19.8.2" - } - }, - "node_modules/@nx/linter/node_modules/@nrwl/tao": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.8.2.tgz", - "integrity": "sha512-WvGvFjCy/dSpviLJE8YKcSqpTVpX78UFUhYGgd0OxNlnz0I52HDsZekVWJnyCuU0NDGH6BNmS77R79zj+WzxvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "nx": "19.8.2", - "tslib": "^2.3.0" - }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nx/linter/node_modules/@nrwl/workspace": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-19.8.2.tgz", - "integrity": "sha512-4yc1sDoQbEIgVBp6nd+ThozQayFznJFHzQ9s26Hw1BB4t+Juu/daHEh30mkFI3eFJqd0GAnBPqSOKQNGhDGobg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nx/workspace": "19.8.2" - } - }, - "node_modules/@nx/linter/node_modules/@nx/devkit": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.8.2.tgz", - "integrity": "sha512-SoCPy24hkzyrANbZhc3/40uWXnOIISC0jk49BcapC9Zykv9/8lCxiaNtB68b00QKEFISkxOeA703D7GCC4sA0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nrwl/devkit": "19.8.2", - "ejs": "^3.1.7", - "enquirer": "~2.3.6", - "ignore": "^5.0.4", - "minimatch": "9.0.3", - "semver": "^7.5.3", - "tmp": "~0.2.1", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" - }, - "peerDependencies": { - "nx": ">= 17 <= 20" - } - }, - "node_modules/@nx/linter/node_modules/@nx/eslint": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-19.8.2.tgz", - "integrity": "sha512-wXgu4b26dYzMXs6MBdxpS5syYz19Ll71CgT7bytj2wqtyvz5mDwMZ8WBe69BNHs9XVa+of4iVU7tmuj4XvZ9lQ==", + "node_modules/@nx/linter/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, "license": "MIT", "dependencies": { - "@nx/devkit": "19.8.2", - "@nx/js": "19.8.2", - "@nx/linter": "19.8.2", - "semver": "^7.5.3", - "tslib": "^2.3.0", - "typescript": "~5.4.2" - }, - "peerDependencies": { - "@zkochan/js-yaml": "0.0.7", - "eslint": "^8.0.0 || ^9.0.0" + "has-flag": "^4.0.0" }, - "peerDependenciesMeta": { - "@zkochan/js-yaml": { - "optional": true - } + "engines": { + "node": ">=8" } - }, - "node_modules/@nx/linter/node_modules/@nx/js": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/js/-/js-19.8.2.tgz", - "integrity": "sha512-Ymoful766lTPTj+bUP2+8wcKq9RmTf7cXWxbx2fQGqsdicd06NnzX0SXFUYcIU35SbaVrmeWe0rTYN7iAj2h+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/plugin-proposal-decorators": "^7.22.7", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@nrwl/js": "19.8.2", - "@nx/devkit": "19.8.2", - "@nx/workspace": "19.8.2", - "babel-plugin-const-enum": "^1.0.1", - "babel-plugin-macros": "^2.8.0", - "babel-plugin-transform-typescript-metadata": "^0.3.1", - "chalk": "^4.1.0", - "columnify": "^1.6.0", - "detect-port": "^1.5.1", - "enquirer": "~2.3.6", - "fast-glob": "3.2.7", - "ignore": "^5.0.4", - "js-tokens": "^4.0.0", - "jsonc-parser": "3.2.0", - "minimatch": "9.0.3", - "npm-package-arg": "11.0.1", - "npm-run-path": "^4.0.1", - "ora": "5.3.0", - "semver": "^7.5.3", - "source-map-support": "0.5.19", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "verdaccio": "^5.0.4" + }, + "node_modules/@nx/linter/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==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "peerDependenciesMeta": { - "verdaccio": { - "optional": true - } + "engines": { + "node": ">=14.17" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-darwin-arm64": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.8.2.tgz", - "integrity": "sha512-O06sOObpaF3UQrx6R5s0kFOrhrk/N20rKhOMaD5Qxw6lmVr6TGGH1epGpD8ES7ZPS+p7FUtU9/FPHwY02BZfBg==", + "node_modules/@nx/node": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/node/-/node-19.6.4.tgz", + "integrity": "sha512-RFzoiuPRHzLEMFrb57gQZYBdsLz5Fbt9ZYh7c398xatEuqoj3BDXUg4wbSF24SkDQKrqZCAwOXVbCOxmUwrzVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nrwl/node": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/eslint": "19.6.4", + "@nx/jest": "19.6.4", + "@nx/js": "19.6.4", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/nx-darwin-arm64": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.6.4.tgz", + "integrity": "sha512-kRn2FLvhwJA/TJrNlsCSqqQTrguNZLmiRsiXhvjkfUMbUKwyQfVMgJlvkZ+KoqraUSG+Qyb0FmrGur1I/Mld0Q==", "cpu": [ "arm64" ], @@ -6972,10 +8591,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-darwin-x64": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.8.2.tgz", - "integrity": "sha512-hRFA7xpnIeMUF5FiDh681fxSx/EzkFYZ+UE/XBfzbc+T1neRy7NB2vMEa/WMsN0+Y5+NXtibx1akEDD6VOqeJA==", + "node_modules/@nx/nx-darwin-x64": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.6.4.tgz", + "integrity": "sha512-3uABBUhxVk+SdRwpUu30iuBlgRWm3tA/G9seG+wt7oN2R+fOu8zzRCYa+Blvoh1Ef+D9743Ir4rDc9Mhzl2B2g==", "cpu": [ "x64" ], @@ -6989,10 +8608,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-freebsd-x64": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.8.2.tgz", - "integrity": "sha512-GwZUtUQJt2LrZFB9r29ZYQ9I2r76pg+Lwj7vgrFAq+UHcLejHYyLvhDPoRfKWdASdegI3M5jbh8Cvamd+sgbNA==", + "node_modules/@nx/nx-freebsd-x64": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.6.4.tgz", + "integrity": "sha512-OG83MiEk5L54/vAldmwZJBKEvZaM+DEIDqn2yZLTToBRj5Z9jwKJX3jKP60xbHiaT/hzsb1xPlwhOHJnYd80EQ==", "cpu": [ "x64" ], @@ -7006,10 +8625,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.8.2.tgz", - "integrity": "sha512-+OtoU5tXOLRv0ufy8ifD6EHn+VOjnC8mFIaaBO/cb/YEW1MTZq1RqKd4e1O9sjAloTe4X3mydw/Ue333+FqIww==", + "node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.6.4.tgz", + "integrity": "sha512-hQ9x4qSKUh9mIVDuD270ULrBnmYfDTjXq7LnIwECw1AuP4LkKzKxULhsbqVnFQ/k3xxyFkwyGTIu2mfDcw16Gw==", "cpu": [ "arm" ], @@ -7023,10 +8642,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.8.2.tgz", - "integrity": "sha512-rH7WSvoh1nvYmQs3cd4nBDPilEYIGTUOZF2eXPBqSu1K6938tu1Uf1zXzqRK7o016GoVepiD0VRVYWD3R82nRQ==", + "node_modules/@nx/nx-linux-arm64-gnu": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.6.4.tgz", + "integrity": "sha512-OFKpAHiaVg3YGFIMBmi/JshciP9buwtOPiHDXcQdzQgE22jyYzKEiFxfcpG0nCT8PlMYAbHPAda15WfWkfVGVQ==", "cpu": [ "arm64" ], @@ -7040,10 +8659,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.8.2.tgz", - "integrity": "sha512-a7vuWDOcqHL0S0gQYYz8DDRmNFs4NOd7A+BTgBRPX54r0pS82tKF2ZsP48TAr9WHyjsTPis5LlFw8VhLrjzdLA==", + "node_modules/@nx/nx-linux-arm64-musl": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.6.4.tgz", + "integrity": "sha512-ZIR9u+mN0A7SmNd6vDxmPV1QVTgYPTdfBSM5TEnKl3q2fHw2Nkui81QBxA4d7VopJoJUz/pRHiUV+dlgEEZ6nA==", "cpu": [ "arm64" ], @@ -7057,10 +8676,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.8.2.tgz", - "integrity": "sha512-3h4dmIi5Muym18dsiiXQBygPlSAHZNe3PaYo8mLsUsvuAt2ye0XUDcAlHWXOt/FeuVDG1NEGI05vZJvbIIGikQ==", + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.6.4.tgz", + "integrity": "sha512-AUMPvLs9KeCUuWD5DdlpbP3VfVsiD0IlptS2b3ul336rsQ7LwwdvE7jTVO5CixFOsiRZxP72fKJhaEargMn5Aw==", "cpu": [ "x64" ], @@ -7074,10 +8693,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-linux-x64-musl": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.8.2.tgz", - "integrity": "sha512-LbOC3rbnREh7DbFYdZDuAEDmJsdQDLEjUzacwXDHMb/XlTL3YpWoXohd+zSVHM4nvd8o7QFuZNC4a4zYXwA+wg==", + "node_modules/@nx/nx-linux-x64-musl": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.6.4.tgz", + "integrity": "sha512-PU7AaBlrgnJnDxTiV/PNCu0pHUCzaogm6uNcbzCyFJLGn7DoQK9rkqUMPJjb3CnJkAj9XrrhuZwmOdbrhvHAvA==", "cpu": [ "x64" ], @@ -7091,10 +8710,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.8.2.tgz", - "integrity": "sha512-ZkSZBxGrGXDqwRxC4WyHR3sAUIH6akk1rTDvqTr1nKPribs53cqEms20i7qF1at3o99xL3YairOcnt7JxNWDWA==", + "node_modules/@nx/nx-win32-arm64-msvc": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.6.4.tgz", + "integrity": "sha512-6CCmGWwH/J2k+Uxeci48w4QVhtcQ3hRZ5Z2jh26HI8YzH4wqZyA7QPgLBE6sNCPVLoGW5cBgTsfnyEdr+xarQA==", "cpu": [ "arm64" ], @@ -7108,10 +8727,10 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.8.2.tgz", - "integrity": "sha512-rRt+XIZk+ctxhFORWvugqmS07xi52eRS4QpTq8b24ZJKk1Zw0L5opsXAdzughhBzfIpSx4rxnknFlI78DcRPxA==", + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.6.4.tgz", + "integrity": "sha512-jTNrlaFaKtbL2mYOcfPAiljtpF5CI7vbHIqYqBFYLUQXOwW9lOHlO+SeQnft6JYZs0FIr1IdHaCfdOw/hpnCiQ==", "cpu": [ "x64" ], @@ -7125,23 +8744,23 @@ "node": ">= 10" } }, - "node_modules/@nx/linter/node_modules/@nx/workspace": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-19.8.2.tgz", - "integrity": "sha512-oJ8f4ZdwXspoGVzpeHNr5SMdAlEe4h72BE75ztNtNdYIl0GsmjH03g7KeBoDI97DwdKuQLoVZ5nWE/MyABLwOg==", + "node_modules/@nx/web": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-19.6.4.tgz", + "integrity": "sha512-RcDfN+bTjCFlXdGC2UkpPguPP6M+g4mMf5dWBvD54Fs56/KKhMxRBjYXRSuLn7OLrdc6QTkU/2FQT/6lzrecgg==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/workspace": "19.8.2", - "@nx/devkit": "19.8.2", + "@nrwl/web": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/js": "19.6.4", "chalk": "^4.1.0", - "enquirer": "~2.3.6", - "nx": "19.8.2", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" } }, - "node_modules/@nx/linter/node_modules/ansi-styles": { + "node_modules/@nx/web/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==", @@ -7157,19 +8776,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/linter/node_modules/babel-plugin-macros": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.7.2", - "cosmiconfig": "^6.0.0", - "resolve": "^1.12.0" - } - }, - "node_modules/@nx/linter/node_modules/chalk": { + "node_modules/@nx/web/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7186,20 +8793,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nx/linter/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/linter/node_modules/color-convert": { + "node_modules/@nx/web/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==", @@ -7212,555 +8806,518 @@ "node": ">=7.0.0" } }, - "node_modules/@nx/linter/node_modules/color-name": { + "node_modules/@nx/web/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, "license": "MIT" }, - "node_modules/@nx/linter/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/linter/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "node_modules/@nx/web/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, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/@nx/linter/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, - "license": "MIT" - }, - "node_modules/@nx/linter/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "node_modules/@nx/web/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, "license": "MIT", "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" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@nx/linter/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==", + "node_modules/@nx/webpack": { + "version": "19.6.4", + "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-19.6.4.tgz", + "integrity": "sha512-VG1DoAPZgPanyh2x+n3pgqKwhy7qYZpNdugqQ4d4dkJ9VEiM6KOhckwAkUBJ1lb66w8glqkcThP1X9wunwI9jg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/core": "^7.23.2", + "@module-federation/enhanced": "^0.2.3", + "@module-federation/sdk": "^0.2.3", + "@nrwl/webpack": "19.6.4", + "@nx/devkit": "19.6.4", + "@nx/js": "19.6.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "ajv": "^8.12.0", + "autoprefixer": "^10.4.9", + "babel-loader": "^9.1.2", + "browserslist": "^4.21.4", + "chalk": "^4.1.0", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "^6.4.0", + "css-minimizer-webpack-plugin": "^5.0.0", + "express": "^4.19.2", + "fork-ts-checker-webpack-plugin": "7.2.13", + "http-proxy-middleware": "^3.0.0", + "less": "4.1.3", + "less-loader": "11.1.0", + "license-webpack-plugin": "^4.0.2", + "loader-utils": "^2.0.3", + "mini-css-extract-plugin": "~2.4.7", + "parse5": "4.0.0", + "postcss": "^8.4.38", + "postcss-import": "~14.1.0", + "postcss-loader": "^6.1.1", + "rxjs": "^7.8.0", + "sass": "^1.42.1", + "sass-loader": "^12.2.0", + "source-map-loader": "^5.0.0", + "style-loader": "^3.3.0", + "stylus": "^0.59.0", + "stylus-loader": "^7.1.0", + "terser-webpack-plugin": "^5.3.3", + "ts-loader": "^9.3.1", + "tsconfig-paths-webpack-plugin": "4.0.0", + "tslib": "^2.3.0", + "webpack": "^5.80.0", + "webpack-dev-server": "^5.0.4", + "webpack-node-externals": "^3.0.0", + "webpack-subresource-integrity": "^5.1.0" } }, - "node_modules/@nx/linter/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "node_modules/@nx/webpack/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, "license": "MIT", - "bin": { - "is-docker": "cli.js" + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/linter/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==", + "node_modules/@nx/webpack/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nx/linter/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/@nx/webpack/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, "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nx/linter/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/linter/node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "node_modules/@nx/webpack/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, "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@nx/linter/node_modules/npm-package-arg": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", - "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", - "dev": true, - "license": "ISC", "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/@nx/linter/node_modules/nx": { - "version": "19.8.2", - "resolved": "https://registry.npmjs.org/nx/-/nx-19.8.2.tgz", - "integrity": "sha512-NE88CbEZj8hCrUKiYzL1sB6O1tmgu/OjvTp3pJOoROMvo0kE7N4XT3TiKAge+E6wVRXf/zU55cH1G2u0djpZhA==", + "node_modules/@nx/webpack/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, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@napi-rs/wasm-runtime": "0.2.4", - "@nrwl/tao": "19.8.2", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", - "@zkochan/js-yaml": "0.0.7", - "axios": "^1.7.4", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^8.0.1", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "enquirer": "~2.3.6", - "figures": "3.2.0", - "flat": "^5.0.2", - "front-matter": "^4.0.2", - "ignore": "^5.0.4", - "jest-diff": "^29.4.1", - "jsonc-parser": "3.2.0", - "lines-and-columns": "2.0.3", - "minimatch": "9.0.3", - "node-machine-id": "1.1.12", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "ora": "5.3.0", - "semver": "^7.5.3", - "string-width": "^4.2.3", - "strong-log-transformer": "^2.1.0", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js", - "nx-cloud": "bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "19.8.2", - "@nx/nx-darwin-x64": "19.8.2", - "@nx/nx-freebsd-x64": "19.8.2", - "@nx/nx-linux-arm-gnueabihf": "19.8.2", - "@nx/nx-linux-arm64-gnu": "19.8.2", - "@nx/nx-linux-arm64-musl": "19.8.2", - "@nx/nx-linux-x64-gnu": "19.8.2", - "@nx/nx-linux-x64-musl": "19.8.2", - "@nx/nx-win32-arm64-msvc": "19.8.2", - "@nx/nx-win32-x64-msvc": "19.8.2" - }, - "peerDependencies": { - "@swc-node/register": "^1.8.0", - "@swc/core": "^1.3.85" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } + "license": "MIT" }, - "node_modules/@nx/linter/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "node_modules/@nx/webpack/node_modules/copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", "dev": true, "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">= 12.20.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, - "node_modules/@nx/linter/node_modules/ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "node_modules/@nx/webpack/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@nx/linter/node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nx/linter/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/@nx/webpack/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/linter/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, - "license": "ISC" - }, - "node_modules/@nx/linter/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, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@nx/linter/node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "node_modules/@nx/webpack/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/@nx/linter/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==", + "node_modules/@nx/webpack/node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nx/linter/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==", + "node_modules/@nx/webpack/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, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/@nx/linter/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==", + "node_modules/@nx/webpack/node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "lessc": "bin/lessc" }, "engines": { - "node": ">=14.17" + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" } }, - "node_modules/@nx/node": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/node/-/node-19.6.4.tgz", - "integrity": "sha512-RFzoiuPRHzLEMFrb57gQZYBdsLz5Fbt9ZYh7c398xatEuqoj3BDXUg4wbSF24SkDQKrqZCAwOXVbCOxmUwrzVw==", + "node_modules/@nx/webpack/node_modules/less-loader": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", + "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/node": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/eslint": "19.6.4", - "@nx/jest": "19.6.4", - "@nx/js": "19.6.4", - "tslib": "^2.3.0" + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" } }, - "node_modules/@nx/nx-darwin-arm64": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.6.4.tgz", - "integrity": "sha512-kRn2FLvhwJA/TJrNlsCSqqQTrguNZLmiRsiXhvjkfUMbUKwyQfVMgJlvkZ+KoqraUSG+Qyb0FmrGur1I/Mld0Q==", - "cpu": [ - "arm64" - ], + "node_modules/@nx/webpack/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, "engines": { - "node": ">= 10" + "node": ">=8.9.0" } }, - "node_modules/@nx/nx-darwin-x64": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.6.4.tgz", - "integrity": "sha512-3uABBUhxVk+SdRwpUu30iuBlgRWm3tA/G9seG+wt7oN2R+fOu8zzRCYa+Blvoh1Ef+D9743Ir4rDc9Mhzl2B2g==", - "cpu": [ - "x64" - ], + "node_modules/@nx/webpack/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, "engines": { - "node": ">= 10" + "node": ">=6" } }, - "node_modules/@nx/nx-freebsd-x64": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.6.4.tgz", - "integrity": "sha512-OG83MiEk5L54/vAldmwZJBKEvZaM+DEIDqn2yZLTToBRj5Z9jwKJX3jKP60xbHiaT/hzsb1xPlwhOHJnYd80EQ==", - "cpu": [ - "x64" - ], + "node_modules/@nx/webpack/node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", + "license": "ISC", "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" + "bin": { + "semver": "bin/semver" } }, - "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.6.4.tgz", - "integrity": "sha512-hQ9x4qSKUh9mIVDuD270ULrBnmYfDTjXq7LnIwECw1AuP4LkKzKxULhsbqVnFQ/k3xxyFkwyGTIu2mfDcw16Gw==", - "cpu": [ - "arm" - ], + "node_modules/@nx/webpack/node_modules/mini-css-extract-plugin": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", + "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "schema-utils": "^4.0.0" + }, "engines": { - "node": ">= 10" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.6.4.tgz", - "integrity": "sha512-OFKpAHiaVg3YGFIMBmi/JshciP9buwtOPiHDXcQdzQgE22jyYzKEiFxfcpG0nCT8PlMYAbHPAda15WfWkfVGVQ==", - "cpu": [ - "arm64" - ], + "node_modules/@nx/webpack/node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nx/webpack/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">=6" } }, - "node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.6.4.tgz", - "integrity": "sha512-ZIR9u+mN0A7SmNd6vDxmPV1QVTgYPTdfBSM5TEnKl3q2fHw2Nkui81QBxA4d7VopJoJUz/pRHiUV+dlgEEZ6nA==", - "cpu": [ - "arm64" - ], + "node_modules/@nx/webpack/node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, "engines": { - "node": ">= 10" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" } }, - "node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.6.4.tgz", - "integrity": "sha512-AUMPvLs9KeCUuWD5DdlpbP3VfVsiD0IlptS2b3ul336rsQ7LwwdvE7jTVO5CixFOsiRZxP72fKJhaEargMn5Aw==", - "cpu": [ - "x64" - ], + "node_modules/@nx/webpack/node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } } }, - "node_modules/@nx/nx-linux-x64-musl": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.6.4.tgz", - "integrity": "sha512-PU7AaBlrgnJnDxTiV/PNCu0pHUCzaogm6uNcbzCyFJLGn7DoQK9rkqUMPJjb3CnJkAj9XrrhuZwmOdbrhvHAvA==", - "cpu": [ - "x64" - ], + "node_modules/@nx/webpack/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.6.4.tgz", - "integrity": "sha512-6CCmGWwH/J2k+Uxeci48w4QVhtcQ3hRZ5Z2jh26HI8YzH4wqZyA7QPgLBE6sNCPVLoGW5cBgTsfnyEdr+xarQA==", - "cpu": [ - "arm64" - ], + "node_modules/@nx/webpack/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, - "license": "MIT", + "license": "BSD-3-Clause", "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 10" + "node": ">=0.10.0" } }, - "node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.6.4.tgz", - "integrity": "sha512-jTNrlaFaKtbL2mYOcfPAiljtpF5CI7vbHIqYqBFYLUQXOwW9lOHlO+SeQnft6JYZs0FIr1IdHaCfdOw/hpnCiQ==", - "cpu": [ - "x64" - ], + "node_modules/@nx/webpack/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, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/@nx/web": { + "node_modules/@nx/workspace": { "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/web/-/web-19.6.4.tgz", - "integrity": "sha512-RcDfN+bTjCFlXdGC2UkpPguPP6M+g4mMf5dWBvD54Fs56/KKhMxRBjYXRSuLn7OLrdc6QTkU/2FQT/6lzrecgg==", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-19.6.4.tgz", + "integrity": "sha512-SMrI03OmjYm93XpDTlIWgAyIaEhToN4LNu4cxOXW9ygpzPe8HJlqR18aTaCqu1T+BY8XeaZ8BQ7qYS1kEjM9YA==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/web": "19.6.4", + "@nrwl/workspace": "19.6.4", "@nx/devkit": "19.6.4", - "@nx/js": "19.6.4", "chalk": "^4.1.0", - "detect-port": "^1.5.1", - "http-server": "^14.1.0", - "tslib": "^2.3.0" + "enquirer": "~2.3.6", + "nx": "19.6.4", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" } }, - "node_modules/@nx/web/node_modules/ansi-styles": { + "node_modules/@nx/workspace/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==", @@ -7776,7 +9333,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/web/node_modules/chalk": { + "node_modules/@nx/workspace/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7793,7 +9350,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nx/web/node_modules/color-convert": { + "node_modules/@nx/workspace/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==", @@ -7806,14 +9363,14 @@ "node": ">=7.0.0" } }, - "node_modules/@nx/web/node_modules/color-name": { + "node_modules/@nx/workspace/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, "license": "MIT" }, - "node_modules/@nx/web/node_modules/has-flag": { + "node_modules/@nx/workspace/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==", @@ -7823,7 +9380,7 @@ "node": ">=8" } }, - "node_modules/@nx/web/node_modules/supports-color": { + "node_modules/@nx/workspace/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==", @@ -7836,650 +9393,913 @@ "node": ">=8" } }, - "node_modules/@nx/webpack": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-19.6.4.tgz", - "integrity": "sha512-VG1DoAPZgPanyh2x+n3pgqKwhy7qYZpNdugqQ4d4dkJ9VEiM6KOhckwAkUBJ1lb66w8glqkcThP1X9wunwI9jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.2", - "@module-federation/enhanced": "^0.2.3", - "@module-federation/sdk": "^0.2.3", - "@nrwl/webpack": "19.6.4", - "@nx/devkit": "19.6.4", - "@nx/js": "19.6.4", - "@phenomnomnominal/tsquery": "~5.0.1", - "ajv": "^8.12.0", - "autoprefixer": "^10.4.9", - "babel-loader": "^9.1.2", - "browserslist": "^4.21.4", - "chalk": "^4.1.0", - "copy-webpack-plugin": "^10.2.4", - "css-loader": "^6.4.0", - "css-minimizer-webpack-plugin": "^5.0.0", - "express": "^4.19.2", - "fork-ts-checker-webpack-plugin": "7.2.13", - "http-proxy-middleware": "^3.0.0", - "less": "4.1.3", - "less-loader": "11.1.0", - "license-webpack-plugin": "^4.0.2", - "loader-utils": "^2.0.3", - "mini-css-extract-plugin": "~2.4.7", - "parse5": "4.0.0", - "postcss": "^8.4.38", - "postcss-import": "~14.1.0", - "postcss-loader": "^6.1.1", - "rxjs": "^7.8.0", - "sass": "^1.42.1", - "sass-loader": "^12.2.0", - "source-map-loader": "^5.0.0", - "style-loader": "^3.3.0", - "stylus": "^0.59.0", - "stylus-loader": "^7.1.0", - "terser-webpack-plugin": "^5.3.3", - "ts-loader": "^9.3.1", - "tsconfig-paths-webpack-plugin": "4.0.0", - "tslib": "^2.3.0", - "webpack": "^5.80.0", - "webpack-dev-server": "^5.0.4", - "webpack-node-externals": "^3.0.0", - "webpack-subresource-integrity": "^5.1.0" - } - }, - "node_modules/@nx/webpack/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==", + "node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "esquery": "^1.4.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" } }, - "node_modules/@nx/webpack/node_modules/array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "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, "license": "MIT", + "optional": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=14" } }, - "node_modules/@nx/webpack/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", + "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-html": "^0.0.9", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" }, "engines": { - "node": ">=10" + "node": ">= 10.13" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <5.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x || 5.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } } }, - "node_modules/@nx/webpack/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==", + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=8.9.0" } }, - "node_modules/@nx/webpack/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==", + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", "dev": true, "license": "MIT" }, - "node_modules/@nx/webpack/node_modules/copy-webpack-plugin": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", - "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.1", - "globby": "^12.0.2", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 12.20.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { - "webpack": "^5.1.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", "dev": true, "license": "MIT", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "dev": true, "license": "MIT", "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "@rspack/core": { + "@types/react": { "optional": true }, - "webpack": { + "@types/react-dom": { "optional": true } } }, - "node_modules/@nx/webpack/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=10.13.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/globby": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", - "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "dev": true, "license": "MIT", "dependencies": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/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==", + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "copy-anything": "^2.0.1", - "parse-node-version": "^1.0.1", - "tslib": "^2.3.0" - }, - "bin": { - "lessc": "bin/lessc" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "source-map": "~0.6.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "dev": true, "license": "MIT", - "dependencies": { - "klona": "^2.0.4" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "dev": true, "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" }, - "engines": { - "node": ">=8.9.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/@radix-ui/react-icons": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", + "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" } }, - "node_modules/@nx/webpack/node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/mini-css-extract-plugin": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", - "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "dev": true, "license": "MIT", "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { - "webpack": "^5.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@nx/webpack/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "dev": true, "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "dev": true, "license": "MIT", "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">= 12.13.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "dev": true, "license": "MIT", "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" + "@radix-ui/react-slot": "1.2.3" }, - "engines": { - "node": ">= 12.13.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "fibers": { + "@types/react": { "optional": true }, - "node-sass": { + "@types/react-dom": { "optional": true - }, - "sass": { + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { "optional": true }, - "sass-embedded": { + "@types/react-dom": { "optional": true } } }, - "node_modules/@nx/webpack/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/webpack/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==", + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/webpack/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==", + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/workspace": { - "version": "19.6.4", - "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-19.6.4.tgz", - "integrity": "sha512-SMrI03OmjYm93XpDTlIWgAyIaEhToN4LNu4cxOXW9ygpzPe8HJlqR18aTaCqu1T+BY8XeaZ8BQ7qYS1kEjM9YA==", + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", "dev": true, "license": "MIT", "dependencies": { - "@nrwl/workspace": "19.6.4", - "@nx/devkit": "19.6.4", - "chalk": "^4.1.0", - "enquirer": "~2.3.6", - "nx": "19.6.4", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@nx/workspace/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==", + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/workspace/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/workspace/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==", + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/workspace/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==", + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@nx/workspace/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==", + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nx/workspace/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==", + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@phenomnomnominal/tsquery": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", - "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "dev": true, "license": "MIT", "dependencies": { - "esquery": "^1.4.0" + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { - "typescript": "^3 || ^4 || ^5" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "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==", + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "dev": true, "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", - "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "dev": true, "license": "MIT", "dependencies": { - "ansi-html": "^0.0.9", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^4.2.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x || 5.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { + "@types/react": { "optional": true }, - "webpack-plugin-serve": { + "@types/react-dom": { "optional": true } } }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.22.4", @@ -9534,6 +11354,48 @@ "node": ">=10.13.0" } }, + "node_modules/@ts-morph/common": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", + "integrity": "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.3", + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -9602,6 +11464,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -10223,6 +12091,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "license": "MIT" + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -10273,6 +12147,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.180.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.180.0.tgz", + "integrity": "sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.22.0" + } + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -10280,6 +12175,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webxr": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.23.tgz", + "integrity": "sha512-GPe4AsfOSpqWd3xA/0gwoKod13ChcfV67trvxaW2krUbgb9gxQjnCx8zGshzMl8LSHZlNH5gQ8LNScsDuc7nGQ==", + "license": "MIT" + }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", @@ -10704,6 +12605,12 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webgpu/types": { + "version": "0.1.64", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz", + "integrity": "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==", + "license": "BSD-3-Clause" + }, "node_modules/@webpack-cli/configtest": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", @@ -11074,7 +12981,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11144,6 +13050,19 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -11297,6 +13216,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "license": "MIT" + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -11304,6 +13235,12 @@ "dev": true, "license": "MIT" }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -11362,7 +13299,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -11636,43 +13572,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", @@ -11779,6 +13678,18 @@ "@babel/core": "^7.0.0" } }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -11869,6 +13780,72 @@ "readable-stream": "^3.4.0" } }, + "node_modules/blamer": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.6.tgz", + "integrity": "sha512-fv7QToPS87oD1m1bDDTf29zC/bVKJxj2Nqh1r/v4NhMtbnzDIbWOHBYIfxCjlmkVGu3FGOjKgdNG3SFm7TkvBQ==", + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/blamer/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/blamer/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blamer/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/blamer/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==", + "license": "ISC" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -12169,16 +14146,44 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -12210,6 +14215,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -12269,6 +14285,15 @@ "node": ">=10" } }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -12365,6 +14390,25 @@ "dev": true, "license": "MIT" }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "license": "Apache-2.0" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -12404,6 +14448,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/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==", + "license": "MIT" + }, + "node_modules/cli-table3/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -12557,6 +14645,33 @@ "node": ">=6" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12568,6 +14683,12 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -12606,6 +14727,15 @@ "dev": true, "license": "MIT" }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/columnify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", @@ -12739,6 +14869,124 @@ "dev": true, "license": "MIT" }, + "node_modules/concurrently": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz", + "integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/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, + "license": "MIT", + "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/concurrently/node_modules/chalk/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/concurrently/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, + "license": "MIT" + }, + "node_modules/concurrently/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", @@ -12756,6 +15004,16 @@ "node": ">=0.8" } }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -12989,6 +15247,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", @@ -13064,6 +15335,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -13275,10 +15558,9 @@ } }, "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, + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -14165,10 +16447,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -14189,6 +16470,21 @@ "dev": true, "license": "MIT" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -14413,6 +16709,13 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-port": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", @@ -14431,6 +16734,14 @@ "node": ">= 4.0.0" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -14451,6 +16762,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "license": "MIT" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -14464,6 +16781,14 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -14490,6 +16815,12 @@ "node": ">=6.0.0" } }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "license": "MIT" + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -14616,6 +16947,20 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -14630,6 +16975,22 @@ "dev": true, "license": "MIT" }, + "node_modules/echarts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -14713,7 +17074,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -14905,13 +17265,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -14933,10 +17290,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -15760,6 +18116,27 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz", + "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -15879,6 +18256,21 @@ "node": ">= 0.10.0" } }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -15939,7 +18331,6 @@ "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, "license": "MIT" }, "node_modules/fast-glob": { @@ -15962,7 +18353,6 @@ "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, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -16021,6 +18411,12 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -16233,13 +18629,18 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -16672,16 +19073,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -16690,6 +19096,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -16700,6 +19116,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -16749,6 +19178,15 @@ "node": ">=16" } }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -16941,12 +19379,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16956,7 +19394,6 @@ "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, "license": "ISC" }, "node_modules/graphemer": { @@ -17025,6 +19462,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17034,9 +19472,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -17049,7 +19487,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -17592,7 +20029,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -17882,7 +20318,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17895,7 +20330,6 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -17955,6 +20389,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -18155,11 +20611,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -18192,7 +20653,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -18247,13 +20707,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -18325,14 +20784,12 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -18345,6 +20802,41 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-git": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.33.0.tgz", + "integrity": "sha512-a90aVhiBFtkUUe8JaqmR0gL7Thk1Ol/30rLS9c7nM20CwSbVqDctnwxX9VFSDLz5iq1wyzV6p4uyU7GStQKkag==", + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "path-browserify": "^1.0.1", + "pify": "^4.0.1", + "readable-stream": "^3.4.0", + "sha.js": "^2.4.9", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/isomorphic-git/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/isomorphic-ws": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", @@ -20371,6 +22863,12 @@ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", "license": "MIT" }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -20399,6 +22897,74 @@ "dev": true, "license": "MIT" }, + "node_modules/jscpd": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.5.tgz", + "integrity": "sha512-AzJlSLvKtXYkQm93DKE1cRN3rf6pkpv3fm5TVuvECwoqljQlCM/56ujHn9xPcE7wyUnH5+yHr7tcTiveIoMBoQ==", + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.1", + "@jscpd/finder": "4.0.1", + "@jscpd/html-reporter": "4.0.1", + "@jscpd/tokenizer": "4.0.1", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.3" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.3.tgz", + "integrity": "sha512-0T7KiWiDIVArvlBkvCorn2NFwQe7p7DJ37o4YFRuPLDpcr1jNHQlEfbFPw8hDdgJ4hpfby6A5YwyHqASKJ7drA==", + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^2.0.3" + } + }, + "node_modules/jscpd-sarif-reporter/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/jscpd/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jscpd/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -20559,7 +23125,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -20595,6 +23160,16 @@ "node": "*" } }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, "node_modules/karma-source-map-support": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", @@ -20951,9 +23526,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -21456,6 +24031,19 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -21476,6 +24064,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.523.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.523.0.tgz", + "integrity": "sha512-rUjQoy7egZT9XYVXBK1je9ckBnNp7qzRZOhLQx5RcEp2dCGlXo+mv6vf7Am4LimEcFBJIIZzSGfgTqc9QCrPSw==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/luxon": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", @@ -21563,6 +24161,28 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -21618,7 +24238,6 @@ "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, "license": "MIT" }, "node_modules/merge2": { @@ -21630,6 +24249,12 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", + "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", + "license": "MIT" + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -21701,7 +24326,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -21720,6 +24344,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", @@ -21768,12 +24404,20 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -22034,10 +24678,23 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "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, + "license": "MIT", + "peer": 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==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -22290,6 +24947,33 @@ "dev": true, "license": "MIT" }, + "node_modules/node-sarif-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-2.0.3.tgz", + "integrity": "sha512-Pzr3rol8fvhG/oJjIq2NTVB0vmdNNlz22FENhhPojYRZ4/ee08CfK4YuKmuL54V9MLhI1kpzxfOJ/63LzmZzDg==", + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.4", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/node-sarif-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/node-schedule": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", @@ -22461,7 +25145,6 @@ "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, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -22807,10 +25490,30 @@ "node": ">=8" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -22933,7 +25636,6 @@ "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, "license": "ISC", "dependencies": { "wrappy": "1" @@ -22943,7 +25645,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -23313,6 +26014,12 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -23429,6 +26136,12 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", @@ -23449,11 +26162,17 @@ "node": ">=0.10.0" } }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, "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, "license": "MIT", "engines": { "node": ">=8" @@ -23463,7 +26182,6 @@ "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, "license": "MIT" }, "node_modules/path-scurry": { @@ -23507,9 +26225,9 @@ } }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -23556,6 +26274,15 @@ "nice-napi": "^1.0.2" } }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -23674,7 +26401,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -23832,6 +26558,84 @@ "postcss": "^8.0.0" } }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "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" + } + ], + "license": "MIT", + "peer": true, + "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/postcss-load-config/node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/postcss-loader": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", @@ -24038,6 +26842,33 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, "node_modules/postcss-normalize-charset": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", @@ -24338,6 +27169,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/proc-log": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", @@ -24355,6 +27196,15 @@ "dev": true, "license": "MIT" }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -24424,11 +27274,144 @@ "dev": true, "license": "MIT" }, + "node_modules/pug": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", + "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.3", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", + "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "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, "license": "MIT", "engines": { "node": ">=6" @@ -24555,6 +27538,33 @@ "node": ">=0.10.0" } }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -24572,6 +27582,89 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-simple-code-editor": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz", + "integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -24586,7 +27679,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -24746,6 +27838,21 @@ "jsesc": "bin/jsesc" } }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -24777,7 +27884,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -24999,6 +28105,37 @@ "fsevents": "~2.3.2" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -25193,6 +28330,16 @@ "node": ">=v12.22.7" } }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -25330,6 +28477,106 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-handler/node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -25470,6 +28717,26 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -25487,7 +28754,6 @@ "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, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -25500,32 +28766,88 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -25565,6 +28887,51 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -25745,6 +29112,23 @@ "node": ">=0.10.0" } }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/spawn-rx": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-5.1.2.tgz", + "integrity": "sha512-/y7tJKALVZ1lPzeZZB9jYnmtrL7d0N2zkorii5a7r7dhHkWIuLTzZpZzMJLK1dmYRgX/NCc4iarTO3F7BS2c/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7", + "rxjs": "^7.8.1" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -25936,7 +29320,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -26092,7 +29475,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -26129,7 +29511,6 @@ "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, "license": "MIT", "engines": { "node": ">=6" @@ -26214,44 +29595,126 @@ "source-map": "^0.7.3" }, "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus-loader": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", + "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.12", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "license": "ISC" + }, + "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, + "license": "MIT", + "peer": 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/sucrase/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, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://opencollective.com/stylus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stylus-loader": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", - "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", + "node_modules/sucrase/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, "license": "MIT", + "peer": true + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "peer": true, "dependencies": { - "fast-glob": "^3.2.12", - "normalize-path": "^3.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 14.15.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylus": ">=0.52.4", - "webpack": "^5.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stylus/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "license": "ISC" - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -26269,7 +29732,6 @@ "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, "license": "MIT", "engines": { "node": ">= 0.4" @@ -26328,6 +29790,137 @@ "dev": true, "license": "MIT" }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "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" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/tailwindcss/node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -26643,6 +30236,31 @@ "dev": true, "license": "MIT" }, + "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, + "license": "MIT", + "peer": 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, + "license": "MIT", + "peer": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -26656,6 +30274,12 @@ "tslib": "^2" } }, + "node_modules/three": { + "version": "0.180.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", + "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", + "license": "MIT" + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -26694,11 +30318,24 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "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==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -26725,6 +30362,12 @@ "node": ">=0.6" } }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "license": "MIT" + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -26804,6 +30447,14 @@ "typescript": ">=4.2.0" } }, + "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, + "license": "Apache-2.0", + "peer": true + }, "node_modules/ts-jest": { "version": "29.2.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", @@ -26950,6 +30601,16 @@ "node": ">=8" } }, + "node_modules/ts-morph": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz", + "integrity": "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.27.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -27191,15 +30852,14 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -27409,7 +31069,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -27470,7 +31129,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -27494,11 +31152,55 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/utils-merge": { @@ -28098,6 +31800,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -28769,7 +32480,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -28799,16 +32509,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -28825,6 +32536,21 @@ "dev": true, "license": "MIT" }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -29009,7 +32735,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -29055,6 +32780,22 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", @@ -29206,11 +32947,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zone.js": { "version": "0.14.10", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", "license": "MIT" + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" } } } diff --git a/package.json b/package.json index 2329ccb..1ee6350 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ "@angular/platform-browser": "~18.2.0", "@angular/platform-browser-dynamic": "~18.2.0", "@angular/router": "~18.2.0", + "@jscpd/core": "^4.0.1", + "@modelcontextprotocol/sdk": "^1.17.3", "@ngrx/operators": "^18.0.2", "@ngrx/signals": "^18.0.2", "@softarc/sheriff-core": "^0.17.1", + "@types/three": "0.180.0", "axios": "^1.6.0", "chart.js": "^4.4.4", "chartjs-chart-treemap": "^3.1.0", @@ -32,12 +35,19 @@ "cytoscape-qtip": "^2.8.0", "cytoscape-spread": "^3.0.0", "d3": "^7.9.0", + "echarts": "^6.0.0", "express": "^4.18.1", "fast-glob": "^3.3.2", + "isomorphic-git": "^1.33.0", + "jscpd": "^4.0.5", "micromatch": "^4.0.8", "qtip2": "^3.0.3", "rxjs": "~7.8.0", + "three": "0.180.0", + "ts-morph": "^26.0.0", "tslib": "^2.3.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.5", "zone.js": "~0.14.3" }, "devDependencies": { @@ -52,6 +62,7 @@ "@angular/language-service": "~18.2.0", "@commitlint/cli": "^19.4.1", "@commitlint/config-conventional": "^19.4.1", + "@modelcontextprotocol/inspector": "^0.16.5", "@nx/angular": "19.6.4", "@nx/eslint": "19.6.4", "@nx/eslint-plugin": "19.6.4", @@ -98,6 +109,8 @@ "lint": "nx run-many --target=lint --all --fix --max-warnings=0", "test": "nx run-many --target=test --all", "build": "nx run-many --target=build --all", - "release": "nx release --skip-publish" + "release": "nx release --skip-publish", + "mcp:build": "nx build backend", + "mcp:inspect": "npx @modelcontextprotocol/inspector http http://localhost:3334/mcp" } }