From 8067be3c351291936d2d3583ec3ad719b89fe1a7 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Thu, 2 Jul 2026 12:40:23 +0300 Subject: [PATCH] Implement rate limiting and enhance authentication flow - Added rate limiting configuration for authentication endpoints, allowing customizable request limits via environment variables. - Updated authentication flow to utilize HttpRequest for cookie management, ensuring secure handling of refresh tokens. - Introduced a new endpoint to retrieve user subscription details. - Enhanced the handling of Telegram bot token validation to prevent errors with empty tokens. - Updated the application to serialize enums as strings for better documentation and compatibility with TypeScript. - Improved test coverage for new features and adjustments in command handlers. --- .claude/launch.json | 11 + .env.example | 4 + PnvPanel.sln | 77 + .../PnvPanel.Api/Endpoints/AuthEndpoints.cs | 24 +- .../PnvPanel.Api/Endpoints/ConfigEndpoints.cs | 12 + backend/src/PnvPanel.Api/Program.cs | 20 +- .../ApproveActivationCommandHandler.cs | 4 + .../RejectActivationCommandHandler.cs | 5 + .../Inbounds/PublishInboundCommandHandler.cs | 7 +- .../Admin/Nodes/DeleteNodeCommandHandler.cs | 6 +- .../Admin/Nodes/RegisterNodeCommandHandler.cs | 6 +- .../Admin/Nodes/UpdateNodeCommandHandler.cs | 7 +- .../Users/ChangeUserRoleCommandHandler.cs | 18 +- .../Common/Interfaces/IIdentityService.cs | 3 +- .../GetMySubscriptionQuery.cs | 9 + .../GetMySubscriptionQueryHandler.cs | 22 + .../DependencyInjection.cs | 10 +- .../Identity/IdentityService.cs | 3 +- .../PublishInboundCommandHandlerTests.cs | 10 +- .../Nodes/RegisterNodeCommandHandlerTests.cs | 7 +- .../ChangeUserRoleCommandHandlerTests.cs | 12 +- .../Auth/GetCurrentUserQueryHandlerTests.cs | 2 +- .../Auth/LoginCommandHandlerTests.cs | 2 +- .../Auth/RefreshCommandHandlerTests.cs | 2 +- .../GetMyConfigsQueryHandlerTests.cs | 2 +- .../GetLoginRequestStatusQueryHandlerTests.cs | 2 +- .../Admin/NodeInboundCrudTests.cs | 6 +- .../Auth/AuthFlowTests.cs | 2 +- .../Configs/ConfigQuotaTests.cs | 10 +- .../PnvPanelWebApplicationFactory.cs | 8 +- frontend/package.json | 29 +- frontend/pnpm-lock.yaml | 2262 ++++++++++++++++- frontend/src/App.tsx | 68 - .../features/activation/ActivationGate.tsx | 83 + frontend/src/features/activation/api.ts | 10 + frontend/src/features/admin/activation/api.ts | 16 + .../src/features/admin/apps/AppFormDialog.tsx | 113 + frontend/src/features/admin/apps/api.ts | 40 + frontend/src/features/admin/audit/api.ts | 6 + .../admin/inbounds/PublishInboundDialog.tsx | 97 + frontend/src/features/admin/inbounds/api.ts | 19 + .../features/admin/nodes/EditNodeDialog.tsx | 76 + .../src/features/admin/nodes/NodeCard.tsx | 134 + .../admin/nodes/RegisterNodeDialog.tsx | 88 + frontend/src/features/admin/nodes/api.ts | 36 + .../features/admin/roles/RoleFormDialog.tsx | 80 + frontend/src/features/admin/roles/api.ts | 18 + frontend/src/features/admin/stats/api.ts | 6 + .../features/admin/users/UserManageDialog.tsx | 154 ++ frontend/src/features/admin/users/api.ts | 32 + frontend/src/features/apps/AppsCatalog.tsx | 47 + frontend/src/features/apps/api.ts | 6 + frontend/src/features/auth/LoginForm.tsx | 55 + frontend/src/features/auth/RegisterForm.tsx | 69 + frontend/src/features/auth/api.ts | 50 + frontend/src/features/auth/guards.ts | 39 + frontend/src/features/auth/store.ts | 17 + frontend/src/features/configs/ConfigCard.tsx | 146 ++ .../features/configs/CreateConfigDialog.tsx | 100 + .../src/features/configs/SubscriptionCard.tsx | 44 + frontend/src/features/configs/api.ts | 46 + .../features/settings/ChangePasswordForm.tsx | 65 + .../settings/DeleteAccountSection.tsx | 49 + .../features/settings/TelegramLinkCard.tsx | 104 + .../features/telegram/TelegramLoginButton.tsx | 87 + frontend/src/features/telegram/api.ts | 18 + frontend/src/lib/i18n.ts | 46 - frontend/src/main.tsx | 15 +- frontend/src/routeTree.gen.ts | 342 +++ frontend/src/router.tsx | 10 + frontend/src/routes/__root.tsx | 97 + frontend/src/routes/admin.tsx | 43 + frontend/src/routes/admin/activation.tsx | 93 + frontend/src/routes/admin/apps.tsx | 89 + frontend/src/routes/admin/audit.tsx | 79 + frontend/src/routes/admin/index.tsx | 51 + frontend/src/routes/admin/nodes.tsx | 37 + frontend/src/routes/admin/roles.tsx | 92 + frontend/src/routes/admin/users.tsx | 104 + frontend/src/routes/dashboard.tsx | 58 + frontend/src/routes/index.tsx | 18 + frontend/src/routes/instructions.tsx | 33 + frontend/src/routes/login.tsx | 39 + frontend/src/routes/register.tsx | 32 + frontend/src/routes/settings.tsx | 24 + frontend/src/shared/api/client.ts | 93 + frontend/src/shared/api/schema.gen.ts | 1771 +++++++++++++ frontend/src/shared/api/types.ts | 212 ++ frontend/src/shared/lib/cn.ts | 6 + frontend/src/shared/lib/format.ts | 8 + frontend/src/shared/lib/i18n.ts | 543 ++++ .../src/shared/realtime/RealtimeProvider.tsx | 66 + frontend/src/shared/realtime/connection.ts | 33 + frontend/src/shared/ui/badge.tsx | 22 + frontend/src/shared/ui/button.tsx | 37 + frontend/src/shared/ui/card.tsx | 34 + frontend/src/shared/ui/dialog.tsx | 33 + frontend/src/shared/ui/input.tsx | 17 + frontend/src/shared/ui/label.tsx | 15 + frontend/src/shared/ui/progress.tsx | 10 + frontend/src/shared/ui/select.tsx | 70 + frontend/src/shared/ui/toast-store.tsx | 43 + frontend/src/shared/ui/toaster.tsx | 46 + .../theme.tsx => theme/ThemeProvider.tsx} | 0 frontend/tsconfig.app.json | 1 + frontend/vite.config.ts | 11 +- 106 files changed, 8823 insertions(+), 172 deletions(-) create mode 100644 .claude/launch.json create mode 100644 PnvPanel.sln create mode 100644 backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQuery.cs create mode 100644 backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQueryHandler.cs delete mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/features/activation/ActivationGate.tsx create mode 100644 frontend/src/features/activation/api.ts create mode 100644 frontend/src/features/admin/activation/api.ts create mode 100644 frontend/src/features/admin/apps/AppFormDialog.tsx create mode 100644 frontend/src/features/admin/apps/api.ts create mode 100644 frontend/src/features/admin/audit/api.ts create mode 100644 frontend/src/features/admin/inbounds/PublishInboundDialog.tsx create mode 100644 frontend/src/features/admin/inbounds/api.ts create mode 100644 frontend/src/features/admin/nodes/EditNodeDialog.tsx create mode 100644 frontend/src/features/admin/nodes/NodeCard.tsx create mode 100644 frontend/src/features/admin/nodes/RegisterNodeDialog.tsx create mode 100644 frontend/src/features/admin/nodes/api.ts create mode 100644 frontend/src/features/admin/roles/RoleFormDialog.tsx create mode 100644 frontend/src/features/admin/roles/api.ts create mode 100644 frontend/src/features/admin/stats/api.ts create mode 100644 frontend/src/features/admin/users/UserManageDialog.tsx create mode 100644 frontend/src/features/admin/users/api.ts create mode 100644 frontend/src/features/apps/AppsCatalog.tsx create mode 100644 frontend/src/features/apps/api.ts create mode 100644 frontend/src/features/auth/LoginForm.tsx create mode 100644 frontend/src/features/auth/RegisterForm.tsx create mode 100644 frontend/src/features/auth/api.ts create mode 100644 frontend/src/features/auth/guards.ts create mode 100644 frontend/src/features/auth/store.ts create mode 100644 frontend/src/features/configs/ConfigCard.tsx create mode 100644 frontend/src/features/configs/CreateConfigDialog.tsx create mode 100644 frontend/src/features/configs/SubscriptionCard.tsx create mode 100644 frontend/src/features/configs/api.ts create mode 100644 frontend/src/features/settings/ChangePasswordForm.tsx create mode 100644 frontend/src/features/settings/DeleteAccountSection.tsx create mode 100644 frontend/src/features/settings/TelegramLinkCard.tsx create mode 100644 frontend/src/features/telegram/TelegramLoginButton.tsx create mode 100644 frontend/src/features/telegram/api.ts delete mode 100644 frontend/src/lib/i18n.ts create mode 100644 frontend/src/routeTree.gen.ts create mode 100644 frontend/src/router.tsx create mode 100644 frontend/src/routes/__root.tsx create mode 100644 frontend/src/routes/admin.tsx create mode 100644 frontend/src/routes/admin/activation.tsx create mode 100644 frontend/src/routes/admin/apps.tsx create mode 100644 frontend/src/routes/admin/audit.tsx create mode 100644 frontend/src/routes/admin/index.tsx create mode 100644 frontend/src/routes/admin/nodes.tsx create mode 100644 frontend/src/routes/admin/roles.tsx create mode 100644 frontend/src/routes/admin/users.tsx create mode 100644 frontend/src/routes/dashboard.tsx create mode 100644 frontend/src/routes/index.tsx create mode 100644 frontend/src/routes/instructions.tsx create mode 100644 frontend/src/routes/login.tsx create mode 100644 frontend/src/routes/register.tsx create mode 100644 frontend/src/routes/settings.tsx create mode 100644 frontend/src/shared/api/client.ts create mode 100644 frontend/src/shared/api/schema.gen.ts create mode 100644 frontend/src/shared/api/types.ts create mode 100644 frontend/src/shared/lib/cn.ts create mode 100644 frontend/src/shared/lib/format.ts create mode 100644 frontend/src/shared/lib/i18n.ts create mode 100644 frontend/src/shared/realtime/RealtimeProvider.tsx create mode 100644 frontend/src/shared/realtime/connection.ts create mode 100644 frontend/src/shared/ui/badge.tsx create mode 100644 frontend/src/shared/ui/button.tsx create mode 100644 frontend/src/shared/ui/card.tsx create mode 100644 frontend/src/shared/ui/dialog.tsx create mode 100644 frontend/src/shared/ui/input.tsx create mode 100644 frontend/src/shared/ui/label.tsx create mode 100644 frontend/src/shared/ui/progress.tsx create mode 100644 frontend/src/shared/ui/select.tsx create mode 100644 frontend/src/shared/ui/toast-store.tsx create mode 100644 frontend/src/shared/ui/toaster.tsx rename frontend/src/{lib/theme.tsx => theme/ThemeProvider.tsx} (100%) diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..7f6c3c0 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "frontend", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["--dir", "frontend", "dev"], + "port": 5173 + } + ] +} diff --git a/.env.example b/.env.example index a8e64b8..4cbf622 100644 --- a/.env.example +++ b/.env.example @@ -33,6 +33,10 @@ AdminSeed__TelegramUserIds=123456789 # Квота конфигов для системной роли "user" (выдаётся при регистрации). Roles__DefaultUserMaxConfigs=3 +# ── Rate limiting ──────────────────────────────────────────────────────── +# Лимит запросов/мин на auth-эндпоинты (login/register/refresh/telegram/subscription). По умолчанию 20. +# RateLimiting__AuthPermitLimit=20 + # ── Telegram-бот ────────────────────────────────────────────────────────── # Если BotToken пуст — бот не стартует, панель работает без него. Telegram__BotToken= diff --git a/PnvPanel.sln b/PnvPanel.sln new file mode 100644 index 0000000..b7c6846 --- /dev/null +++ b/PnvPanel.sln @@ -0,0 +1,77 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend", "backend", "{1AE8ACA6-933B-BF2A-3671-3E2EAC007D16}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F9113EE-888A-26D2-68B0-4A7D0A2A8745}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Api", "backend\src\PnvPanel.Api\PnvPanel.Api.csproj", "{3B6A930E-4799-6F42-1E94-163F6773FBBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Application", "backend\src\PnvPanel.Application\PnvPanel.Application.csproj", "{25F9AF36-7508-0DC8-2469-D065D078DBF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Domain", "backend\src\PnvPanel.Domain\PnvPanel.Domain.csproj", "{7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Infrastructure", "backend\src\PnvPanel.Infrastructure\PnvPanel.Infrastructure.csproj", "{470155DC-9172-CAAF-7AA4-D642ECCFD2D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F57642F3-C37C-D174-720E-6A6AAD5BEE22}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Application.Tests", "backend\tests\PnvPanel.Application.Tests\PnvPanel.Application.Tests.csproj", "{4A683703-6702-96CD-5AB6-56199C1C1C7E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.Domain.Tests", "backend\tests\PnvPanel.Domain.Tests\PnvPanel.Domain.Tests.csproj", "{F3793E3C-AC9E-D8DA-704A-E67DC4FA790F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PnvPanel.IntegrationTests", "backend\tests\PnvPanel.IntegrationTests\PnvPanel.IntegrationTests.csproj", "{8A44D601-6F27-4D86-63F7-25C42FF67414}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B6A930E-4799-6F42-1E94-163F6773FBBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B6A930E-4799-6F42-1E94-163F6773FBBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B6A930E-4799-6F42-1E94-163F6773FBBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B6A930E-4799-6F42-1E94-163F6773FBBC}.Release|Any CPU.Build.0 = Release|Any CPU + {25F9AF36-7508-0DC8-2469-D065D078DBF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25F9AF36-7508-0DC8-2469-D065D078DBF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25F9AF36-7508-0DC8-2469-D065D078DBF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25F9AF36-7508-0DC8-2469-D065D078DBF3}.Release|Any CPU.Build.0 = Release|Any CPU + {7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3}.Release|Any CPU.Build.0 = Release|Any CPU + {470155DC-9172-CAAF-7AA4-D642ECCFD2D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {470155DC-9172-CAAF-7AA4-D642ECCFD2D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {470155DC-9172-CAAF-7AA4-D642ECCFD2D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {470155DC-9172-CAAF-7AA4-D642ECCFD2D2}.Release|Any CPU.Build.0 = Release|Any CPU + {4A683703-6702-96CD-5AB6-56199C1C1C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A683703-6702-96CD-5AB6-56199C1C1C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A683703-6702-96CD-5AB6-56199C1C1C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A683703-6702-96CD-5AB6-56199C1C1C7E}.Release|Any CPU.Build.0 = Release|Any CPU + {F3793E3C-AC9E-D8DA-704A-E67DC4FA790F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3793E3C-AC9E-D8DA-704A-E67DC4FA790F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3793E3C-AC9E-D8DA-704A-E67DC4FA790F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3793E3C-AC9E-D8DA-704A-E67DC4FA790F}.Release|Any CPU.Build.0 = Release|Any CPU + {8A44D601-6F27-4D86-63F7-25C42FF67414}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A44D601-6F27-4D86-63F7-25C42FF67414}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A44D601-6F27-4D86-63F7-25C42FF67414}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A44D601-6F27-4D86-63F7-25C42FF67414}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0F9113EE-888A-26D2-68B0-4A7D0A2A8745} = {1AE8ACA6-933B-BF2A-3671-3E2EAC007D16} + {3B6A930E-4799-6F42-1E94-163F6773FBBC} = {0F9113EE-888A-26D2-68B0-4A7D0A2A8745} + {25F9AF36-7508-0DC8-2469-D065D078DBF3} = {0F9113EE-888A-26D2-68B0-4A7D0A2A8745} + {7511C433-2BCA-BF01-BEF4-DBC6A5DB8CF3} = {0F9113EE-888A-26D2-68B0-4A7D0A2A8745} + {470155DC-9172-CAAF-7AA4-D642ECCFD2D2} = {0F9113EE-888A-26D2-68B0-4A7D0A2A8745} + {F57642F3-C37C-D174-720E-6A6AAD5BEE22} = {1AE8ACA6-933B-BF2A-3671-3E2EAC007D16} + {4A683703-6702-96CD-5AB6-56199C1C1C7E} = {F57642F3-C37C-D174-720E-6A6AAD5BEE22} + {F3793E3C-AC9E-D8DA-704A-E67DC4FA790F} = {F57642F3-C37C-D174-720E-6A6AAD5BEE22} + {8A44D601-6F27-4D86-63F7-25C42FF67414} = {F57642F3-C37C-D174-720E-6A6AAD5BEE22} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B0DAEC4D-A6CA-40D5-94C1-F0ED1F9EBB44} + EndGlobalSection +EndGlobal diff --git a/backend/src/PnvPanel.Api/Endpoints/AuthEndpoints.cs b/backend/src/PnvPanel.Api/Endpoints/AuthEndpoints.cs index cf661d4..4127db6 100644 --- a/backend/src/PnvPanel.Api/Endpoints/AuthEndpoints.cs +++ b/backend/src/PnvPanel.Api/Endpoints/AuthEndpoints.cs @@ -38,13 +38,13 @@ public static class AuthEndpoints return result.ToHttpResult(); } - private static async Task Login(LoginCommand command, ISender sender, HttpResponse response, CancellationToken cancellationToken) + private static async Task Login(LoginCommand command, ISender sender, HttpRequest request, HttpResponse response, CancellationToken cancellationToken) { var result = await sender.Send(command, cancellationToken); if (!result.IsSuccess) return result.ToHttpResult(); - SetRefreshCookie(response, result.Value.RefreshToken, result.Value.RefreshTokenExpiresAt); + SetRefreshCookie(request, response, result.Value.RefreshToken, result.Value.RefreshTokenExpiresAt); return Results.Ok(ToLoginResponse(result.Value)); } @@ -56,11 +56,11 @@ public static class AuthEndpoints var result = await sender.Send(new RefreshCommand(rawToken), cancellationToken); if (!result.IsSuccess) { - response.Cookies.Delete(RefreshCookieName, BuildCookieOptions()); + response.Cookies.Delete(RefreshCookieName, BuildCookieOptions(request)); return result.ToHttpResult(); } - SetRefreshCookie(response, result.Value.RefreshToken, result.Value.RefreshTokenExpiresAt); + SetRefreshCookie(request, response, result.Value.RefreshToken, result.Value.RefreshTokenExpiresAt); return Results.Ok(ToLoginResponse(result.Value)); } @@ -69,7 +69,7 @@ public static class AuthEndpoints if (request.Cookies.TryGetValue(RefreshCookieName, out var rawToken) && !string.IsNullOrEmpty(rawToken)) await sender.Send(new LogoutCommand(rawToken), cancellationToken); - response.Cookies.Delete(RefreshCookieName, BuildCookieOptions()); + response.Cookies.Delete(RefreshCookieName, BuildCookieOptions(request)); return Results.NoContent(); } @@ -85,10 +85,10 @@ public static class AuthEndpoints return result.ToHttpResult(); } - private static async Task DeleteMe(HttpResponse response, ISender sender, CancellationToken cancellationToken) + private static async Task DeleteMe(HttpRequest request, HttpResponse response, ISender sender, CancellationToken cancellationToken) { var result = await sender.Send(new DeleteMyAccountCommand(), cancellationToken); - response.Cookies.Delete(RefreshCookieName, BuildCookieOptions()); + response.Cookies.Delete(RefreshCookieName, BuildCookieOptions(request)); return result.ToHttpResult(); } @@ -99,17 +99,19 @@ public static class AuthEndpoints user = auth.User, }; - private static void SetRefreshCookie(HttpResponse response, string rawToken, DateTimeOffset expiresAt) + private static void SetRefreshCookie(HttpRequest request, HttpResponse response, string rawToken, DateTimeOffset expiresAt) { - var options = BuildCookieOptions(); + var options = BuildCookieOptions(request); options.Expires = expiresAt; response.Cookies.Append(RefreshCookieName, rawToken, options); } - private static CookieOptions BuildCookieOptions() => new() + // Secure = IsHttps запроса (учитывает ForwardedHeaders за внешним TLS-прокси, см. CLAUDE.md) — + // иначе браузер/HttpClient не пришлёт cookie обратно на plain-http (локальный dev, TestServer). + private static CookieOptions BuildCookieOptions(HttpRequest request) => new() { HttpOnly = true, - Secure = true, + Secure = request.IsHttps, SameSite = SameSiteMode.Strict, Path = "/api/auth", }; diff --git a/backend/src/PnvPanel.Api/Endpoints/ConfigEndpoints.cs b/backend/src/PnvPanel.Api/Endpoints/ConfigEndpoints.cs index 9b32f7d..b259d3e 100644 --- a/backend/src/PnvPanel.Api/Endpoints/ConfigEndpoints.cs +++ b/backend/src/PnvPanel.Api/Endpoints/ConfigEndpoints.cs @@ -4,6 +4,7 @@ using PnvPanel.Application.Configs.Create; using PnvPanel.Application.Configs.Edit; using PnvPanel.Application.Configs.GetConfigLink; using PnvPanel.Application.Configs.GetMyConfigs; +using PnvPanel.Application.Configs.GetMySubscription; using PnvPanel.Application.Configs.ListAvailableInbounds; using PnvPanel.Application.Configs.Revoke; using PnvPanel.Application.Configs.Rotate; @@ -23,6 +24,7 @@ public static class ConfigEndpoints group.MapPost("/configs/{id:guid}/rotate", RotateConfig); group.MapDelete("/configs/{id:guid}", RevokeConfig); group.MapGet("/configs/{id:guid}/link", GetConfigLink); + group.MapGet("/subscription", GetMySubscription); return app; } @@ -74,6 +76,16 @@ public static class ConfigEndpoints var subscriptionUrl = $"{request.Scheme}://{request.Host}/sub/{result.Value.SubscriptionToken}"; return Results.Ok(new { connectionString = result.Value.ConnectionString, subscriptionUrl }); } + + private static async Task GetMySubscription(HttpRequest request, ISender sender, CancellationToken cancellationToken) + { + var result = await sender.Send(new GetMySubscriptionQuery(), cancellationToken); + if (!result.IsSuccess) + return result.ToHttpResult(); + + var subscriptionUrl = $"{request.Scheme}://{request.Host}/sub/{result.Value.SubscriptionToken}"; + return Results.Ok(new { subscriptionUrl }); + } } public sealed record CreateConfigBody(Guid InboundId, string? Label, int? DeviceLimit); diff --git a/backend/src/PnvPanel.Api/Program.cs b/backend/src/PnvPanel.Api/Program.cs index f89a532..b94769b 100644 --- a/backend/src/PnvPanel.Api/Program.cs +++ b/backend/src/PnvPanel.Api/Program.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.RateLimiting; using Microsoft.Extensions.Options; @@ -40,11 +41,17 @@ builder.Services.AddSignalR(); builder.Services.AddSingleton(); // Telegram-бот: presentation-адаптер, хостится в процессе Api (long polling). Клиент регистрируем -// всегда (даже с пустым токеном) — TelegramBotHostedService сам решает не стартовать без токена. +// всегда (даже с пустым токеном) — TelegramBotHostedService сам решает не стартовать без токена, +// а TelegramNotifier — не слать сообщения. TelegramBotClient(...) при этом валидирует формат токена +// и падает на пустой строке, поэтому при пустом BotToken подставляем синтаксически валидную заглушку — +// реальный HTTP-вызов через неё никогда не происходит (все вызывающие места сами проверяют BotToken). builder.Services.AddSingleton(sp => { var options = sp.GetRequiredService>(); - return new TelegramBotClient(options.Value.BotToken ?? string.Empty); + var token = options.Value.BotToken; + return new TelegramBotClient(string.IsNullOrWhiteSpace(token) + ? "0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + : token); }); // Scoped — зависит от IIdentityService (scoped), не Singleton. builder.Services.AddScoped(); @@ -55,13 +62,20 @@ builder.Services.AddRateLimiter(options => { options.AddFixedWindowLimiter(RateLimiting.AuthPolicy, limiterOptions => { - limiterOptions.PermitLimit = 20; + // Настраиваемо через конфиг, чтобы интеграционные тесты (общий TestServer/host на весь + // collection, все запросы — от одного "клиента") могли поднять лимит и не ловить 429. + limiterOptions.PermitLimit = builder.Configuration.GetValue("RateLimiting:AuthPermitLimit", 20); limiterOptions.Window = TimeSpan.FromMinutes(1); limiterOptions.QueueLimit = 0; }); options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; }); +// Энумы сериализуются строками ("Vless", "Active", ...), не числами — самодокументируемый JSON, +// корректные строковые литералы при генерации TS-типов из OpenAPI-схемы (см. docs/frontend.md). +builder.Services.ConfigureHttpJsonOptions(options => + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())); + builder.Services.AddProblemDetails(); builder.Services.AddOpenApi(); builder.Services.AddHealthChecks() diff --git a/backend/src/PnvPanel.Application/Admin/Activation/ApproveActivationCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Activation/ApproveActivationCommandHandler.cs index 2e8d620..074d479 100644 --- a/backend/src/PnvPanel.Application/Admin/Activation/ApproveActivationCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Activation/ApproveActivationCommandHandler.cs @@ -5,6 +5,7 @@ using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; using PnvPanel.Domain.Activation; +using PnvPanel.Domain.Audit; namespace PnvPanel.Application.Admin.Activation; @@ -33,6 +34,9 @@ public sealed class ApproveActivationCommandHandler( if (!activateResult.IsSuccess) return activateResult; + dbContext.AuditLogs.Add(AuditLog.Create( + adminId, "ActivationApproved", "User", request.UserId.ToString(), metadata: null, AuditSource.Web)); + await notifier.NotifyUserActivatedAsync(request.UserId, cancellationToken); await telegramNotifier.NotifyUserAsync(request.UserId, "✅ Ваш аккаунт активирован администратором.", cancellationToken); return Result.Success(); diff --git a/backend/src/PnvPanel.Application/Admin/Activation/RejectActivationCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Activation/RejectActivationCommandHandler.cs index 087686a..d5e643f 100644 --- a/backend/src/PnvPanel.Application/Admin/Activation/RejectActivationCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Activation/RejectActivationCommandHandler.cs @@ -5,6 +5,7 @@ using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; using PnvPanel.Domain.Activation; +using PnvPanel.Domain.Audit; namespace PnvPanel.Application.Admin.Activation; @@ -26,6 +27,10 @@ public sealed class RejectActivationCommandHandler(IAppDbContext dbContext, ICur return Result.Failure(ActivationErrors.AlreadyDecided); request.Reject(adminId, command.Reason); + + dbContext.AuditLogs.Add(AuditLog.Create( + adminId, "ActivationRejected", "User", request.UserId.ToString(), metadata: null, AuditSource.Web)); + return Result.Success(); } } diff --git a/backend/src/PnvPanel.Application/Admin/Inbounds/PublishInboundCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Inbounds/PublishInboundCommandHandler.cs index 11957a5..77e90dd 100644 --- a/backend/src/PnvPanel.Application/Admin/Inbounds/PublishInboundCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Inbounds/PublishInboundCommandHandler.cs @@ -2,10 +2,11 @@ using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; +using PnvPanel.Domain.Audit; namespace PnvPanel.Application.Admin.Inbounds; -public sealed class PublishInboundCommandHandler(IAppDbContext dbContext) +public sealed class PublishInboundCommandHandler(IAppDbContext dbContext, ICurrentUser currentUser) : ICommandHandler> { public async Task> Handle(PublishInboundCommand command, CancellationToken cancellationToken) @@ -19,6 +20,10 @@ public sealed class PublishInboundCommandHandler(IAppDbContext dbContext) else inbound.Unpublish(); + dbContext.AuditLogs.Add(AuditLog.Create( + currentUser.UserId, command.IsPublished ? "InboundPublished" : "InboundUnpublished", + "Inbound", inbound.Id.ToString(), metadata: null, AuditSource.Web)); + return Result.Success(InboundDto.FromDomain(inbound)); } } diff --git a/backend/src/PnvPanel.Application/Admin/Nodes/DeleteNodeCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Nodes/DeleteNodeCommandHandler.cs index 690f950..7a3a8a8 100644 --- a/backend/src/PnvPanel.Application/Admin/Nodes/DeleteNodeCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Nodes/DeleteNodeCommandHandler.cs @@ -2,10 +2,11 @@ using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; +using PnvPanel.Domain.Audit; namespace PnvPanel.Application.Admin.Nodes; -public sealed class DeleteNodeCommandHandler(IAppDbContext dbContext, IXuiPanelGateway gateway) +public sealed class DeleteNodeCommandHandler(IAppDbContext dbContext, IXuiPanelGateway gateway, ICurrentUser currentUser) : ICommandHandler { public async Task Handle(DeleteNodeCommand command, CancellationToken cancellationToken) @@ -19,6 +20,9 @@ public sealed class DeleteNodeCommandHandler(IAppDbContext dbContext, IXuiPanelG dbContext.Nodes.Remove(node); gateway.InvalidateClient(node.Id); + dbContext.AuditLogs.Add(AuditLog.Create( + currentUser.UserId, "NodeDeleted", "Node", node.Id.ToString(), metadata: null, AuditSource.Web)); + return Result.Success(); } } diff --git a/backend/src/PnvPanel.Application/Admin/Nodes/RegisterNodeCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Nodes/RegisterNodeCommandHandler.cs index 4dfe409..20fff70 100644 --- a/backend/src/PnvPanel.Application/Admin/Nodes/RegisterNodeCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Nodes/RegisterNodeCommandHandler.cs @@ -1,11 +1,13 @@ using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; +using PnvPanel.Domain.Audit; using PnvPanel.Domain.Nodes; namespace PnvPanel.Application.Admin.Nodes; -public sealed class RegisterNodeCommandHandler(IAppDbContext dbContext, IXuiPanelGateway gateway, ISecretProtector secretProtector) +public sealed class RegisterNodeCommandHandler( + IAppDbContext dbContext, IXuiPanelGateway gateway, ISecretProtector secretProtector, ICurrentUser currentUser) : ICommandHandler> { public Task> Handle(RegisterNodeCommand command, CancellationToken cancellationToken) @@ -21,6 +23,8 @@ public sealed class RegisterNodeCommandHandler(IAppDbContext dbContext, IXuiPane var node = Node.Register(command.Name, baseAddress, credentials, command.Location); dbContext.Nodes.Add(node); + dbContext.AuditLogs.Add(AuditLog.Create( + currentUser.UserId, "NodeRegistered", "Node", node.Id.ToString(), metadata: null, AuditSource.Web)); return Task.FromResult(Result.Success(NodeDto.FromDomain(node))); } diff --git a/backend/src/PnvPanel.Application/Admin/Nodes/UpdateNodeCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Nodes/UpdateNodeCommandHandler.cs index a78934e..638de8e 100644 --- a/backend/src/PnvPanel.Application/Admin/Nodes/UpdateNodeCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Nodes/UpdateNodeCommandHandler.cs @@ -2,11 +2,13 @@ using Microsoft.EntityFrameworkCore; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; +using PnvPanel.Domain.Audit; using PnvPanel.Domain.Nodes; namespace PnvPanel.Application.Admin.Nodes; -public sealed class UpdateNodeCommandHandler(IAppDbContext dbContext, IXuiPanelGateway gateway, ISecretProtector secretProtector) +public sealed class UpdateNodeCommandHandler( + IAppDbContext dbContext, IXuiPanelGateway gateway, ISecretProtector secretProtector, ICurrentUser currentUser) : ICommandHandler> { public async Task> Handle(UpdateNodeCommand command, CancellationToken cancellationToken) @@ -28,6 +30,9 @@ public sealed class UpdateNodeCommandHandler(IAppDbContext dbContext, IXuiPanelG gateway.InvalidateClient(node.Id); } + dbContext.AuditLogs.Add(AuditLog.Create( + currentUser.UserId, "NodeUpdated", "Node", node.Id.ToString(), metadata: null, AuditSource.Web)); + return Result.Success(NodeDto.FromDomain(node)); } } diff --git a/backend/src/PnvPanel.Application/Admin/Users/ChangeUserRoleCommandHandler.cs b/backend/src/PnvPanel.Application/Admin/Users/ChangeUserRoleCommandHandler.cs index aed80e5..f1014ad 100644 --- a/backend/src/PnvPanel.Application/Admin/Users/ChangeUserRoleCommandHandler.cs +++ b/backend/src/PnvPanel.Application/Admin/Users/ChangeUserRoleCommandHandler.cs @@ -1,11 +1,23 @@ using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Messaging; using PnvPanel.Application.Common.Models; +using PnvPanel.Domain.Audit; namespace PnvPanel.Application.Admin.Users; -public sealed class ChangeUserRoleCommandHandler(IRoleService roleService) : ICommandHandler +public sealed class ChangeUserRoleCommandHandler(IRoleService roleService, IAppDbContext dbContext, ICurrentUser currentUser) + : ICommandHandler { - public Task Handle(ChangeUserRoleCommand command, CancellationToken cancellationToken) - => roleService.ChangeUserRoleAsync(command.UserId, command.RoleId, cancellationToken); + public async Task Handle(ChangeUserRoleCommand command, CancellationToken cancellationToken) + { + var result = await roleService.ChangeUserRoleAsync(command.UserId, command.RoleId, cancellationToken); + if (!result.IsSuccess) + return result; + + dbContext.AuditLogs.Add(AuditLog.Create( + currentUser.UserId, "UserRoleChanged", "User", command.UserId.ToString(), + metadata: $"{{\"roleId\":\"{command.RoleId}\"}}", AuditSource.Web)); + + return result; + } } diff --git a/backend/src/PnvPanel.Application/Common/Interfaces/IIdentityService.cs b/backend/src/PnvPanel.Application/Common/Interfaces/IIdentityService.cs index ae45518..449dc38 100644 --- a/backend/src/PnvPanel.Application/Common/Interfaces/IIdentityService.cs +++ b/backend/src/PnvPanel.Application/Common/Interfaces/IIdentityService.cs @@ -4,7 +4,8 @@ namespace PnvPanel.Application.Common.Interfaces; public sealed record AuthenticatedUser(Guid Id, string UserName, string Role); -public sealed record CurrentUserProfile(Guid Id, string UserName, Guid RoleId, string Role, bool IsActivated, bool IsBlocked, int MaxConfigs); +public sealed record CurrentUserProfile( + Guid Id, string UserName, Guid RoleId, string Role, bool IsActivated, bool IsBlocked, int MaxConfigs, string SubscriptionToken); public sealed record UserSummaryDto(Guid Id, string UserName, string Role, bool IsActivated, bool IsBlocked, DateTimeOffset? ActivatedAt); diff --git a/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQuery.cs b/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQuery.cs new file mode 100644 index 0000000..59c1424 --- /dev/null +++ b/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQuery.cs @@ -0,0 +1,9 @@ +using PnvPanel.Application.Common.Messaging; +using PnvPanel.Application.Common.Models; + +namespace PnvPanel.Application.Configs.GetMySubscription; + +public sealed record GetMySubscriptionQuery : IQuery>; + +/// SubscriptionToken — Api-слой строит из него абсолютный URL (знает scheme/host запроса), см. GetConfigLinkQuery. +public sealed record MySubscriptionDto(string SubscriptionToken); diff --git a/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQueryHandler.cs b/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQueryHandler.cs new file mode 100644 index 0000000..509e2a9 --- /dev/null +++ b/backend/src/PnvPanel.Application/Configs/GetMySubscription/GetMySubscriptionQueryHandler.cs @@ -0,0 +1,22 @@ +using PnvPanel.Application.Auth; +using PnvPanel.Application.Common.Interfaces; +using PnvPanel.Application.Common.Messaging; +using PnvPanel.Application.Common.Models; + +namespace PnvPanel.Application.Configs.GetMySubscription; + +public sealed class GetMySubscriptionQueryHandler(IIdentityService identityService, ICurrentUser currentUser) + : IQueryHandler> +{ + public async Task> Handle(GetMySubscriptionQuery query, CancellationToken cancellationToken) + { + if (currentUser.UserId is not { } userId) + return Result.Failure(AuthErrors.Unauthorized); + + var profile = await identityService.GetProfileAsync(userId, cancellationToken); + if (profile is null) + return Result.Failure(AuthErrors.Unauthorized); + + return Result.Success(new MySubscriptionDto(profile.SubscriptionToken)); + } +} diff --git a/backend/src/PnvPanel.Infrastructure/DependencyInjection.cs b/backend/src/PnvPanel.Infrastructure/DependencyInjection.cs index 180339d..5574d21 100644 --- a/backend/src/PnvPanel.Infrastructure/DependencyInjection.cs +++ b/backend/src/PnvPanel.Infrastructure/DependencyInjection.cs @@ -26,10 +26,12 @@ public static class DependencyInjection { public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) { - var connectionString = configuration["ConnectionStrings:Default"] - ?? throw new InvalidOperationException("Строка подключения 'ConnectionStrings:Default' не сконфигурирована."); - - services.AddDbContext(options => options.UseNpgsql(connectionString)); + // Строка подключения читается лениво внутри лямбды (а не в локальную переменную сразу), иначе + // в тестах WebApplicationFactory.ConfigureAppConfiguration (Testcontainers-порт) не успевает + // примениться до регистрации DbContext — окажется закэширован дефолт из appsettings.json. + services.AddDbContext(options => options.UseNpgsql( + configuration["ConnectionStrings:Default"] + ?? throw new InvalidOperationException("Строка подключения 'ConnectionStrings:Default' не сконфигурирована."))); services.AddScoped(sp => sp.GetRequiredService()); services diff --git a/backend/src/PnvPanel.Infrastructure/Identity/IdentityService.cs b/backend/src/PnvPanel.Infrastructure/Identity/IdentityService.cs index 4c6f8dd..3d7f395 100644 --- a/backend/src/PnvPanel.Infrastructure/Identity/IdentityService.cs +++ b/backend/src/PnvPanel.Infrastructure/Identity/IdentityService.cs @@ -59,7 +59,8 @@ internal sealed class IdentityService(UserManager userManager, SignInMa return null; var role = await GetPrimaryRoleAsync(user); - return new CurrentUserProfile(user.Id, user.UserName!, role.Id, role.Name!, user.IsActivated, user.IsBlocked, role.MaxConfigs); + return new CurrentUserProfile( + user.Id, user.UserName!, role.Id, role.Name!, user.IsActivated, user.IsBlocked, role.MaxConfigs, user.SubscriptionToken); } public async Task ChangePasswordAsync(Guid userId, string currentPassword, string newPassword, CancellationToken cancellationToken) diff --git a/backend/tests/PnvPanel.Application.Tests/Admin/Inbounds/PublishInboundCommandHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Admin/Inbounds/PublishInboundCommandHandlerTests.cs index 0f443a8..f67a711 100644 --- a/backend/tests/PnvPanel.Application.Tests/Admin/Inbounds/PublishInboundCommandHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Admin/Inbounds/PublishInboundCommandHandlerTests.cs @@ -1,4 +1,6 @@ +using NSubstitute; using PnvPanel.Application.Admin.Inbounds; +using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Tests.TestSupport; using PnvPanel.Domain.Inbounds; using Xunit; @@ -7,6 +9,8 @@ namespace PnvPanel.Application.Tests.Admin.Inbounds; public class PublishInboundCommandHandlerTests { + private readonly ICurrentUser _currentUser = Substitute.For(); + [Fact] public async Task Handle_WhenPublishingExistingInbound_UpdatesPublishState() { @@ -16,7 +20,7 @@ public class PublishInboundCommandHandlerTests await dbContext.SaveChangesAsync(CancellationToken.None); var roleId = Guid.NewGuid(); - var handler = new PublishInboundCommandHandler(dbContext); + var handler = new PublishInboundCommandHandler(dbContext, _currentUser); var command = new PublishInboundCommand(inbound.Id, true, "EU Fast", [roleId], 100); @@ -39,7 +43,7 @@ public class PublishInboundCommandHandlerTests dbContext.Inbounds.Add(inbound); await dbContext.SaveChangesAsync(CancellationToken.None); - var handler = new PublishInboundCommandHandler(dbContext); + var handler = new PublishInboundCommandHandler(dbContext, _currentUser); var command = new PublishInboundCommand(inbound.Id, false, null, [], null); @@ -54,7 +58,7 @@ public class PublishInboundCommandHandlerTests { using var dbContext = InMemoryDbContextFactory.Create(); - var handler = new PublishInboundCommandHandler(dbContext); + var handler = new PublishInboundCommandHandler(dbContext, _currentUser); var command = new PublishInboundCommand(Guid.NewGuid(), true, "EU Fast", [], null); diff --git a/backend/tests/PnvPanel.Application.Tests/Admin/Nodes/RegisterNodeCommandHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Admin/Nodes/RegisterNodeCommandHandlerTests.cs index d3b9400..2147eff 100644 --- a/backend/tests/PnvPanel.Application.Tests/Admin/Nodes/RegisterNodeCommandHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Admin/Nodes/RegisterNodeCommandHandlerTests.cs @@ -11,6 +11,7 @@ public class RegisterNodeCommandHandlerTests { private readonly IXuiPanelGateway _gateway = Substitute.For(); private readonly ISecretProtector _secretProtector = Substitute.For(); + private readonly ICurrentUser _currentUser = Substitute.For(); [Fact] public async Task Handle_WithValidAddress_RegistersNodeWithProtectedPassword() @@ -20,7 +21,7 @@ public class RegisterNodeCommandHandlerTests _gateway.ValidateBaseAddress(Arg.Any()).Returns(Result.Success()); _secretProtector.Protect("secret-password").Returns("protected-secret-password"); - var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector); + var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser); var command = new RegisterNodeCommand("node-1", "https://node1.example.com", "admin", "secret-password", "eu-west"); @@ -38,7 +39,7 @@ public class RegisterNodeCommandHandlerTests { using var dbContext = InMemoryDbContextFactory.Create(); - var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector); + var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser); var command = new RegisterNodeCommand("node-1", "not-a-uri", "admin", "secret-password", null); @@ -58,7 +59,7 @@ public class RegisterNodeCommandHandlerTests var error = Error.Validation("Nodes.SchemeNotAllowed", "Разрешён только HTTPS."); _gateway.ValidateBaseAddress(Arg.Any()).Returns(Result.Failure(error)); - var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector); + var handler = new RegisterNodeCommandHandler(dbContext, _gateway, _secretProtector, _currentUser); var command = new RegisterNodeCommand("node-1", "http://node1.example.com", "admin", "secret-password", null); diff --git a/backend/tests/PnvPanel.Application.Tests/Admin/Users/ChangeUserRoleCommandHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Admin/Users/ChangeUserRoleCommandHandlerTests.cs index 73aac58..3cd03a6 100644 --- a/backend/tests/PnvPanel.Application.Tests/Admin/Users/ChangeUserRoleCommandHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Admin/Users/ChangeUserRoleCommandHandlerTests.cs @@ -2,6 +2,7 @@ using NSubstitute; using PnvPanel.Application.Admin.Users; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Models; +using PnvPanel.Application.Tests.TestSupport; using Xunit; namespace PnvPanel.Application.Tests.Admin.Users; @@ -9,35 +10,42 @@ namespace PnvPanel.Application.Tests.Admin.Users; public class ChangeUserRoleCommandHandlerTests { private readonly IRoleService _roleService = Substitute.For(); + private readonly ICurrentUser _currentUser = Substitute.For(); [Fact] public async Task Handle_DelegatesToRoleServiceAndReturnsSuccess() { + using var dbContext = InMemoryDbContextFactory.Create(); + var userId = Guid.NewGuid(); var roleId = Guid.NewGuid(); _roleService.ChangeUserRoleAsync(userId, roleId, Arg.Any()).Returns(Result.Success()); - var handler = new ChangeUserRoleCommandHandler(_roleService); + var handler = new ChangeUserRoleCommandHandler(_roleService, dbContext, _currentUser); var result = await handler.Handle(new ChangeUserRoleCommand(userId, roleId), CancellationToken.None); Assert.True(result.IsSuccess); await _roleService.Received(1).ChangeUserRoleAsync(userId, roleId, Arg.Any()); + Assert.Single(dbContext.AuditLogs.Local); } [Fact] public async Task Handle_WhenRoleServiceFails_PropagatesFailure() { + using var dbContext = InMemoryDbContextFactory.Create(); + var userId = Guid.NewGuid(); var roleId = Guid.NewGuid(); var error = UserErrors.NotFound; _roleService.ChangeUserRoleAsync(userId, roleId, Arg.Any()).Returns(Result.Failure(error)); - var handler = new ChangeUserRoleCommandHandler(_roleService); + var handler = new ChangeUserRoleCommandHandler(_roleService, dbContext, _currentUser); var result = await handler.Handle(new ChangeUserRoleCommand(userId, roleId), CancellationToken.None); Assert.False(result.IsSuccess); Assert.Equal(error, result.Error); + Assert.Empty(dbContext.AuditLogs.Local); } } diff --git a/backend/tests/PnvPanel.Application.Tests/Auth/GetCurrentUserQueryHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Auth/GetCurrentUserQueryHandlerTests.cs index a5656ff..8db7b2c 100644 --- a/backend/tests/PnvPanel.Application.Tests/Auth/GetCurrentUserQueryHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Auth/GetCurrentUserQueryHandlerTests.cs @@ -40,7 +40,7 @@ public class GetCurrentUserQueryHandlerTests public async Task Handle_AuthenticatedWithProfile_ReturnsCurrentUserDto() { var userId = Guid.NewGuid(); - var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3); + var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3, "sub-token"); _identityService.GetProfileAsync(userId, Arg.Any()).Returns(profile); _identityService.GetTelegramLinkInfoAsync(userId, Arg.Any()) .Returns(new TelegramLinkInfo(true, 42, "alice_tg")); diff --git a/backend/tests/PnvPanel.Application.Tests/Auth/LoginCommandHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Auth/LoginCommandHandlerTests.cs index bdd8b09..0f7494a 100644 --- a/backend/tests/PnvPanel.Application.Tests/Auth/LoginCommandHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Auth/LoginCommandHandlerTests.cs @@ -20,7 +20,7 @@ public class LoginCommandHandlerTests { var userId = Guid.NewGuid(); var authUser = new AuthenticatedUser(userId, "alice", "user"); - var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3); + var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3, SubscriptionToken: "sub-token"); _identityService.ValidateCredentialsAsync("alice", "P@ssw0rd", Arg.Any()) .Returns(Result.Success(authUser)); diff --git a/backend/tests/PnvPanel.Application.Tests/Auth/RefreshCommandHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Auth/RefreshCommandHandlerTests.cs index 60ff8ac..0c72292 100644 --- a/backend/tests/PnvPanel.Application.Tests/Auth/RefreshCommandHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Auth/RefreshCommandHandlerTests.cs @@ -19,7 +19,7 @@ public class RefreshCommandHandlerTests public async Task Handle_WithValidToken_RotatesAndReturnsNewAuthResult() { var userId = Guid.NewGuid(); - var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3); + var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", IsActivated: true, IsBlocked: false, MaxConfigs: 3, SubscriptionToken: "sub-token"); var rotated = new RotatedRefreshToken(userId, "new-refresh-token", DateTimeOffset.UtcNow.AddDays(30)); _refreshTokenService.RotateAsync("old-token", Arg.Any()).Returns(Result.Success(rotated)); diff --git a/backend/tests/PnvPanel.Application.Tests/Configs/GetMyConfigs/GetMyConfigsQueryHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Configs/GetMyConfigs/GetMyConfigsQueryHandlerTests.cs index 101aa99..8aacc62 100644 --- a/backend/tests/PnvPanel.Application.Tests/Configs/GetMyConfigs/GetMyConfigsQueryHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Configs/GetMyConfigs/GetMyConfigsQueryHandlerTests.cs @@ -32,7 +32,7 @@ public class GetMyConfigsQueryHandlerTests dbContext.VpnConfigs.AddRange(activeConfig, revokedConfig, otherUsersConfig); await dbContext.SaveChangesAsync(CancellationToken.None); - var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 5); + var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 5, "sub-token"); _identityService.GetProfileAsync(userId, Arg.Any()).Returns(profile); var handler = new GetMyConfigsQueryHandler(dbContext, _identityService, FakeCurrentUser.Authenticated(userId)); diff --git a/backend/tests/PnvPanel.Application.Tests/Telegram/GetLoginRequestStatusQueryHandlerTests.cs b/backend/tests/PnvPanel.Application.Tests/Telegram/GetLoginRequestStatusQueryHandlerTests.cs index 1a0e0e5..8280823 100644 --- a/backend/tests/PnvPanel.Application.Tests/Telegram/GetLoginRequestStatusQueryHandlerTests.cs +++ b/backend/tests/PnvPanel.Application.Tests/Telegram/GetLoginRequestStatusQueryHandlerTests.cs @@ -75,7 +75,7 @@ public class GetLoginRequestStatusQueryHandlerTests dbContext.TelegramLoginRequests.Add(request); await dbContext.SaveChangesAsync(CancellationToken.None); - var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3); + var profile = new CurrentUserProfile(userId, "alice", Guid.NewGuid(), "user", true, false, 3, "sub-token"); _identityService.GetProfileAsync(userId, Arg.Any()).Returns(profile); _jwtTokenService.GenerateAccessToken(Arg.Any()) .Returns(("access-token", DateTimeOffset.UtcNow.AddMinutes(15))); diff --git a/backend/tests/PnvPanel.IntegrationTests/Admin/NodeInboundCrudTests.cs b/backend/tests/PnvPanel.IntegrationTests/Admin/NodeInboundCrudTests.cs index 12cf88b..597c17f 100644 --- a/backend/tests/PnvPanel.IntegrationTests/Admin/NodeInboundCrudTests.cs +++ b/backend/tests/PnvPanel.IntegrationTests/Admin/NodeInboundCrudTests.cs @@ -8,13 +8,13 @@ namespace PnvPanel.IntegrationTests.Admin; [Collection(IntegrationTestCollection.Name)] public class NodeInboundCrudTests(PnvPanelWebApplicationFactory factory) { - private sealed record NodeResponse(Guid Id, string Name, string BaseAddress, string Username, string? Location, int Status, bool IsEnabled); + private sealed record NodeResponse(Guid Id, string Name, string BaseAddress, string Username, string? Location, string Status, bool IsEnabled); private sealed record InboundResponse( - Guid Id, Guid NodeId, string RemoteInboundId, int Protocol, string Remark, int Port, + Guid Id, Guid NodeId, string RemoteInboundId, string Protocol, string Remark, int Port, bool IsPublished, string? DisplayName, int? MaxClients, IReadOnlyList AllowedRoleIds); - private sealed record SyncNodeResponse(int InboundsSynced, int Status); + private sealed record SyncNodeResponse(int InboundsSynced, string Status); [Fact] public async Task RegisterSyncListPublish_FullNodeInboundLifecycle_Succeeds() diff --git a/backend/tests/PnvPanel.IntegrationTests/Auth/AuthFlowTests.cs b/backend/tests/PnvPanel.IntegrationTests/Auth/AuthFlowTests.cs index 0d6436c..640dfe7 100644 --- a/backend/tests/PnvPanel.IntegrationTests/Auth/AuthFlowTests.cs +++ b/backend/tests/PnvPanel.IntegrationTests/Auth/AuthFlowTests.cs @@ -77,7 +77,7 @@ public class AuthFlowTests(PnvPanelWebApplicationFactory factory) var first = await client.PostJsonAsync("/api/auth/register", new { userName, password = "P@ssw0rd123" }); Assert.Equal(HttpStatusCode.OK, first.StatusCode); - var second = await client.PostJsonAsync("/api/auth/register", new { userName, password = "AnotherPass123" }); + var second = await client.PostJsonAsync("/api/auth/register", new { userName, password = "AnotherPass123!" }); Assert.Equal(HttpStatusCode.Conflict, second.StatusCode); } diff --git a/backend/tests/PnvPanel.IntegrationTests/Configs/ConfigQuotaTests.cs b/backend/tests/PnvPanel.IntegrationTests/Configs/ConfigQuotaTests.cs index 4b11266..7b3e994 100644 --- a/backend/tests/PnvPanel.IntegrationTests/Configs/ConfigQuotaTests.cs +++ b/backend/tests/PnvPanel.IntegrationTests/Configs/ConfigQuotaTests.cs @@ -15,12 +15,14 @@ public class ConfigQuotaTests(PnvPanelWebApplicationFactory factory) private sealed record NodeResponse(Guid Id, string Name); - private sealed record SyncNodeResponse(int InboundsSynced, int Status); + private sealed record SyncNodeResponse(int InboundsSynced, string Status); - private sealed record InboundResponse(Guid Id, Guid NodeId, string RemoteInboundId, int Protocol, string Remark, int Port, bool IsPublished); + private sealed record InboundResponse(Guid Id, Guid NodeId, string RemoteInboundId, string Protocol, string Remark, int Port, bool IsPublished); private sealed record ActivationRequestResponse(Guid Id, string? Comment, DateTimeOffset CreatedAt); + private sealed record MyConfigsResponse(List Configs, int MaxConfigs); + /// /// Доказывает, что pg_advisory_xact_lock в CreateVpnConfigCommandHandler реально защищает /// от гонки: при параллельных запросах ровно Quota проходят, остальные — 409 QuotaExceeded. @@ -98,7 +100,7 @@ public class ConfigQuotaTests(PnvPanelWebApplicationFactory factory) Assert.Equal(ConcurrentAttempts - Quota, quotaExceeded); var myConfigsResponse = await userClient.GetAsync("/api/configs"); - var myConfigs = await myConfigsResponse.ReadAsAsync>(); - Assert.Equal(Quota, myConfigs!.Count); + var myConfigs = await myConfigsResponse.ReadAsAsync(); + Assert.Equal(Quota, myConfigs!.Configs.Count); } } diff --git a/backend/tests/PnvPanel.IntegrationTests/TestSupport/PnvPanelWebApplicationFactory.cs b/backend/tests/PnvPanel.IntegrationTests/TestSupport/PnvPanelWebApplicationFactory.cs index 4f3f339..77d886e 100644 --- a/backend/tests/PnvPanel.IntegrationTests/TestSupport/PnvPanelWebApplicationFactory.cs +++ b/backend/tests/PnvPanel.IntegrationTests/TestSupport/PnvPanelWebApplicationFactory.cs @@ -27,7 +27,10 @@ public sealed class PnvPanelWebApplicationFactory : WebApplicationFactory await _postgres.StartAsync(); + public async Task InitializeAsync() + { + await _postgres.StartAsync(); + } async Task IAsyncLifetime.DisposeAsync() { @@ -48,6 +51,9 @@ public sealed class PnvPanelWebApplicationFactory : WebApplicationFactory=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} @@ -67,6 +206,26 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@hookform/resolvers@5.4.0': + resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} + peerDependencies: + react-hook-form: ^7.55.0 + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -83,6 +242,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@microsoft/signalr@10.0.0': + resolution: {integrity: sha512-0BRqz/uCx3JdrOqiqgFhih/+hfTERaUfCZXFB52uMaZJrKaPRzHzMuqVsJC/V3pt7NozcNXGspjKiQEK+X7P2w==} + '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: @@ -214,6 +376,387 @@ packages: cpu: [x64] os: [win32] + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} + + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} + 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 + + '@radix-ui/react-checkbox@1.3.6': + resolution: {integrity: sha512-eUEUoGMDpfkgHWSE97ZZaUJtzR1M7EKnNIpD1Q16+8JR9NWghcaqMulx9PuCQ720w0UclfYn6FEbCdd5Hx087g==} + 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 + + '@radix-ui/react-collection@1.1.11': + resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} + 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 + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.18': + resolution: {integrity: sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==} + 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 + + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.14': + resolution: {integrity: sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==} + 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 + + '@radix-ui/react-dropdown-menu@2.1.19': + resolution: {integrity: sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==} + 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 + + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.11': + resolution: {integrity: sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==} + 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 + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.11': + resolution: {integrity: sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==} + 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 + + '@radix-ui/react-menu@2.1.19': + resolution: {integrity: sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==} + 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 + + '@radix-ui/react-popper@1.3.2': + resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} + 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 + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + 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 + + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + 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 + + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + 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 + + '@radix-ui/react-roving-focus@1.1.14': + resolution: {integrity: sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==} + 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 + + '@radix-ui/react-select@2.3.2': + resolution: {integrity: sha512-brXD6C/V0fVK0DDbscLVw6LsXrjQ+ay8jdOBaN+tLb4vsHsAMm6Gt6eT77wHX1Eq8GPtD5rJ+RxFtfDozsb4+Q==} + 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 + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.2': + resolution: {integrity: sha512-tgRBI3DdNwAJYE4BBZyZcz/HRRCvAsPkRvG1wvKc+41tBGMxPn/a87T/wikXAvyDypNQ9kaZwHbeZe+veHCGpA==} + 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 + + '@radix-ui/react-tabs@1.1.16': + resolution: {integrity: sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==} + 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 + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + 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 + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/config@0.22.0': + resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==} + + '@redocly/openapi-core@1.34.17': + resolution: {integrity: sha512-wsV2keCt6B806XpSdezbWZ9aFJYf14YVh+XQf0ESt7M90yqVuxH9//PxvtC70sgj9OCkRM3nRaLfu4MsGQZRig==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@reduxjs/toolkit@2.12.0': + resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rolldown/binding-android-arm64@1.1.3': resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -312,6 +855,12 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@tailwindcss/node@4.3.2': resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} @@ -406,6 +955,10 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tanstack/history@1.162.0': + resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} + engines: {node: '>=20.19'} + '@tanstack/query-core@5.101.2': resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} @@ -414,9 +967,122 @@ packages: peerDependencies: react: ^18 || ^19 + '@tanstack/react-router-devtools@1.167.0': + resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/react-router': ^1.170.0 + '@tanstack/router-core': ^1.170.0 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + '@tanstack/router-core': + optional: true + + '@tanstack/react-router@1.170.16': + resolution: {integrity: sha512-w6eq1IJklujs1tESazaK/FxH0+H2l8vm/QPuu1cD3oRW/ubgKneQpd7b64ti/8gUyEimzimJQZDmJr6YHfP5+g==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.9.3': + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/router-core@1.171.13': + resolution: {integrity: sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA==} + engines: {node: '>=20.19'} + + '@tanstack/router-devtools-core@1.168.0': + resolution: {integrity: sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg==} + engines: {node: '>=20.19'} + peerDependencies: + '@tanstack/router-core': ^1.170.0 + csstype: ^3.0.10 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.167.17': + resolution: {integrity: sha512-xtB9tB2Ws0tWR6Pi7nc3Qk9IYgoh1mQCKWjHqIl9tf6BNUpKoqniJoPAQ4+LGrK8FeZYU0o0p/qlZEyj9FAulA==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.18': + resolution: {integrity: sha512-MofS28/axfnfnhOD2RSgJEaU882aX5RsAzhGz5Vc4XhAmvCjy919u9JrNs4QsTWFbTD1P7IJ8WFlFVsrg0pStg==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.15 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} + engines: {node: '>=20.19'} + + '@tanstack/store@0.9.3': + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/node@24.13.2': resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} @@ -428,6 +1094,9 @@ packages: '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@vitejs/plugin-react@6.0.3': resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -441,17 +1110,172 @@ packages: babel-plugin-react-compiler: optional: true + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.10.40: + resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + + browserslist@4.28.4: + resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001800: + resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + electron-to-chromium@1.5.383: + resolution: {integrity: sha512-I2484/KkAvl8lm9VyjH2JnbOIV0d/UCqT7gbzs6l+o6Vmn9wgB66uVcKX+Vk6HrXtY6fbWTOEXuv8waDTuFNCw==} + enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} + es-toolkit@1.49.0: + resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + eventsource@2.0.2: + resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -461,17 +1285,37 @@ packages: picomatch: optional: true + fetch-cookie@2.2.0: + resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + goober@2.1.19: + resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} + peerDependencies: + csstype: ^3.0.10 + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + i18next@26.3.4: resolution: {integrity: sha512-pa7m0d7pBDqGHZxljT+WPFeyFgQ7P7SciPPo1tTqYuO0z4sqADYhwnBESmmGp/wEof1inwdls/k8ZgTg8rxFHA==} peerDependencies: @@ -480,10 +1324,49 @@ packages: typescript: optional: true + immer@11.1.8: + resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + isbot@5.1.44: + resolution: {integrity: sha512-PGEHtwMnKbZpeSEXW2Utx+/JWed7dp6DiH0WWg33vGSDA7RUvpUeJSVlLrVkQ1RCpvDOUc/eH9ql7VsdbBZ8pA==} + engines: {node: '>=18'} + jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -558,14 +1441,48 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@1.22.0: + resolution: {integrity: sha512-c9o3l0PiNcgOQDW4F31BEYHudE7kgxVt3o30qMl36ZPwTxXlGB4QnLilhERvVM4uh/pl5MDyY1/gzZSYcHDtBg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.15: resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.50: + resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} + engines: {node: '>=18'} + + openapi-typescript@7.13.0: + resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + oxlint@1.72.0: resolution: {integrity: sha512-1rhdZIP/EvoI91ABIwNU5Q8+bWf8mjrS5UzIOZld4d4bXxJvtlUhlQvaoTogIGin/qdErMOrwaIJvCSIAKTLhA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -579,6 +1496,13 @@ packages: vite-plus: optional: true + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -586,15 +1510,45 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} + engines: {node: '>=14'} + hasBin: true + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + react-dom@19.2.7: resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: react: ^19.2.7 + react-hook-form@7.80.0: + resolution: {integrity: sha512-4P+fk6oXsxY+6xSj7Euhc2sumQD8zQqCuVHoJwoyp9EchP+IUW9OESB7uHFJOKsIBQ4MQqYE84INJFqUCYNoOg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-i18next@17.0.8: resolution: {integrity: sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==} peerDependencies: @@ -611,10 +1565,85 @@ packages: typescript: optional: true + react-is@19.2.7: + resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + + react-redux@9.3.0: + resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + 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 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + 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 + react@19.2.7: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + recharts@3.9.1: + resolution: {integrity: sha512-WMcwlXcB7l+BbxiEdyClkG+1sxrMHNZpzT577LEvU4+rXPd8oTAy1wXk72hnk2KOOmxuLvw3z5DtXT7HEAydtg==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} + rolldown@1.1.3: resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -623,10 +1652,34 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + seroval-plugins@1.5.4: + resolution: {integrity: sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.5.4: + resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} + engines: {node: '>=10'} + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + tailwindcss@4.3.2: resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} @@ -634,13 +1687,27 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -649,11 +1716,83 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + 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 + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + 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 + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} + vite@8.1.1: resolution: {integrity: sha512-X/05/cT+VITy2AeDc1der6smvGWWREtL4hPbPTaVbjSBuuWkmNOjR6HP3NzqcQA2nF6VHGUPaFRJyft/2AE9Kg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -701,10 +1840,162 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + ws@7.5.11: + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + '@babel/runtime@7.29.7': {} + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@emnapi/core@1.11.1': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -721,6 +2012,28 @@ snapshots: tslib: 2.8.1 optional: true + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.11': {} + + '@hookform/resolvers@5.4.0(react-hook-form@7.80.0(react@19.2.7))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.80.0(react@19.2.7) + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -740,6 +2053,18 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@microsoft/signalr@10.0.0': + dependencies: + abort-controller: 3.0.0 + eventsource: 2.0.2 + fetch-cookie: 2.2.0 + node-fetch: 2.7.0 + ws: 7.5.11 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -806,6 +2131,398 @@ snapshots: '@oxlint/binding-win32-x64-msvc@1.72.0': optional: true + '@radix-ui/number@1.1.2': {} + + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-checkbox@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-dropdown-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-focus-scope@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-label@2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-select@2.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-switch@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-tabs@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/rect@1.1.2': {} + + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/config@0.22.0': {} + + '@redocly/openapi-core@1.34.17(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.22.0 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.2.0 + minimatch: 5.1.9 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + + '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.8 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.2.0 + optionalDependencies: + react: 19.2.7 + react-redux: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) + '@rolldown/binding-android-arm64@1.1.3': optional: true @@ -857,6 +2574,10 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@standard-schema/spec@1.1.0': {} + + '@standard-schema/utils@0.3.0': {} + '@tailwindcss/node@4.3.2': dependencies: '@jridgewell/remapping': 2.3.5 @@ -925,6 +2646,8 @@ snapshots: tailwindcss: 4.3.2 vite: 8.1.1(@types/node@24.13.2)(jiti@2.7.0) + '@tanstack/history@1.162.0': {} + '@tanstack/query-core@5.101.2': {} '@tanstack/react-query@5.101.2(react@19.2.7)': @@ -932,11 +2655,139 @@ snapshots: '@tanstack/query-core': 5.101.2 react: 19.2.7 + '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/react-router': 1.170.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.171.13)(csstype@3.2.3) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@tanstack/router-core': 1.171.13 + transitivePeerDependencies: + - csstype + + '@tanstack/react-router@1.170.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/history': 1.162.0 + '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.13 + isbot: 5.1.44 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/store': 0.9.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) + + '@tanstack/react-table@8.21.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@tanstack/router-core@1.171.13': + dependencies: + '@tanstack/history': 1.162.0 + cookie-es: 3.1.1 + seroval: 1.5.4 + seroval-plugins: 1.5.4(seroval@1.5.4) + + '@tanstack/router-devtools-core@1.168.0(@tanstack/router-core@1.171.13)(csstype@3.2.3)': + dependencies: + '@tanstack/router-core': 1.171.13 + clsx: 2.1.1 + goober: 2.1.19(csstype@3.2.3) + optionalDependencies: + csstype: 3.2.3 + + '@tanstack/router-generator@1.167.17': + dependencies: + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.13 + '@tanstack/router-utils': 1.162.2 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.4 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(rolldown@1.1.3)(supports-color@10.2.2)(vite@8.1.1(@types/node@24.13.2)(jiti@2.7.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@tanstack/router-core': 1.171.13 + '@tanstack/router-generator': 1.167.17 + '@tanstack/router-utils': 1.162.2 + chokidar: 5.0.0 + unplugin: 3.3.0(rolldown@1.1.3)(vite@8.1.1(@types/node@24.13.2)(jiti@2.7.0)) + zod: 4.4.3 + optionalDependencies: + '@tanstack/react-router': 1.170.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + vite: 8.1.1(@types/node@24.13.2)(jiti@2.7.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.2': + dependencies: + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/store@0.9.3': {} + + '@tanstack/table-core@8.21.3': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true + '@types/d3-array@3.2.2': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + '@types/node@24.13.2': dependencies: undici-types: 7.18.2 @@ -949,39 +2800,208 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/use-sync-external-store@0.0.6': {} + '@vitejs/plugin-react@6.0.3(vite@8.1.1(@types/node@24.13.2)(jiti@2.7.0))': dependencies: '@rolldown/pluginutils': 1.0.1 vite: 8.1.1(@types/node@24.13.2)(jiti@2.7.0) + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + agent-base@7.1.4: {} + + ansi-colors@4.1.3: {} + + ansis@4.3.1: {} + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/parser': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.10.40: {} + + brace-expansion@2.1.1: + dependencies: + balanced-match: 1.0.2 + + browserslist@4.28.4: + dependencies: + baseline-browser-mapping: 2.10.40 + caniuse-lite: 1.0.30001800 + electron-to-chromium: 1.5.383 + node-releases: 2.0.50 + update-browserslist-db: 1.2.3(browserslist@4.28.4) + + caniuse-lite@1.0.30001800: {} + + change-case@5.4.4: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + colorette@1.4.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + csstype@3.2.3: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + decimal.js-light@2.5.1: {} + detect-libc@2.1.2: {} + detect-node-es@1.1.0: {} + + diff@8.0.4: {} + + electron-to-chromium@1.5.383: {} + enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 + es-toolkit@1.49.0: {} + + escalade@3.2.0: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.4: {} + + eventsource@2.0.2: {} + + fast-deep-equal@3.1.3: {} + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 + fetch-cookie@2.2.0: + dependencies: + set-cookie-parser: 2.7.2 + tough-cookie: 4.1.4 + fsevents@2.3.3: optional: true + gensync@1.0.0-beta.2: {} + + get-nonce@1.0.1: {} + + goober@2.1.19(csstype@3.2.3): + dependencies: + csstype: 3.2.3 + graceful-fs@4.2.11: {} html-parse-stringify@3.0.1: dependencies: void-elements: 3.1.0 + https-proxy-agent@7.0.6(supports-color@10.2.2): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + i18next@26.3.4(typescript@6.0.3): optionalDependencies: typescript: 6.0.3 + immer@11.1.8: {} + + index-to-position@1.2.0: {} + + internmap@2.0.3: {} + + isbot@5.1.44: {} + jiti@2.7.0: {} + js-levenshtein@1.1.6: {} + + js-tokens@4.0.0: {} + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + lightningcss-android-arm64@1.32.0: optional: true @@ -1031,12 +3051,42 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@1.22.0(react@19.2.7): + dependencies: + react: 19.2.7 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + + ms@2.1.3: {} + nanoid@3.3.15: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.50: {} + + openapi-typescript@7.13.0(typescript@6.0.3): + dependencies: + '@redocly/openapi-core': 1.34.17(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 6.0.3 + yargs-parser: 21.1.1 + oxlint@1.72.0: optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.72.0 @@ -1059,21 +3109,49 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.72.0 '@oxlint/binding-win32-x64-msvc': 1.72.0 + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + pathe@2.0.3: {} + picocolors@1.1.1: {} picomatch@4.0.4: {} + pluralize@8.0.0: {} + postcss@8.5.16: dependencies: nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@3.9.4: {} + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + punycode@2.3.1: {} + + qrcode.react@4.2.0(react@19.2.7): + dependencies: + react: 19.2.7 + + querystringify@2.2.0: {} + react-dom@19.2.7(react@19.2.7): dependencies: react: 19.2.7 scheduler: 0.27.0 + react-hook-form@7.80.0(react@19.2.7): + dependencies: + react: 19.2.7 + react-i18next@17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 @@ -1085,8 +3163,80 @@ snapshots: react-dom: 19.2.7(react@19.2.7) typescript: 6.0.3 + react-is@19.2.7: {} + + react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + redux: 5.0.1 + + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + get-nonce: 1.0.1 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + react@19.2.7: {} + readdirp@5.0.0: {} + + recharts@3.9.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.49.0 + eventemitter3: 5.0.4 + immer: 11.1.8 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-is: 19.2.7 + react-redux: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) + reselect: 5.2.0 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.6.0(react@19.2.7) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + reselect@5.2.0: {} + rolldown@1.1.3: dependencies: '@oxc-project/types': 0.137.0 @@ -1110,28 +3260,110 @@ snapshots: scheduler@0.27.0: {} + semver@6.3.1: {} + + seroval-plugins@1.5.4(seroval@1.5.4): + dependencies: + seroval: 1.5.4 + + seroval@1.5.4: {} + + set-cookie-parser@2.7.2: {} + source-map-js@1.2.1: {} + supports-color@10.2.2: {} + + tailwind-merge@3.6.0: {} + tailwindcss@4.3.2: {} tapable@2.3.3: {} + tiny-invariant@1.3.3: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tslib@2.8.1: - optional: true + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: {} + + tslib@2.8.1: {} + + type-fest@4.41.0: {} typescript@6.0.3: {} undici-types@7.18.2: {} + universalify@0.2.0: {} + + unplugin@3.3.0(rolldown@1.1.3)(vite@8.1.1(@types/node@24.13.2)(jiti@2.7.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.1.3 + vite: 8.1.1(@types/node@24.13.2)(jiti@2.7.0) + + update-browserslist-db@1.2.3(browserslist@4.28.4): + dependencies: + browserslist: 4.28.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js-replace@1.0.1: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + use-sync-external-store@1.6.0(react@19.2.7): dependencies: react: 19.2.7 + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + vite@8.1.1(@types/node@24.13.2)(jiti@2.7.0): dependencies: lightningcss: 1.32.0 @@ -1145,3 +3377,29 @@ snapshots: jiti: 2.7.0 void-elements@3.1.0: {} + + webidl-conversions@3.0.1: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + ws@7.5.11: {} + + yallist@3.1.1: {} + + yaml-ast-parser@0.0.43: {} + + yargs-parser@21.1.1: {} + + zod@4.4.3: {} + + zustand@5.0.14(@types/react@19.2.17)(immer@11.1.8)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)): + optionalDependencies: + '@types/react': 19.2.17 + immer: 11.1.8 + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 8c8867c..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useTranslation } from 'react-i18next' -import { useTheme, type Theme } from './lib/theme' -import { setLanguage } from './lib/i18n' - -function App() { - const { t, i18n } = useTranslation() - const { theme, setTheme } = useTheme() - - const themes: Theme[] = ['light', 'dark', 'system'] - const langs = ['ru', 'en'] - - return ( -
-
- {t('appName')} -
- - -
-
- -
-

{t('appName')}

-

{t('tagline')}

-

{t('scaffoldNote')}

- -
-
- ) -} - -export default App diff --git a/frontend/src/features/activation/ActivationGate.tsx b/frontend/src/features/activation/ActivationGate.tsx new file mode 100644 index 0000000..973f63c --- /dev/null +++ b/frontend/src/features/activation/ActivationGate.tsx @@ -0,0 +1,83 @@ +import { useState, type ReactNode } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { useTranslation } from 'react-i18next' +import { toast } from '@/shared/ui/toast-store' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/ui/card' +import { Button } from '@/shared/ui/button' +import { Label } from '@/shared/ui/label' +import { HttpError } from '@/shared/api/client' +import { getActivationStatus, requestActivation } from './api' + +/** Показывает детям только активированным пользователям; иначе — экран запроса активации. */ +export function ActivationGate({ children }: { children: ReactNode }) { + const { t } = useTranslation() + const queryClient = useQueryClient() + const [comment, setComment] = useState('') + const [submitting, setSubmitting] = useState(false) + + const { data, isLoading, isError, refetch } = useQuery({ + queryKey: ['activation-status'], + queryFn: getActivationStatus, + }) + + if (isLoading) return null + if (isError || !data) { + return ( +
+ + +

{t('auth.genericError')}

+ +
+
+
+ ) + } + if (data.isActivated) return <>{children} + + const handleSubmit = async () => { + setSubmitting(true) + try { + await requestActivation(comment.trim() || undefined) + await queryClient.invalidateQueries({ queryKey: ['activation-status'] }) + } catch (error) { + const message = error instanceof HttpError && error.status === 409 ? t('activation.alreadyPending') : t('auth.genericError') + toast.error(message) + } finally { + setSubmitting(false) + } + } + + return ( +
+ + + {t('activation.title')} + {t('activation.description')} + + + {data.pendingRequest ? ( +

{t('activation.pending')}

+ ) : ( + <> +
+ +