From 4ad18912fe52b304a104f436751a51b85a447b9d Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:21:55 +0100 Subject: [PATCH 01/53] feat(role): add RolePermission model and migration; include confidential field in service request schema --- apps/api/docs/docs.go | 3 +++ apps/api/docs/swagger.json | 3 +++ apps/api/docs/swagger.yaml | 2 ++ apps/api/internal/domain/role/model.go | 18 +++++++++++++++++- ...0020_create_role_permissions_table.down.sql | 1 + ...000020_create_role_permissions_table.up.sql | 12 ++++++++++++ ...ernalDomainServiceCreateServiceRequest.json | 1 + apps/web/src/lib/api/generated/zod.ts | 1 + apps/web/src/lib/api/schema.d.ts | 9 ++------- 9 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 apps/api/internal/migrations/000020_create_role_permissions_table.down.sql create mode 100644 apps/api/internal/migrations/000020_create_role_permissions_table.up.sql diff --git a/apps/api/docs/docs.go b/apps/api/docs/docs.go index fb3a113..2cbcf3f 100644 --- a/apps/api/docs/docs.go +++ b/apps/api/docs/docs.go @@ -2041,6 +2041,9 @@ const docTemplate = `{ "type": "string" } }, + "confidential": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 1000 diff --git a/apps/api/docs/swagger.json b/apps/api/docs/swagger.json index 97b3704..824ba81 100644 --- a/apps/api/docs/swagger.json +++ b/apps/api/docs/swagger.json @@ -2034,6 +2034,9 @@ "type": "string" } }, + "confidential": { + "type": "boolean" + }, "description": { "type": "string", "maxLength": 1000 diff --git a/apps/api/docs/swagger.yaml b/apps/api/docs/swagger.yaml index 92c4259..20e8e6c 100644 --- a/apps/api/docs/swagger.yaml +++ b/apps/api/docs/swagger.yaml @@ -448,6 +448,8 @@ definitions: maxItems: 10 minItems: 1 type: array + confidential: + type: boolean description: maxLength: 1000 type: string diff --git a/apps/api/internal/domain/role/model.go b/apps/api/internal/domain/role/model.go index 2540f27..0853828 100644 --- a/apps/api/internal/domain/role/model.go +++ b/apps/api/internal/domain/role/model.go @@ -11,9 +11,25 @@ type Role struct { ServiceID uuid.UUID `gorm:"column:service_id;type:uuid;not null;index"` Name string `gorm:"column:name;type:varchar(255);not null"` Description string `gorm:"column:description;type:text"` - Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0"` + Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0"` // Deprecated for granular resources, used for global IsDefault bool `gorm:"column:is_default;type:boolean;not null;default:false"` Priority int `gorm:"column:priority;type:integer;not null;default:0"` + + // Associations + Permissions []RolePermission `gorm:"foreignKey:RoleID" json:"permissions,omitempty"` +} + +// RolePermission represents a bitmask for a specific resource within a role +type RolePermission struct { + database.BaseModel + RoleID uuid.UUID `gorm:"column:role_id;type:uuid;not null;index"` + Resource *string `gorm:"column:resource;type:varchar(100)"` // NULL = global + Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0"` +} + +// TableName returns the table name for Gorm +func (RolePermission) TableName() string { + return "role_permissions" } // TableName returns the table name for Gorm diff --git a/apps/api/internal/migrations/000020_create_role_permissions_table.down.sql b/apps/api/internal/migrations/000020_create_role_permissions_table.down.sql new file mode 100644 index 0000000..ce067b3 --- /dev/null +++ b/apps/api/internal/migrations/000020_create_role_permissions_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS role_permissions; diff --git a/apps/api/internal/migrations/000020_create_role_permissions_table.up.sql b/apps/api/internal/migrations/000020_create_role_permissions_table.up.sql new file mode 100644 index 0000000..6897f16 --- /dev/null +++ b/apps/api/internal/migrations/000020_create_role_permissions_table.up.sql @@ -0,0 +1,12 @@ +CREATE TABLE role_permissions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE, + role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE, + resource VARCHAR(100), + bitmask NUMERIC(20) NOT NULL DEFAULT 0, + UNIQUE(role_id, resource) +); + +CREATE INDEX idx_role_permissions_role_id ON role_permissions(role_id); diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainServiceCreateServiceRequest.json b/apps/web/src/lib/api/generated/schemas/internalDomainServiceCreateServiceRequest.json index 6bb71ea..8443383 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainServiceCreateServiceRequest.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainServiceCreateServiceRequest.json @@ -3,6 +3,7 @@ "required": ["allowed_scopes", "domain", "name", "redirect_uris", "slug"], "properties": { "allowed_scopes": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "type": "string" } }, + "confidential": { "type": "boolean" }, "description": { "type": "string", "maxLength": 1000 }, "domain": { "type": "string" }, "name": { "type": "string", "maxLength": 255, "minLength": 3 }, diff --git a/apps/web/src/lib/api/generated/zod.ts b/apps/web/src/lib/api/generated/zod.ts index 4566839..47cf924 100644 --- a/apps/web/src/lib/api/generated/zod.ts +++ b/apps/web/src/lib/api/generated/zod.ts @@ -259,6 +259,7 @@ export const internalDomainRoleUpdateRoleRequestSchema = z.object({ export const internalDomainServiceCreateServiceRequestSchema = z.object({ allowed_scopes: z.array(z.string()).min(1).max(10), + confidential: z.optional(z.boolean()), description: z.optional(z.string().max(1000)), domain: z.string(), name: z.string().min(3).max(255), diff --git a/apps/web/src/lib/api/schema.d.ts b/apps/web/src/lib/api/schema.d.ts index e3c6fa4..dfb11f9 100644 --- a/apps/web/src/lib/api/schema.d.ts +++ b/apps/web/src/lib/api/schema.d.ts @@ -1407,13 +1407,7 @@ export interface components { created_at: string; details?: Record; /** @enum {string} */ - event_type: - | "LOGIN_SUCCESS" - | "LOGIN_FAILURE" - | "LOGOUT" - | "REGISTER" - | "TOKEN_REFRESH" - | "AUTHORIZATION_CODE_GRANTED"; + event_type: "LOGIN_SUCCESS" | "LOGIN_FAILURE" | "LOGOUT" | "REGISTER" | "TOKEN_REFRESH" | "AUTHORIZATION_CODE_GRANTED"; id: string; ip_address: string; username: string; @@ -1562,6 +1556,7 @@ export interface components { }; "internal_domain_service.CreateServiceRequest": { allowed_scopes: string[]; + confidential?: boolean; description?: string; domain: string; name: string; From 5da0a07b3b226cf20bb086bb97a6f787de7e863c Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:27:52 +0100 Subject: [PATCH 02/53] feat(role): preload permissions in role repository queries and enhance role assignment logic in service --- apps/api/internal/domain/role/repository.go | 10 +- apps/api/internal/domain/role/service.go | 133 ++++++++++++++------ 2 files changed, 99 insertions(+), 44 deletions(-) diff --git a/apps/api/internal/domain/role/repository.go b/apps/api/internal/domain/role/repository.go index b7ddf80..a4b30ed 100644 --- a/apps/api/internal/domain/role/repository.go +++ b/apps/api/internal/domain/role/repository.go @@ -43,7 +43,7 @@ func (r *repository) Create(role *Role) error { func (r *repository) FindByID(id string) (*Role, error) { var role Role - if err := r.db.Where("id = ?", id).First(&role).Error; err != nil { + if err := r.db.Preload("Permissions").Where("id = ?", id).First(&role).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRoleNotFound } @@ -54,7 +54,7 @@ func (r *repository) FindByID(id string) (*Role, error) { func (r *repository) FindByName(serviceID, name string) (*Role, error) { var role Role - if err := r.db.Where("service_id = ? AND name = ?", serviceID, name).First(&role).Error; err != nil { + if err := r.db.Preload("Permissions").Where("service_id = ? AND name = ?", serviceID, name).First(&role).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRoleNotFound } @@ -65,7 +65,7 @@ func (r *repository) FindByName(serviceID, name string) (*Role, error) { func (r *repository) FindByServiceID(serviceID string) ([]*Role, error) { var roles []*Role - if err := r.db.Where("service_id = ?", serviceID).Order("priority DESC").Find(&roles).Error; err != nil { + if err := r.db.Preload("Permissions").Where("service_id = ?", serviceID).Order("priority DESC").Find(&roles).Error; err != nil { return nil, err } return roles, nil @@ -73,7 +73,7 @@ func (r *repository) FindByServiceID(serviceID string) ([]*Role, error) { func (r *repository) FindDefaultByServiceID(serviceID string) (*Role, error) { var role Role - if err := r.db.Where("service_id = ? AND is_default = ?", serviceID, true).First(&role).Error; err != nil { + if err := r.db.Preload("Permissions").Where("service_id = ? AND is_default = ?", serviceID, true).First(&role).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRoleNotFound } @@ -84,7 +84,7 @@ func (r *repository) FindDefaultByServiceID(serviceID string) (*Role, error) { func (r *repository) FindAllDefaults() ([]*Role, error) { var roles []*Role - if err := r.db.Where("is_default = ?", true).Find(&roles).Error; err != nil { + if err := r.db.Preload("Permissions").Where("is_default = ?", true).Find(&roles).Error; err != nil { return nil, err } return roles, nil diff --git a/apps/api/internal/domain/role/service.go b/apps/api/internal/domain/role/service.go index 1afecd7..676c3ba 100644 --- a/apps/api/internal/domain/role/service.go +++ b/apps/api/internal/domain/role/service.go @@ -165,62 +165,117 @@ func (s *service) AssignRole(userID, roleID string) error { return err } + uid, err := uuid.Parse(userID) + if err != nil { + return err + } + rid, err := uuid.Parse(roleID) + if err != nil { + return err + } + userPerms, err := txSvc.permissionRepo.FindUserPermissionsByUserIDAndServiceID(userID, role.ServiceID.String()) if err != nil { return err } - var userPerm *permission.UserPermission - // Find the global permission (resource is null) - for _, p := range userPerms { - if p.Resource == nil { - userPerm = p - break + processedResources := make(map[string]bool) + + for _, up := range userPerms { + if up.RoleID != nil { + oldRole, err := txSvc.repo.FindByID(up.RoleID.String()) + if err != nil { + up.RoleID = nil + } else { + var oldRoleMask uint64 + if up.Resource == nil { + oldRoleMask = oldRole.Bitmask + } else { + for _, rp := range oldRole.Permissions { + if rp.Resource != nil && *rp.Resource == *up.Resource { + oldRoleMask = rp.Bitmask + break + } + } + } + up.Bitmask = up.Bitmask &^ oldRoleMask + up.RoleID = nil + } + if err := txSvc.permissionRepo.UpdateUserPermission(up); err != nil { + return err + } } } - if userPerm == nil { - // Create new permission - uid, err := uuid.Parse(userID) - if err != nil { - return err + if role.Bitmask > 0 { + var globalPerm *permission.UserPermission + for _, up := range userPerms { + if up.Resource == nil { + globalPerm = up + break + } } - rid, err := uuid.Parse(roleID) - if err != nil { - return err + if globalPerm == nil { + globalPerm = &permission.UserPermission{ + UserID: uid, + ServiceID: role.ServiceID, + RoleID: &rid, + Bitmask: role.Bitmask, + Resource: nil, + } + if err := txSvc.permissionRepo.CreateUserPermission(globalPerm); err != nil { + return err + } + } else { + globalPerm.RoleID = &rid + globalPerm.Bitmask = globalPerm.Bitmask | role.Bitmask + if err := txSvc.permissionRepo.UpdateUserPermission(globalPerm); err != nil { + return err + } } - - userPerm = &permission.UserPermission{ - UserID: uid, - ServiceID: role.ServiceID, - RoleID: &rid, - Bitmask: role.Bitmask, - Resource: nil, - } - return txSvc.permissionRepo.CreateUserPermission(userPerm) + processedResources["__global__"] = true } - // Update existing permission - if userPerm.RoleID != nil { - oldRole, err := txSvc.repo.FindByID(userPerm.RoleID.String()) - if err != nil { - return fmt.Errorf("failed to fetch old role %s for user %s: %w", userPerm.RoleID, userID, err) + for _, rp := range role.Permissions { + var resourceKey string + if rp.Resource != nil { + resourceKey = *rp.Resource + } else { + resourceKey = "__global__" } - // Remove old role's bits - userPerm.Bitmask = userPerm.Bitmask &^ oldRole.Bitmask - } - userPerm.Bitmask = userPerm.Bitmask | role.Bitmask + if resourceKey == "__global__" && processedResources["__global__"] { + continue + } - rid, err := uuid.Parse(roleID) - if err != nil { - return err - } - userPerm.RoleID = &rid + var targetPerm *permission.UserPermission + for _, up := range userPerms { + if up.Resource != nil && *up.Resource == resourceKey { + targetPerm = up + break + } + } - if err := txSvc.permissionRepo.UpdateUserPermission(userPerm); err != nil { - return err + if targetPerm == nil { + resPtr := rp.Resource + targetPerm = &permission.UserPermission{ + UserID: uid, + ServiceID: role.ServiceID, + RoleID: &rid, + Bitmask: rp.Bitmask, + Resource: resPtr, + } + if err := txSvc.permissionRepo.CreateUserPermission(targetPerm); err != nil { + return err + } + } else { + targetPerm.RoleID = &rid + targetPerm.Bitmask = targetPerm.Bitmask | rp.Bitmask + if err := txSvc.permissionRepo.UpdateUserPermission(targetPerm); err != nil { + return err + } + } } // Increment permission version to invalidate tokens From 06fa7d7bd74dd9d207cb412f2586cceef6ca7a24 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:31:32 +0100 Subject: [PATCH 03/53] feat(service): validate service ID format in GetService method --- apps/api/internal/domain/service/handler.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/api/internal/domain/service/handler.go b/apps/api/internal/domain/service/handler.go index b13ccdf..289aa53 100644 --- a/apps/api/internal/domain/service/handler.go +++ b/apps/api/internal/domain/service/handler.go @@ -3,6 +3,7 @@ package service import ( "github.com/Anvoria/authly/internal/utils" "github.com/gofiber/fiber/v2" + "github.com/google/uuid" ) type Handler struct { @@ -64,6 +65,10 @@ func (h *Handler) GetService(c *fiber.Ctx) error { return utils.ErrorResponse(c, utils.NewAPIError("VALIDATION_ERROR", "ID is required", fiber.StatusBadRequest)) } + if _, err := uuid.Parse(id); err != nil { + return utils.ErrorResponse(c, utils.NewAPIError("VALIDATION_ERROR", "Invalid ID format", fiber.StatusBadRequest)) + } + svc, err := h.serviceService.FindByID(id) if err != nil { if err == ErrServiceNotFound { From 8deea7643e6ebe00198440cc97767570d0edb372 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:53:00 +0100 Subject: [PATCH 04/53] feat(tabs): implement Tabs component with List, Trigger, and Content subcomponents --- apps/web/src/components/ui/Tabs.tsx | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 apps/web/src/components/ui/Tabs.tsx diff --git a/apps/web/src/components/ui/Tabs.tsx b/apps/web/src/components/ui/Tabs.tsx new file mode 100644 index 0000000..49df823 --- /dev/null +++ b/apps/web/src/components/ui/Tabs.tsx @@ -0,0 +1,51 @@ +"use client"; + +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; +import { cn } from "@/authly/lib/utils"; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; From f717a7e31f4d6a09167027a4f79a2e79f9c0564c Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:20:38 +0100 Subject: [PATCH 05/53] feat(permission): add bulk update and delete methods for user permissions by role; implement user retrieval by role ID --- .../internal/domain/permission/repository.go | 38 +++++ apps/api/internal/domain/role/handler.go | 22 ++- apps/api/internal/domain/role/model.go | 28 ++-- apps/api/internal/domain/role/service.go | 146 ++++++++++++++---- 4 files changed, 190 insertions(+), 44 deletions(-) diff --git a/apps/api/internal/domain/permission/repository.go b/apps/api/internal/domain/permission/repository.go index d364e83..8dbc8de 100644 --- a/apps/api/internal/domain/permission/repository.go +++ b/apps/api/internal/domain/permission/repository.go @@ -24,6 +24,9 @@ type Repository interface { FindUserPermissionsByUserID(userID string) ([]*UserPermission, error) FindUserPermissionsByUserIDAndServiceID(userID, serviceID string) ([]*UserPermission, error) FindUserPermissionsByRoleID(roleID string) ([]*UserPermission, error) + GetUsersByRoleID(roleID string) ([]string, error) + BulkUpdateUserPermissionsByRole(roleID string, resource *string, addedBits, removedBits uint64) error + BulkDeleteUserPermissionsByRole(roleID string, resource *string) error UpdateUserPermission(userPerm *UserPermission) error DeleteUserPermission(userID, serviceID string, resource *string) error IncrementPermissionVersion(userID string) error @@ -206,6 +209,41 @@ func (r *repository) FindUserPermissionsByRoleID(roleID string) ([]*UserPermissi return userPerms, nil } +// GetUsersByRoleID gets distinct user IDs associated with a specific role +func (r *repository) GetUsersByRoleID(roleID string) ([]string, error) { + var userIDs []string + if err := r.db.Model(&UserPermission{}).Where("role_id = ?", roleID).Distinct("user_id").Pluck("user_id", &userIDs).Error; err != nil { + return nil, err + } + return userIDs, nil +} + +// BulkUpdateUserPermissionsByRole updates bitmasks for all users with a specific role and resource +func (r *repository) BulkUpdateUserPermissionsByRole(roleID string, resource *string, addedBits, removedBits uint64) error { + query := r.db.Model(&UserPermission{}).Where("role_id = ?", roleID) + + if resource == nil { + query = query.Where("resource IS NULL") + } else { + query = query.Where("resource = ?", *resource) + } + + return query.Update("bitmask", gorm.Expr("(bitmask | ?) & ~?", addedBits, removedBits)).Error +} + +// BulkDeleteUserPermissionsByRole deletes user permissions for a specific role and resource +func (r *repository) BulkDeleteUserPermissionsByRole(roleID string, resource *string) error { + query := r.db.Where("role_id = ?", roleID) + + if resource == nil { + query = query.Where("resource IS NULL") + } else { + query = query.Where("resource = ?", *resource) + } + + return query.Delete(&UserPermission{}).Error +} + // UpdateUserPermission updates a user permission func (r *repository) UpdateUserPermission(userPerm *UserPermission) error { return r.db.Save(userPerm).Error diff --git a/apps/api/internal/domain/role/handler.go b/apps/api/internal/domain/role/handler.go index ac5a949..24ea618 100644 --- a/apps/api/internal/domain/role/handler.go +++ b/apps/api/internal/domain/role/handler.go @@ -74,9 +74,16 @@ func (h *Handler) CreateRole(c *fiber.Ctx) error { ServiceID: serviceUUID, Name: req.Name, Description: req.Description, - Bitmask: req.Bitmask, IsDefault: req.IsDefault, Priority: req.Priority, + Permissions: make([]RolePermission, len(req.Permissions)), + } + + for i, p := range req.Permissions { + role.Permissions[i] = RolePermission{ + Resource: p.Resource, + Bitmask: p.Bitmask, + } } if err := h.roleService.CreateRole(role); err != nil { @@ -134,9 +141,6 @@ func (h *Handler) UpdateRole(c *fiber.Ctx) error { if req.Description != nil { role.Description = *req.Description } - if req.Bitmask != nil { - role.Bitmask = *req.Bitmask - } if req.IsDefault != nil { role.IsDefault = *req.IsDefault } @@ -144,6 +148,16 @@ func (h *Handler) UpdateRole(c *fiber.Ctx) error { role.Priority = *req.Priority } + if req.Permissions != nil { + role.Permissions = make([]RolePermission, len(req.Permissions)) + for i, p := range req.Permissions { + role.Permissions[i] = RolePermission{ + Resource: p.Resource, + Bitmask: p.Bitmask, + } + } + } + if err := h.roleService.UpdateRole(role); err != nil { return utils.ErrorResponse(c, utils.NewAPIError("INTERNAL_SERVER_ERROR", err.Error(), fiber.StatusInternalServerError)) } diff --git a/apps/api/internal/domain/role/model.go b/apps/api/internal/domain/role/model.go index 0853828..8635a67 100644 --- a/apps/api/internal/domain/role/model.go +++ b/apps/api/internal/domain/role/model.go @@ -37,23 +37,29 @@ func (Role) TableName() string { return "roles" } +// RolePermissionRequest represents a permission assignment for a resource +type RolePermissionRequest struct { + Resource *string `json:"resource"` + Bitmask uint64 `json:"bitmask"` +} + // CreateRoleRequest represents the input for creating a role type CreateRoleRequest struct { - ServiceID string `json:"service_id" validate:"required,uuid"` - Name string `json:"name" validate:"required,min=3,max=64"` - Description string `json:"description"` - Bitmask uint64 `json:"bitmask"` - IsDefault bool `json:"is_default"` - Priority int `json:"priority"` + ServiceID string `json:"service_id" validate:"required,uuid"` + Name string `json:"name" validate:"required,min=3,max=64"` + Description string `json:"description"` + IsDefault bool `json:"is_default"` + Priority int `json:"priority"` + Permissions []RolePermissionRequest `json:"permissions"` } // UpdateRoleRequest represents the input for updating a role type UpdateRoleRequest struct { - Name *string `json:"name" validate:"omitempty,min=3,max=64"` - Description *string `json:"description"` - Bitmask *uint64 `json:"bitmask"` - IsDefault *bool `json:"is_default"` - Priority *int `json:"priority"` + Name *string `json:"name" validate:"omitempty,min=3,max=64"` + Description *string `json:"description"` + IsDefault *bool `json:"is_default"` + Priority *int `json:"priority"` + Permissions []RolePermissionRequest `json:"permissions"` } // AssignRoleRequest represents the input for assigning a role to a user diff --git a/apps/api/internal/domain/role/service.go b/apps/api/internal/domain/role/service.go index 676c3ba..c59f229 100644 --- a/apps/api/internal/domain/role/service.go +++ b/apps/api/internal/domain/role/service.go @@ -89,7 +89,7 @@ func (s *service) UpdateRole(updatedRole *Role) error { return fmt.Errorf("internal error: failed to cast service") } - oldRole, err := txSvc.repo.FindByID(updatedRole.ID.String()) + oldRole, err := txSvc.repo.FindByID(updatedRole.ID.String()) // FindByID already preloads Permissions if err != nil { return err } @@ -104,17 +104,127 @@ func (s *service) UpdateRole(updatedRole *Role) error { return err } - addedBits := updatedRole.Bitmask &^ oldRole.Bitmask - removedBits := oldRole.Bitmask &^ updatedRole.Bitmask - - if addedBits == 0 && removedBits == 0 { - return nil + // Update permissions associations + if err := tx.Model(updatedRole).Association("Permissions").Replace(updatedRole.Permissions); err != nil { + return err } - return txSvc.propagateRoleChanges(updatedRole.ID.String(), addedBits, removedBits) + return txSvc.propagateRoleChanges(oldRole, updatedRole) }) } +// propagateRoleChanges updates user permissions based on changes in role permissions +func (s *service) propagateRoleChanges(oldRole, newRole *Role) error { + oldMap := make(map[string]uint64) + if oldRole.Bitmask > 0 { + oldMap["__global__"] = oldRole.Bitmask + } + for _, p := range oldRole.Permissions { + key := "__global__" + if p.Resource != nil { + key = *p.Resource + } + oldMap[key] = p.Bitmask + } + + newMap := make(map[string]uint64) + if newRole.Bitmask > 0 { + newMap["__global__"] = newRole.Bitmask + } + for _, p := range newRole.Permissions { + key := "__global__" + if p.Resource != nil { + key = *p.Resource + } + newMap[key] = p.Bitmask + } + + for key, oldMask := range oldMap { + var resourcePtr *string + if key != "__global__" { + k := key + resourcePtr = &k + } + + if newMask, exists := newMap[key]; exists { + addedBits := newMask &^ oldMask + removedBits := oldMask &^ newMask + + if addedBits > 0 || removedBits > 0 { + if err := s.permissionRepo.BulkUpdateUserPermissionsByRole(oldRole.ID.String(), resourcePtr, addedBits, removedBits); err != nil { + return err + } + } + } else { + if err := s.permissionRepo.BulkDeleteUserPermissionsByRole(oldRole.ID.String(), resourcePtr); err != nil { + return err + } + } + } + + var addedResources []string + for key := range newMap { + if _, exists := oldMap[key]; !exists { + addedResources = append(addedResources, key) + } + } + + if len(addedResources) > 0 { + userIDs, err := s.permissionRepo.GetUsersByRoleID(oldRole.ID.String()) + if err != nil { + return err + } + + for _, userID := range userIDs { + uid, err := uuid.Parse(userID) + if err != nil { + continue + } + rid := oldRole.ID // Role ID hasn't changed + + for _, resKey := range addedResources { + mask := newMap[resKey] + var resPtr *string + if resKey != "__global__" { + k := resKey + resPtr = &k + } + + userPerm, err := s.permissionRepo.FindUserPermission(userID, oldRole.ServiceID.String(), resPtr) + if err != nil { + newPerm := &permission.UserPermission{ + UserID: uid, + ServiceID: oldRole.ServiceID, + RoleID: &rid, + Resource: resPtr, + Bitmask: mask, + PermissionV: 1, + } + if err := s.permissionRepo.CreateUserPermission(newPerm); err != nil { + return err + } + } else { + userPerm.RoleID = &rid + userPerm.Bitmask = userPerm.Bitmask | mask + if err := s.permissionRepo.UpdateUserPermission(userPerm); err != nil { + return err + } + } + } + } + } + + userIDs, err := s.permissionRepo.GetUsersByRoleID(oldRole.ID.String()) + if err != nil { + return err + } + for _, uid := range userIDs { + _ = s.permissionRepo.IncrementPermissionVersion(uid) + } + + return nil +} + func (s *service) DeleteRole(id string) error { return s.db.Transaction(func(tx *gorm.DB) error { txSvc := s.WithTx(tx) @@ -305,28 +415,6 @@ func (s *service) AssignDefaultRoles(userID string) error { }) } -func (s *service) propagateRoleChanges(roleID string, addedBits, removedBits uint64) error { - userPerms, err := s.permissionRepo.FindUserPermissionsByRoleID(roleID) - if err != nil { - return err - } - - for _, perm := range userPerms { - // Apply delta - perm.Bitmask = perm.Bitmask | addedBits - perm.Bitmask = perm.Bitmask &^ removedBits - - if err := s.permissionRepo.UpdateUserPermission(perm); err != nil { - return err - } - // Increment version to invalidate tokens - if err := s.permissionRepo.IncrementPermissionVersion(perm.UserID.String()); err != nil { - return err - } - } - return nil -} - func (s *service) unsetOtherDefaults(serviceID, excludeRoleID string) error { roles, err := s.repo.FindByServiceID(serviceID) if err != nil { From 2d035e45f2d617b8e685d0ff3db6dd486a54fb66 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:24:30 +0100 Subject: [PATCH 06/53] feat(role): add JSON tags to BaseModel and Role models for improved API response formatting --- apps/api/internal/database/base.go | 8 ++++---- apps/api/internal/domain/role/model.go | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/api/internal/database/base.go b/apps/api/internal/database/base.go index d38dc19..6bc87dd 100644 --- a/apps/api/internal/database/base.go +++ b/apps/api/internal/database/base.go @@ -8,8 +8,8 @@ import ( ) type BaseModel struct { - ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()"` - CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` - UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"` - DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index"` + ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"` } diff --git a/apps/api/internal/domain/role/model.go b/apps/api/internal/domain/role/model.go index 8635a67..7af3430 100644 --- a/apps/api/internal/domain/role/model.go +++ b/apps/api/internal/domain/role/model.go @@ -8,12 +8,12 @@ import ( // Role represents a role within a service type Role struct { database.BaseModel - ServiceID uuid.UUID `gorm:"column:service_id;type:uuid;not null;index"` - Name string `gorm:"column:name;type:varchar(255);not null"` - Description string `gorm:"column:description;type:text"` - Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0"` // Deprecated for granular resources, used for global - IsDefault bool `gorm:"column:is_default;type:boolean;not null;default:false"` - Priority int `gorm:"column:priority;type:integer;not null;default:0"` + ServiceID uuid.UUID `gorm:"column:service_id;type:uuid;not null;index" json:"service_id"` + Name string `gorm:"column:name;type:varchar(255);not null" json:"name"` + Description string `gorm:"column:description;type:text" json:"description"` + Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0" json:"bitmask"` // Deprecated for granular resources, used for global + IsDefault bool `gorm:"column:is_default;type:boolean;not null;default:false" json:"is_default"` + Priority int `gorm:"column:priority;type:integer;not null;default:0" json:"priority"` // Associations Permissions []RolePermission `gorm:"foreignKey:RoleID" json:"permissions,omitempty"` @@ -22,9 +22,9 @@ type Role struct { // RolePermission represents a bitmask for a specific resource within a role type RolePermission struct { database.BaseModel - RoleID uuid.UUID `gorm:"column:role_id;type:uuid;not null;index"` - Resource *string `gorm:"column:resource;type:varchar(100)"` // NULL = global - Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0"` + RoleID uuid.UUID `gorm:"column:role_id;type:uuid;not null;index" json:"role_id"` + Resource *string `gorm:"column:resource;type:varchar(100)" json:"resource"` // NULL = global + Bitmask uint64 `gorm:"column:bitmask;type:numeric(20);not null;default:0" json:"bitmask"` } // TableName returns the table name for Gorm From 22a671ad4fb27e3962dfbdf817a8e2b04a2cf44a Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:25:33 +0100 Subject: [PATCH 07/53] chore: gen doc --- apps/api/docs/docs.go | 74 ++++++++++++++++--- apps/api/docs/swagger.json | 74 ++++++++++++++++--- apps/api/docs/swagger.yaml | 53 ++++++++++--- apps/web/src/lib/api/generated/index.ts | 2 + .../internalDomainRoleCreateRoleRequest.json | 9 ++- .../schemas/internalDomainRoleRole.json | 36 +++++++-- .../internalDomainRoleRoleDataResponse.json | 36 +++++++-- .../internalDomainRoleRolePermission.json | 20 +++++ ...ternalDomainRoleRolePermissionRequest.json | 5 ++ .../internalDomainRoleRolesDataResponse.json | 42 +++++++++-- .../internalDomainRoleUpdateRoleRequest.json | 9 ++- apps/web/src/lib/api/generated/zod.ts | 40 ++++++++-- apps/web/src/lib/api/schema.d.ts | 31 ++++++-- 13 files changed, 365 insertions(+), 66 deletions(-) create mode 100644 apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermission.json create mode 100644 apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermissionRequest.json diff --git a/apps/api/docs/docs.go b/apps/api/docs/docs.go index 2cbcf3f..5fe18e0 100644 --- a/apps/api/docs/docs.go +++ b/apps/api/docs/docs.go @@ -1925,9 +1925,6 @@ const docTemplate = `{ "service_id" ], "properties": { - "bitmask": { - "type": "integer" - }, "description": { "type": "string" }, @@ -1939,6 +1936,12 @@ const docTemplate = `{ "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" }, @@ -1951,12 +1954,13 @@ const docTemplate = `{ "type": "object", "properties": { "bitmask": { + "description": "Deprecated for granular resources, used for global", "type": "integer" }, - "createdAt": { + "created_at": { "type": "string" }, - "deletedAt": { + "deleted_at": { "$ref": "#/definitions/gorm.DeletedAt" }, "description": { @@ -1965,19 +1969,26 @@ const docTemplate = `{ "id": { "type": "string" }, - "isDefault": { + "is_default": { "type": "boolean" }, "name": { "type": "string" }, + "permissions": { + "description": "Associations", + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermission" + } + }, "priority": { "type": "integer" }, - "serviceID": { + "service_id": { "type": "string" }, - "updatedAt": { + "updated_at": { "type": "string" } } @@ -1990,6 +2001,44 @@ const docTemplate = `{ } } }, + "internal_domain_role.RolePermission": { + "type": "object", + "properties": { + "bitmask": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "$ref": "#/definitions/gorm.DeletedAt" + }, + "id": { + "type": "string" + }, + "resource": { + "description": "NULL = global", + "type": "string" + }, + "role_id": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "internal_domain_role.RolePermissionRequest": { + "type": "object", + "properties": { + "bitmask": { + "type": "integer" + }, + "resource": { + "type": "string" + } + } + }, "internal_domain_role.RolesDataResponse": { "type": "object", "properties": { @@ -2004,9 +2053,6 @@ const docTemplate = `{ "internal_domain_role.UpdateRoleRequest": { "type": "object", "properties": { - "bitmask": { - "type": "integer" - }, "description": { "type": "string" }, @@ -2018,6 +2064,12 @@ const docTemplate = `{ "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" } diff --git a/apps/api/docs/swagger.json b/apps/api/docs/swagger.json index 824ba81..24ad207 100644 --- a/apps/api/docs/swagger.json +++ b/apps/api/docs/swagger.json @@ -1918,9 +1918,6 @@ "service_id" ], "properties": { - "bitmask": { - "type": "integer" - }, "description": { "type": "string" }, @@ -1932,6 +1929,12 @@ "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" }, @@ -1944,12 +1947,13 @@ "type": "object", "properties": { "bitmask": { + "description": "Deprecated for granular resources, used for global", "type": "integer" }, - "createdAt": { + "created_at": { "type": "string" }, - "deletedAt": { + "deleted_at": { "$ref": "#/definitions/gorm.DeletedAt" }, "description": { @@ -1958,19 +1962,26 @@ "id": { "type": "string" }, - "isDefault": { + "is_default": { "type": "boolean" }, "name": { "type": "string" }, + "permissions": { + "description": "Associations", + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermission" + } + }, "priority": { "type": "integer" }, - "serviceID": { + "service_id": { "type": "string" }, - "updatedAt": { + "updated_at": { "type": "string" } } @@ -1983,6 +1994,44 @@ } } }, + "internal_domain_role.RolePermission": { + "type": "object", + "properties": { + "bitmask": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "deleted_at": { + "$ref": "#/definitions/gorm.DeletedAt" + }, + "id": { + "type": "string" + }, + "resource": { + "description": "NULL = global", + "type": "string" + }, + "role_id": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "internal_domain_role.RolePermissionRequest": { + "type": "object", + "properties": { + "bitmask": { + "type": "integer" + }, + "resource": { + "type": "string" + } + } + }, "internal_domain_role.RolesDataResponse": { "type": "object", "properties": { @@ -1997,9 +2046,6 @@ "internal_domain_role.UpdateRoleRequest": { "type": "object", "properties": { - "bitmask": { - "type": "integer" - }, "description": { "type": "string" }, @@ -2011,6 +2057,12 @@ "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" } diff --git a/apps/api/docs/swagger.yaml b/apps/api/docs/swagger.yaml index 20e8e6c..dd983e6 100644 --- a/apps/api/docs/swagger.yaml +++ b/apps/api/docs/swagger.yaml @@ -372,8 +372,6 @@ definitions: type: object internal_domain_role.CreateRoleRequest: properties: - bitmask: - type: integer description: type: string is_default: @@ -382,6 +380,10 @@ definitions: maxLength: 64 minLength: 3 type: string + permissions: + items: + $ref: '#/definitions/internal_domain_role.RolePermissionRequest' + type: array priority: type: integer service_id: @@ -393,24 +395,30 @@ definitions: internal_domain_role.Role: properties: bitmask: + description: Deprecated for granular resources, used for global type: integer - createdAt: + created_at: type: string - deletedAt: + deleted_at: $ref: '#/definitions/gorm.DeletedAt' description: type: string id: type: string - isDefault: + is_default: type: boolean name: type: string + permissions: + description: Associations + items: + $ref: '#/definitions/internal_domain_role.RolePermission' + type: array priority: type: integer - serviceID: + service_id: type: string - updatedAt: + updated_at: type: string type: object internal_domain_role.RoleDataResponse: @@ -418,6 +426,31 @@ definitions: role: $ref: '#/definitions/internal_domain_role.Role' type: object + internal_domain_role.RolePermission: + properties: + bitmask: + type: integer + created_at: + type: string + deleted_at: + $ref: '#/definitions/gorm.DeletedAt' + id: + type: string + resource: + description: NULL = global + type: string + role_id: + type: string + updated_at: + type: string + type: object + internal_domain_role.RolePermissionRequest: + properties: + bitmask: + type: integer + resource: + type: string + type: object internal_domain_role.RolesDataResponse: properties: roles: @@ -427,8 +460,6 @@ definitions: type: object internal_domain_role.UpdateRoleRequest: properties: - bitmask: - type: integer description: type: string is_default: @@ -437,6 +468,10 @@ definitions: maxLength: 64 minLength: 3 type: string + permissions: + items: + $ref: '#/definitions/internal_domain_role.RolePermissionRequest' + type: array priority: type: integer type: object diff --git a/apps/web/src/lib/api/generated/index.ts b/apps/web/src/lib/api/generated/index.ts index ca86839..368a8e0 100644 --- a/apps/web/src/lib/api/generated/index.ts +++ b/apps/web/src/lib/api/generated/index.ts @@ -25,7 +25,9 @@ export { internalDomainPermissionPermissionsDataResponseSchema, internalDomainPermissionUpdatePermissionRequestSchema, internalDomainRoleAssignRoleRequestSchema, + internalDomainRoleRolePermissionRequestSchema, internalDomainRoleCreateRoleRequestSchema, + internalDomainRoleRolePermissionSchema, internalDomainRoleRoleSchema, internalDomainRoleRoleDataResponseSchema, internalDomainRoleRolesDataResponseSchema, diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleCreateRoleRequest.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleCreateRoleRequest.json index c8b2f46..d3a123b 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainRoleCreateRoleRequest.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleCreateRoleRequest.json @@ -2,10 +2,17 @@ "type": "object", "required": ["name", "service_id"], "properties": { - "bitmask": { "type": "integer" }, "description": { "type": "string" }, "is_default": { "type": "boolean" }, "name": { "type": "string", "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "properties": { "bitmask": { "type": "integer" }, "resource": { "type": "string" } }, + "x-readme-ref-name": "internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" }, "service_id": { "type": "string" } }, diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRole.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRole.json index 4849eae..667728a 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRole.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRole.json @@ -1,9 +1,9 @@ { "type": "object", "properties": { - "bitmask": { "type": "integer" }, - "createdAt": { "type": "string" }, - "deletedAt": { + "bitmask": { "description": "Deprecated for granular resources, used for global", "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { "type": "object", "properties": { "time": { "type": "string" }, @@ -13,11 +13,35 @@ }, "description": { "type": "string" }, "id": { "type": "string" }, - "isDefault": { "type": "boolean" }, + "is_default": { "type": "boolean" }, "name": { "type": "string" }, + "permissions": { + "description": "Associations", + "type": "array", + "items": { + "type": "object", + "properties": { + "bitmask": { "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { + "type": "object", + "properties": { + "time": { "type": "string" }, + "valid": { "description": "Valid is true if Time is not NULL", "type": "boolean" } + }, + "x-readme-ref-name": "gorm.DeletedAt" + }, + "id": { "type": "string" }, + "resource": { "description": "NULL = global", "type": "string" }, + "role_id": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "x-readme-ref-name": "internal_domain_role.RolePermission" + } + }, "priority": { "type": "integer" }, - "serviceID": { "type": "string" }, - "updatedAt": { "type": "string" } + "service_id": { "type": "string" }, + "updated_at": { "type": "string" } }, "x-readme-ref-name": "internal_domain_role.Role" } diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRoleDataResponse.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRoleDataResponse.json index 6cfddac..2860c12 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRoleDataResponse.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRoleDataResponse.json @@ -4,9 +4,9 @@ "role": { "type": "object", "properties": { - "bitmask": { "type": "integer" }, - "createdAt": { "type": "string" }, - "deletedAt": { + "bitmask": { "description": "Deprecated for granular resources, used for global", "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { "type": "object", "properties": { "time": { "type": "string" }, @@ -16,11 +16,35 @@ }, "description": { "type": "string" }, "id": { "type": "string" }, - "isDefault": { "type": "boolean" }, + "is_default": { "type": "boolean" }, "name": { "type": "string" }, + "permissions": { + "description": "Associations", + "type": "array", + "items": { + "type": "object", + "properties": { + "bitmask": { "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { + "type": "object", + "properties": { + "time": { "type": "string" }, + "valid": { "description": "Valid is true if Time is not NULL", "type": "boolean" } + }, + "x-readme-ref-name": "gorm.DeletedAt" + }, + "id": { "type": "string" }, + "resource": { "description": "NULL = global", "type": "string" }, + "role_id": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "x-readme-ref-name": "internal_domain_role.RolePermission" + } + }, "priority": { "type": "integer" }, - "serviceID": { "type": "string" }, - "updatedAt": { "type": "string" } + "service_id": { "type": "string" }, + "updated_at": { "type": "string" } }, "x-readme-ref-name": "internal_domain_role.Role" } diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermission.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermission.json new file mode 100644 index 0000000..eee9741 --- /dev/null +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermission.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "properties": { + "bitmask": { "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { + "type": "object", + "properties": { + "time": { "type": "string" }, + "valid": { "description": "Valid is true if Time is not NULL", "type": "boolean" } + }, + "x-readme-ref-name": "gorm.DeletedAt" + }, + "id": { "type": "string" }, + "resource": { "description": "NULL = global", "type": "string" }, + "role_id": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "x-readme-ref-name": "internal_domain_role.RolePermission" +} diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermissionRequest.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermissionRequest.json new file mode 100644 index 0000000..63df197 --- /dev/null +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolePermissionRequest.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "properties": { "bitmask": { "type": "integer" }, "resource": { "type": "string" } }, + "x-readme-ref-name": "internal_domain_role.RolePermissionRequest" +} diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolesDataResponse.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolesDataResponse.json index 12d32ea..6e11b03 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolesDataResponse.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleRolesDataResponse.json @@ -6,9 +6,12 @@ "items": { "type": "object", "properties": { - "bitmask": { "type": "integer" }, - "createdAt": { "type": "string" }, - "deletedAt": { + "bitmask": { + "description": "Deprecated for granular resources, used for global", + "type": "integer" + }, + "created_at": { "type": "string" }, + "deleted_at": { "type": "object", "properties": { "time": { "type": "string" }, @@ -18,11 +21,38 @@ }, "description": { "type": "string" }, "id": { "type": "string" }, - "isDefault": { "type": "boolean" }, + "is_default": { "type": "boolean" }, "name": { "type": "string" }, + "permissions": { + "description": "Associations", + "type": "array", + "items": { + "type": "object", + "properties": { + "bitmask": { "type": "integer" }, + "created_at": { "type": "string" }, + "deleted_at": { + "type": "object", + "properties": { + "time": { "type": "string" }, + "valid": { + "description": "Valid is true if Time is not NULL", + "type": "boolean" + } + }, + "x-readme-ref-name": "gorm.DeletedAt" + }, + "id": { "type": "string" }, + "resource": { "description": "NULL = global", "type": "string" }, + "role_id": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "x-readme-ref-name": "internal_domain_role.RolePermission" + } + }, "priority": { "type": "integer" }, - "serviceID": { "type": "string" }, - "updatedAt": { "type": "string" } + "service_id": { "type": "string" }, + "updated_at": { "type": "string" } }, "x-readme-ref-name": "internal_domain_role.Role" } diff --git a/apps/web/src/lib/api/generated/schemas/internalDomainRoleUpdateRoleRequest.json b/apps/web/src/lib/api/generated/schemas/internalDomainRoleUpdateRoleRequest.json index edf30cc..d65847e 100644 --- a/apps/web/src/lib/api/generated/schemas/internalDomainRoleUpdateRoleRequest.json +++ b/apps/web/src/lib/api/generated/schemas/internalDomainRoleUpdateRoleRequest.json @@ -1,10 +1,17 @@ { "type": "object", "properties": { - "bitmask": { "type": "integer" }, "description": { "type": "string" }, "is_default": { "type": "boolean" }, "name": { "type": "string", "maxLength": 64, "minLength": 3 }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "properties": { "bitmask": { "type": "integer" }, "resource": { "type": "string" } }, + "x-readme-ref-name": "internal_domain_role.RolePermissionRequest" + } + }, "priority": { "type": "integer" } }, "x-readme-ref-name": "internal_domain_role.UpdateRoleRequest" diff --git a/apps/web/src/lib/api/generated/zod.ts b/apps/web/src/lib/api/generated/zod.ts index 47cf924..ae3e344 100644 --- a/apps/web/src/lib/api/generated/zod.ts +++ b/apps/web/src/lib/api/generated/zod.ts @@ -213,28 +213,50 @@ export const internalDomainRoleAssignRoleRequestSchema = z.object({ user_id: z.string(), }); -export const internalDomainRoleCreateRoleRequestSchema = z.object({ +export const internalDomainRoleRolePermissionRequestSchema = z.object({ bitmask: z.optional(z.int()), + resource: z.optional(z.string()), +}); + +export const internalDomainRoleCreateRoleRequestSchema = z.object({ description: z.optional(z.string()), is_default: z.optional(z.boolean()), name: z.string().min(3).max(64), + get permissions() { + return z.array(internalDomainRoleRolePermissionRequestSchema).optional(); + }, priority: z.optional(z.int()), service_id: z.string(), }); -export const internalDomainRoleRoleSchema = z.object({ +export const internalDomainRoleRolePermissionSchema = z.object({ bitmask: z.optional(z.int()), - createdAt: z.optional(z.string()), - get deletedAt() { + created_at: z.optional(z.string()), + get deleted_at() { + return gormDeletedAtSchema.optional(); + }, + id: z.optional(z.string()), + resource: z.optional(z.string().describe("NULL = global")), + role_id: z.optional(z.string()), + updated_at: z.optional(z.string()), +}); + +export const internalDomainRoleRoleSchema = z.object({ + bitmask: z.optional(z.int().describe("Deprecated for granular resources, used for global")), + created_at: z.optional(z.string()), + get deleted_at() { return gormDeletedAtSchema.optional(); }, description: z.optional(z.string()), id: z.optional(z.string()), - isDefault: z.optional(z.boolean()), + is_default: z.optional(z.boolean()), name: z.optional(z.string()), + get permissions() { + return z.array(internalDomainRoleRolePermissionSchema).describe("Associations").optional(); + }, priority: z.optional(z.int()), - serviceID: z.optional(z.string()), - updatedAt: z.optional(z.string()), + service_id: z.optional(z.string()), + updated_at: z.optional(z.string()), }); export const internalDomainRoleRoleDataResponseSchema = z.object({ @@ -250,10 +272,12 @@ export const internalDomainRoleRolesDataResponseSchema = z.object({ }); export const internalDomainRoleUpdateRoleRequestSchema = z.object({ - bitmask: z.optional(z.int()), description: z.optional(z.string()), is_default: z.optional(z.boolean()), name: z.optional(z.string().min(3).max(64)), + get permissions() { + return z.array(internalDomainRoleRolePermissionRequestSchema).optional(); + }, priority: z.optional(z.int()), }); diff --git a/apps/web/src/lib/api/schema.d.ts b/apps/web/src/lib/api/schema.d.ts index dfb11f9..9543beb 100644 --- a/apps/web/src/lib/api/schema.d.ts +++ b/apps/web/src/lib/api/schema.d.ts @@ -1522,36 +1522,53 @@ export interface components { user_id: string; }; "internal_domain_role.CreateRoleRequest": { - bitmask?: number; description?: string; is_default?: boolean; name: string; + permissions?: components["schemas"]["internal_domain_role.RolePermissionRequest"][]; priority?: number; service_id: string; }; "internal_domain_role.Role": { + /** @description Deprecated for granular resources, used for global */ bitmask?: number; - createdAt?: string; - deletedAt?: components["schemas"]["gorm.DeletedAt"]; + created_at?: string; + deleted_at?: components["schemas"]["gorm.DeletedAt"]; description?: string; id?: string; - isDefault?: boolean; + is_default?: boolean; name?: string; + /** @description Associations */ + permissions?: components["schemas"]["internal_domain_role.RolePermission"][]; priority?: number; - serviceID?: string; - updatedAt?: string; + service_id?: string; + updated_at?: string; }; "internal_domain_role.RoleDataResponse": { role?: components["schemas"]["internal_domain_role.Role"]; }; + "internal_domain_role.RolePermission": { + bitmask?: number; + created_at?: string; + deleted_at?: components["schemas"]["gorm.DeletedAt"]; + id?: string; + /** @description NULL = global */ + resource?: string; + role_id?: string; + updated_at?: string; + }; + "internal_domain_role.RolePermissionRequest": { + bitmask?: number; + resource?: string; + }; "internal_domain_role.RolesDataResponse": { roles?: components["schemas"]["internal_domain_role.Role"][]; }; "internal_domain_role.UpdateRoleRequest": { - bitmask?: number; description?: string; is_default?: boolean; name?: string; + permissions?: components["schemas"]["internal_domain_role.RolePermissionRequest"][]; priority?: number; }; "internal_domain_service.CreateServiceRequest": { From 21c20218174680a4e999ce0d269c0210141e76f3 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:52:32 +0100 Subject: [PATCH 08/53] chore: remove unused focus styles and Lucide icon configurations from globals.css --- apps/web/src/app/globals.css | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 925a8cf..8533389 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -54,16 +54,3 @@ html { background-color: rgba(255, 255, 255, 0.2); color: var(--foreground); } - -/* Focus styles for accessibility */ -:focus-visible { - outline: 2px solid rgba(255, 255, 255, 0.5); - outline-offset: 2px; - border-radius: 2px; -} - -/* Sharp/square icons from Lucide */ -.lucide { - stroke-linecap: square; - stroke-linejoin: miter; -} From c30c4e4ca51cde16017e6a98165e4839fd5968ab Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:54:49 +0100 Subject: [PATCH 09/53] refactor(input): simplify Input component by removing unused props and adding support for start and end icons --- apps/web/src/components/ui/Input.tsx | 114 ++++++++++++--------------- 1 file changed, 50 insertions(+), 64 deletions(-) diff --git a/apps/web/src/components/ui/Input.tsx b/apps/web/src/components/ui/Input.tsx index 83c9d77..54efb5c 100644 --- a/apps/web/src/components/ui/Input.tsx +++ b/apps/web/src/components/ui/Input.tsx @@ -1,86 +1,72 @@ +"use client"; + import * as React from "react"; import { cn } from "@/authly/lib/utils"; -export interface InputProps extends Omit, "prefix" | "suffix"> { - label?: string; - description?: string; - helperText?: string; - error?: string; - prefix?: React.ReactNode; - suffix?: React.ReactNode; +export interface InputProps extends React.InputHTMLAttributes { + startIcon?: React.ReactNode; + endIcon?: React.ReactNode; + error?: boolean; } const Input = React.forwardRef( - ({ className, label, description, helperText, error, prefix, suffix, id, disabled, ...props }, ref) => { - const generatedId = React.useId(); - const inputId = id ?? generatedId; - const helperId = helperText ? `${inputId}-helper` : undefined; - const errorId = error ? `${inputId}-error` : undefined; - const describedBy = [helperId, errorId].filter(Boolean).join(" ") || undefined; - - return ( -
- {label && ( - - )} - - {description &&

{description}

} - -
- {prefix && ( - - {prefix} + ({ className, type, startIcon, endIcon, error, disabled, ...props }, ref) => { + const hasIcons = startIcon || endIcon; + + const inputClasses = cn( + "w-full h-9 rounded-md border bg-white/5 px-3 text-sm text-white placeholder:text-white/30", + "transition-all duration-300 ease-out", + "outline-none ring-0 focus:ring-0", + // Border colors + error + ? "border-red-500/50 hover:border-red-500/70 focus:border-red-500 focus:shadow-[0_0_0_3px_rgba(239,68,68,0.15)]" + : "border-white/10 hover:border-white/25 hover:bg-white/[0.06] focus:border-white/40 focus:bg-white/[0.07] focus:shadow-[0_0_0_3px_rgba(255,255,255,0.08)]", + // Disabled state + disabled && "cursor-not-allowed opacity-50 bg-white/2 border-white/5", + className + ); + + if (hasIcons) { + return ( +
+ {startIcon && ( + + {startIcon} )} - - - {suffix && ( - - {suffix} + {endIcon && ( + + {endIcon} )}
+ ); + } - {helperText && ( -

- {helperText} -

- )} - - {error && ( -

- {error} -

- )} -
+ return ( + ); - }, + } ); Input.displayName = "Input"; -export default Input; +export default Input; \ No newline at end of file From 1dce78158e85f3564a3e24db3db994f4f98a81b6 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:57:27 +0100 Subject: [PATCH 10/53] style(button, input): enhance button and input components with improved styles and transitions; update event_type enum formatting in schema --- apps/web/src/components/ui/Button.tsx | 18 ++++++++++-------- apps/web/src/components/ui/Input.tsx | 20 ++++++-------------- apps/web/src/lib/api/schema.d.ts | 8 +++++++- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/web/src/components/ui/Button.tsx b/apps/web/src/components/ui/Button.tsx index ed732e7..4fd21d4 100644 --- a/apps/web/src/components/ui/Button.tsx +++ b/apps/web/src/components/ui/Button.tsx @@ -31,14 +31,15 @@ export type ButtonProps = ButtonAsButton | ButtonAsAnchor; const variantStyles: Record = { primary: - "bg-white text-black hover:bg-white/90 hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] border border-white/20", - secondary: "bg-white/10 text-white backdrop-blur-sm hover:bg-white/20 border border-white/20", + "bg-white text-black border border-white/30 border-t-white/50 border-b-white/10 hover:bg-white/95 hover:border-white/40 hover:border-t-white/60 hover:border-b-white/15 hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] active:scale-[0.98]", + secondary: + "bg-white/10 text-white backdrop-blur-sm border border-white/20 border-t-white/30 border-b-white/10 hover:bg-white/15 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 active:scale-[0.98]", outline: - "border border-white/20 text-white hover:bg-white/5 hover:border-white/40 hover:shadow-[0_0_15px_rgba(255,255,255,0.1)]", - ghost: "text-white/60 hover:text-white hover:bg-white/5 border border-transparent", - danger: "bg-red-500 text-white hover:bg-red-600 hover:shadow-[0_0_20px_rgba(239,68,68,0.3)] border border-red-500/20", + "border border-white/20 border-t-white/30 border-b-white/10 text-white hover:bg-white/5 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 hover:shadow-[0_0_15px_rgba(255,255,255,0.1)] active:scale-[0.98]", + ghost: "text-white/60 hover:text-white hover:bg-white/5 border border-transparent active:scale-[0.98]", + danger: "bg-red-500 text-white border border-red-500/30 border-t-red-400/50 border-b-red-600/30 hover:bg-red-600 hover:border-red-500/40 hover:border-t-red-400/60 hover:border-b-red-700/40 hover:shadow-[0_0_20px_rgba(239,68,68,0.3)] active:scale-[0.98]", success: - "bg-emerald-500 text-white hover:bg-emerald-600 hover:shadow-[0_0_20px_rgba(16,185,129,0.3)] border border-emerald-500/20", + "bg-emerald-500 text-white border border-emerald-500/30 border-t-emerald-400/50 border-b-emerald-600/30 hover:bg-emerald-600 hover:border-emerald-500/40 hover:border-t-emerald-400/60 hover:border-b-emerald-700/40 hover:shadow-[0_0_20px_rgba(16,185,129,0.3)] active:scale-[0.98]", }; const sizeStyles: Record = { @@ -77,8 +78,9 @@ export default function Button({ "font-medium tracking-widest uppercase", "cursor-pointer", "transition-all duration-300 ease-out", + "transform-gpu", "focus:outline-none focus:ring-2 focus:ring-white/50 focus:ring-offset-2 focus:ring-offset-black", - "disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-current", + "disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-current disabled:active:scale-100", "relative overflow-hidden group rounded-md", variantStyles[variant], sizeStyles[size], @@ -98,7 +100,7 @@ export default function Button({ {children} {icon && iconPosition === "right" && {icon}} - + ); diff --git a/apps/web/src/components/ui/Input.tsx b/apps/web/src/components/ui/Input.tsx index 54efb5c..c2a298f 100644 --- a/apps/web/src/components/ui/Input.tsx +++ b/apps/web/src/components/ui/Input.tsx @@ -12,7 +12,7 @@ export interface InputProps extends React.InputHTMLAttributes const Input = React.forwardRef( ({ className, type, startIcon, endIcon, error, disabled, ...props }, ref) => { const hasIcons = startIcon || endIcon; - + const inputClasses = cn( "w-full h-9 rounded-md border bg-white/5 px-3 text-sm text-white placeholder:text-white/30", "transition-all duration-300 ease-out", @@ -23,9 +23,9 @@ const Input = React.forwardRef( : "border-white/10 hover:border-white/25 hover:bg-white/[0.06] focus:border-white/40 focus:bg-white/[0.07] focus:shadow-[0_0_0_3px_rgba(255,255,255,0.08)]", // Disabled state disabled && "cursor-not-allowed opacity-50 bg-white/2 border-white/5", - className + className, ); - + if (hasIcons) { return (
@@ -56,17 +56,9 @@ const Input = React.forwardRef( ); } - return ( - - ); - } + return ; + }, ); Input.displayName = "Input"; -export default Input; \ No newline at end of file +export default Input; diff --git a/apps/web/src/lib/api/schema.d.ts b/apps/web/src/lib/api/schema.d.ts index 9543beb..9685aca 100644 --- a/apps/web/src/lib/api/schema.d.ts +++ b/apps/web/src/lib/api/schema.d.ts @@ -1407,7 +1407,13 @@ export interface components { created_at: string; details?: Record; /** @enum {string} */ - event_type: "LOGIN_SUCCESS" | "LOGIN_FAILURE" | "LOGOUT" | "REGISTER" | "TOKEN_REFRESH" | "AUTHORIZATION_CODE_GRANTED"; + event_type: + | "LOGIN_SUCCESS" + | "LOGIN_FAILURE" + | "LOGOUT" + | "REGISTER" + | "TOKEN_REFRESH" + | "AUTHORIZATION_CODE_GRANTED"; id: string; ip_address: string; username: string; From 17c15ee8163c763484cd8117cd02963bc8f20d9d Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:02:07 +0100 Subject: [PATCH 11/53] style(button): enhance button styles with disabled states and improved hover effects --- apps/web/src/components/ui/Button.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/ui/Button.tsx b/apps/web/src/components/ui/Button.tsx index 4fd21d4..ddd30ed 100644 --- a/apps/web/src/components/ui/Button.tsx +++ b/apps/web/src/components/ui/Button.tsx @@ -31,15 +31,15 @@ export type ButtonProps = ButtonAsButton | ButtonAsAnchor; const variantStyles: Record = { primary: - "bg-white text-black border border-white/30 border-t-white/50 border-b-white/10 hover:bg-white/95 hover:border-white/40 hover:border-t-white/60 hover:border-b-white/15 hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] active:scale-[0.98]", + "bg-white text-black border border-white/30 border-t-white/50 border-b-white/10 hover:bg-white/95 hover:border-white/40 hover:border-t-white/60 hover:border-b-white/15 hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] active:scale-[0.98] disabled:bg-white/30 disabled:text-white/60 disabled:border-white/15 disabled:border-t-white/20 disabled:border-b-white/10", secondary: - "bg-white/10 text-white backdrop-blur-sm border border-white/20 border-t-white/30 border-b-white/10 hover:bg-white/15 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 active:scale-[0.98]", + "bg-white/10 text-white backdrop-blur-sm border border-white/20 border-t-white/30 border-b-white/10 hover:bg-white/15 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 active:scale-[0.98] disabled:bg-white/8 disabled:text-white/50 disabled:border-white/12 disabled:border-t-white/18 disabled:border-b-white/8", outline: - "border border-white/20 border-t-white/30 border-b-white/10 text-white hover:bg-white/5 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 hover:shadow-[0_0_15px_rgba(255,255,255,0.1)] active:scale-[0.98]", - ghost: "text-white/60 hover:text-white hover:bg-white/5 border border-transparent active:scale-[0.98]", - danger: "bg-red-500 text-white border border-red-500/30 border-t-red-400/50 border-b-red-600/30 hover:bg-red-600 hover:border-red-500/40 hover:border-t-red-400/60 hover:border-b-red-700/40 hover:shadow-[0_0_20px_rgba(239,68,68,0.3)] active:scale-[0.98]", + "border border-white/20 border-t-white/30 border-b-white/10 text-white hover:bg-white/5 hover:border-white/30 hover:border-t-white/40 hover:border-b-white/15 hover:shadow-[0_0_15px_rgba(255,255,255,0.1)] active:scale-[0.98] disabled:text-white/50 disabled:border-white/12 disabled:border-t-white/18 disabled:border-b-white/8", + ghost: "text-white/60 hover:text-white hover:bg-white/5 border border-transparent active:scale-[0.98] disabled:text-white/30", + danger: "bg-red-500 text-white border border-red-500/30 border-t-red-400/50 border-b-red-600/30 hover:bg-red-600 hover:border-red-500/40 hover:border-t-red-400/60 hover:border-b-red-700/40 hover:shadow-[0_0_20px_rgba(239,68,68,0.3)] active:scale-[0.98] disabled:bg-red-500/30 disabled:text-red-300/70 disabled:border-red-500/15 disabled:border-t-red-500/20 disabled:border-b-red-500/10", success: - "bg-emerald-500 text-white border border-emerald-500/30 border-t-emerald-400/50 border-b-emerald-600/30 hover:bg-emerald-600 hover:border-emerald-500/40 hover:border-t-emerald-400/60 hover:border-b-emerald-700/40 hover:shadow-[0_0_20px_rgba(16,185,129,0.3)] active:scale-[0.98]", + "bg-emerald-500 text-white border border-emerald-500/30 border-t-emerald-400/50 border-b-emerald-600/30 hover:bg-emerald-600 hover:border-emerald-500/40 hover:border-t-emerald-400/60 hover:border-b-emerald-700/40 hover:shadow-[0_0_20px_rgba(16,185,129,0.3)] active:scale-[0.98] disabled:bg-emerald-500/30 disabled:text-emerald-300/70 disabled:border-emerald-500/15 disabled:border-t-emerald-500/20 disabled:border-b-emerald-500/10", }; const sizeStyles: Record = { @@ -80,7 +80,7 @@ export default function Button({ "transition-all duration-300 ease-out", "transform-gpu", "focus:outline-none focus:ring-2 focus:ring-white/50 focus:ring-offset-2 focus:ring-offset-black", - "disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-current disabled:active:scale-100", + "disabled:cursor-not-allowed disabled:hover:bg-inherit disabled:hover:border-inherit disabled:hover:shadow-none disabled:active:scale-100", "relative overflow-hidden group rounded-md", variantStyles[variant], sizeStyles[size], @@ -100,7 +100,7 @@ export default function Button({ {children} {icon && iconPosition === "right" && {icon}} - + ); From 7d18e55e97a1b56248e0cef9cbd9c8ce63f48e0f Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:21:46 +0100 Subject: [PATCH 12/53] chore: remove DeleteServiceModal, EditServiceModal, and ServiceSecretModal components as part of code cleanup --- .../admin/services/DeleteServiceModal.tsx | 74 ------- .../admin/services/EditServiceModal.tsx | 199 ------------------ .../admin/services/ServiceSecretModal.tsx | 111 ---------- 3 files changed, 384 deletions(-) delete mode 100644 apps/web/src/components/dashboard/admin/services/DeleteServiceModal.tsx delete mode 100644 apps/web/src/components/dashboard/admin/services/EditServiceModal.tsx delete mode 100644 apps/web/src/components/dashboard/admin/services/ServiceSecretModal.tsx diff --git a/apps/web/src/components/dashboard/admin/services/DeleteServiceModal.tsx b/apps/web/src/components/dashboard/admin/services/DeleteServiceModal.tsx deleted file mode 100644 index 548905d..0000000 --- a/apps/web/src/components/dashboard/admin/services/DeleteServiceModal.tsx +++ /dev/null @@ -1,74 +0,0 @@ -"use client"; - -import { HugeiconsIcon } from "@hugeicons/react"; -import { AlertCircleIcon } from "@hugeicons/core-free-icons"; -import Button from "@/authly/components/ui/Button"; -import { ModalContent, ModalFooter, ModalHeader, ModalTitle, ModalDescription } from "@/authly/components/ui/Modal"; -import { Service } from "@/authly/lib/schemas/admin/services"; -import { useDeleteService } from "@/authly/lib/hooks/admin/useServices"; - -interface DeleteServiceModalProps { - service: Service; - onClose: () => void; -} - -/** - * Render modal content that confirms and performs deletion of a service. - * - * Displays a red-styled confirmation UI including the service name, an optional error banner when deletion fails, - * and footer controls to cancel or permanently delete the service. While deletion is in progress, controls are disabled - * or show a loading state, and the modal closes when deletion succeeds. - * - * @param service - The service to be deleted; its `name` is shown in the confirmation text and its `id` is used for deletion. - * @param onClose - Callback invoked to close the modal (called after a successful deletion or when the user cancels). - * @returns A JSX element containing the modal content for confirming and executing the deletion of the specified service. - */ -export function DeleteServiceModalContent({ service, onClose }: DeleteServiceModalProps) { - const { mutate: deleteService, isPending: isDeleting, error: deleteError } = useDeleteService(); - - const handleDelete = () => { - if (!service.id) return; - - deleteService( - { - params: { - path: { id: service.id }, - }, - }, - { - onSuccess: () => onClose(), - }, - ); - }; - - return ( - - -
-
- -
- Delete Service -
- - Are you sure you want to delete {service.name || "this service"}? This action - cannot be undone. All associated roles and permissions will be permanently removed. - -
- {deleteError && ( -
- - {deleteError.message || "Failed to delete service"} -
- )} - - - - {" "} -
- ); -} diff --git a/apps/web/src/components/dashboard/admin/services/EditServiceModal.tsx b/apps/web/src/components/dashboard/admin/services/EditServiceModal.tsx deleted file mode 100644 index 1eda8b2..0000000 --- a/apps/web/src/components/dashboard/admin/services/EditServiceModal.tsx +++ /dev/null @@ -1,199 +0,0 @@ -"use client"; - -import { useUpdateService } from "@/authly/lib/hooks/admin/useServices"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { AlertCircleIcon } from "@hugeicons/core-free-icons"; -import Button from "@/authly/components/ui/Button"; -import Input from "@/authly/components/ui/Input"; -import { ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle } from "@/authly/components/ui/Modal"; -import { useState } from "react"; -import { Service, updateServiceRequestSchema } from "@/authly/lib/schemas/admin/services"; - -interface EditServiceModalProps { - service: Service; - onClose: () => void; -} - -/** - * Parses a comma-separated string into a trimmed list of non-empty strings. - */ -const parseCommaSeparatedList = (value: string): string[] => { - return value - .split(",") - .map((item) => item.trim()) - .filter((item) => item !== ""); -}; - -/** - * Component for the Service Editing Form modal content. - */ -export function EditServiceModalContent({ service, onClose }: EditServiceModalProps) { - const { mutate: updateService, isPending, error: updateError } = useUpdateService(); - const [formData, setFormData] = useState({ - name: service.name || "", - description: service.description || "", - domain: service.domain || "", - redirectUris: (service.redirect_uris || []).join(", "), - allowedScopes: (service.allowed_scopes || []).join(", "), - active: !!service.active, - }); - const [errors, setErrors] = useState>({}); - - const handleSubmit = () => { - setErrors({}); - - const redirectUris = parseCommaSeparatedList(formData.redirectUris); - const allowedScopes = parseCommaSeparatedList(formData.allowedScopes); - - const validationResult = updateServiceRequestSchema.safeParse({ - name: formData.name, - description: formData.description, - domain: formData.domain, - active: formData.active, - redirect_uris: redirectUris, - allowed_scopes: allowedScopes, - }); - - if (!validationResult.success) { - const fieldErrors: Record = {}; - validationResult.error.issues.forEach((issue) => { - if (issue.path.length === 0) return; - - const path = String(issue.path[0]); - if (path === "redirect_uris") { - fieldErrors.redirectUris = issue.message; - } else if (path === "allowed_scopes") { - fieldErrors.allowedScopes = issue.message; - } else { - fieldErrors[path] = issue.message; - } - }); - setErrors(fieldErrors); - return; - } - - if (!service.id) { - setErrors({ root: "Service ID is missing" }); - return; - } - - updateService( - { - params: { - path: { id: service.id }, - }, - body: validationResult.data, - }, - { - onSuccess: () => { - onClose(); - }, - }, - ); - }; - - return ( - - - Edit Service: {service.name} - Update application details and OIDC configuration. - -
- {(updateError || errors.root) && ( -
- - {errors.root || updateError?.message || "Failed to update service"} -
- )} - -
-
-

- General Information -

- { - setFormData((prev) => ({ ...prev, name: e.target.value })); - if (errors.name) setErrors((prev) => ({ ...prev, name: "" })); - }} - error={errors.name} - required - /> - { - setFormData((prev) => ({ ...prev, description: e.target.value })); - if (errors.description) setErrors((prev) => ({ ...prev, description: "" })); - }} - error={errors.description} - /> - { - setFormData((prev) => ({ ...prev, domain: e.target.value })); - if (errors.domain) setErrors((prev) => ({ ...prev, domain: "" })); - }} - error={errors.domain} - helperText="Verified domain for this service" - /> -
- -
-

- OIDC Configuration -

- { - setFormData((prev) => ({ ...prev, redirectUris: e.target.value })); - if (errors.redirectUris) setErrors((prev) => ({ ...prev, redirectUris: "" })); - }} - error={errors.redirectUris} - helperText="Comma separated list of valid callback URLs" - /> - { - setFormData((prev) => ({ ...prev, allowedScopes: e.target.value })); - if (errors.allowedScopes) setErrors((prev) => ({ ...prev, allowedScopes: "" })); - }} - error={errors.allowedScopes} - helperText="Comma separated list of OAuth2 scopes" - /> -
- setFormData((prev) => ({ ...prev, active: e.target.checked }))} - className="w-4 h-4 rounded border-white/10 bg-white/5 text-primary focus:ring-primary/20" - /> - -
-
-
-
- - - - -
- ); -} diff --git a/apps/web/src/components/dashboard/admin/services/ServiceSecretModal.tsx b/apps/web/src/components/dashboard/admin/services/ServiceSecretModal.tsx deleted file mode 100644 index e419d6e..0000000 --- a/apps/web/src/components/dashboard/admin/services/ServiceSecretModal.tsx +++ /dev/null @@ -1,111 +0,0 @@ -"use client"; - -import { HugeiconsIcon } from "@hugeicons/react"; -import { Copy01Icon, Tick01Icon, ViewIcon, ViewOffIcon, AlertCircleIcon } from "@hugeicons/core-free-icons"; -import Button from "@/authly/components/ui/Button"; -import { ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle } from "@/authly/components/ui/Modal"; -import { useState } from "react"; -import { Service } from "@/authly/lib/schemas/admin/services"; -import { cn } from "@/authly/lib/utils"; -import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; - -interface ServiceSecretModalProps { - service: Service; - onClose: () => void; -} - -/** - * Renders a modal that displays a service's client credentials with controls to reveal and copy the client secret. - * - * The modal shows the service's client ID and client secret, provides a toggle to show or hide the secret, a copy - * control that temporarily indicates success, and a warning about secret handling. Clicking the Done button invokes - * `onClose`. - * - * @param service - The service whose credentials are displayed. - * @param onClose - Callback invoked when the modal should be closed (e.g., when the Done button is clicked). - * @returns The modal content element for the service credentials. - */ -export function ServiceSecretModalContent({ service, onClose }: ServiceSecretModalProps) { - const [isVisible, setIsVisible] = useState(false); - const [copied, copy] = useCopyToClipboard(); - - const handleCopy = () => { - if (!service.client_secret) return; - copy(service.client_secret); - }; - - return ( - - - Service Credentials - - Client credentials for {service.name || "Unnamed Service"}. Keep the secret safe! - - -
-
- -
- - {service.client_id || "No Client ID"} - -
-
- -
- -
-
- - {service.client_secret || "No secret found"} - -
-
- - -
-
-
- -
- -

- The client secret is used to authenticate this service. Never share it in public repositories or - client-side code. -

-
-
- - - -
- ); -} From 50dc32638ddbb9a7e38c79742171312383ca2713 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:22:01 +0100 Subject: [PATCH 13/53] refactor(ServiceRow): simplify component by removing modals and integrating a direct link for service management --- .../dashboard/admin/services/ServiceRow.tsx | 96 ++++--------------- 1 file changed, 18 insertions(+), 78 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx index d40a2c2..50fbda0 100644 --- a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx +++ b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx @@ -1,33 +1,12 @@ "use client"; import { HugeiconsIcon } from "@hugeicons/react"; -import { - Settings01Icon, - Delete02Icon, - Copy01Icon, - CpuIcon, - Layout01Icon, - Tick01Icon, - MoreHorizontalIcon, - Key01Icon, - UserGroupIcon, -} from "@hugeicons/core-free-icons"; +import { Copy01Icon, CpuIcon, Layout01Icon, Tick01Icon, ViewIcon } from "@hugeicons/core-free-icons"; import { TableCell, TableRow } from "@/authly/components/ui/Table"; import { cn } from "@/authly/lib/utils"; -import { useState } from "react"; import { Service } from "@/authly/lib/schemas/admin/services"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/authly/components/ui/Dropdown"; -import { Modal } from "@/authly/components/ui/Modal"; -import { EditServiceModalContent } from "./EditServiceModal"; -import { ServiceSecretModalContent } from "./ServiceSecretModal"; -import { DeleteServiceModalContent } from "./DeleteServiceModal"; +import Button from "@/authly/components/ui/Button"; +import Link from "next/link"; import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; interface ServiceRowProps { @@ -36,14 +15,13 @@ interface ServiceRowProps { /** * Render a table row for a service including name/description, a copyable client ID with feedback, - * status and type indicators, an actions menu, and per-row modals for edit, credentials, and delete. + * status and type indicators, and a view button to manage the service. * * @param service - The service object used to populate the row (name, description, client_id, active, is_system). - * @returns A JSX element rendering the table row and its related modals for the provided service. + * @returns A JSX element rendering the table row for the provided service. */ export function ServiceRow({ service }: ServiceRowProps) { const [copied, copy] = useCopyToClipboard(); - const [activeModal, setActiveModal] = useState<"edit" | "secret" | "delete" | null>(null); const handleCopy = () => { copy(service.client_id || ""); @@ -53,14 +31,14 @@ export function ServiceRow({ service }: ServiceRowProps) { <> -
-

+ +

{service.name || "Unnamed Service"}

{service.description || "No description"}

-
+
- - - Actions - - setActiveModal("edit")}> - - Edit Configuration - - setActiveModal("secret")}> - - View Credentials - - - - Manage Roles - - {!service.is_system && ( - <> - - setActiveModal("delete")} - > - - Delete Service - - - )} - - +
- - {/* Modals */} - !open && setActiveModal(null)}> - setActiveModal(null)} /> - - - !open && setActiveModal(null)}> - setActiveModal(null)} /> - - - !open && setActiveModal(null)}> - setActiveModal(null)} /> - ); } From 61b6885a2b0ed9bf64480fcb223f3094e563eff7 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:25:59 +0100 Subject: [PATCH 14/53] feat(FormField): add new FormField component for structured form layout with label, error, and hint support --- apps/web/src/components/ui/FormField.tsx | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 apps/web/src/components/ui/FormField.tsx diff --git a/apps/web/src/components/ui/FormField.tsx b/apps/web/src/components/ui/FormField.tsx new file mode 100644 index 0000000..94d20e9 --- /dev/null +++ b/apps/web/src/components/ui/FormField.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { cn } from "@/authly/lib/utils"; + +interface FormFieldProps { + label?: string; + error?: string; + hint?: string; + children: React.ReactNode; + className?: string; +} + +export function FormField({ label, error, hint, children, className }: FormFieldProps) { + return ( +
+ {label && ( + + )} + {children} + {hint && !error &&

{hint}

} + {error && ( +

+ {error} +

+ )} +
+ ); +} From f2d46aae1499efbf6f5ce00c5c5ae2cdc6b26a25 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:32:16 +0100 Subject: [PATCH 15/53] feat(Switch): introduce a new Switch component with customizable checked state and accessibility features --- apps/web/src/components/ui/Switch.tsx | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 apps/web/src/components/ui/Switch.tsx diff --git a/apps/web/src/components/ui/Switch.tsx b/apps/web/src/components/ui/Switch.tsx new file mode 100644 index 0000000..009ae48 --- /dev/null +++ b/apps/web/src/components/ui/Switch.tsx @@ -0,0 +1,43 @@ +"use client"; + +import * as React from "react"; +import { cn } from "@/authly/lib/utils"; + +export interface SwitchProps extends Omit, "onChange"> { + checked: boolean; + onCheckedChange: (checked: boolean) => void; + disabled?: boolean; +} + +const Switch = React.forwardRef( + ({ className, checked, onCheckedChange, disabled, ...props }, ref) => { + return ( + + ); + } +); +Switch.displayName = "Switch"; + +export default Switch; From 8fa104c1d88f68362d81bd16177e7e1417086ef2 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:32:32 +0100 Subject: [PATCH 16/53] feat: add @radix-ui/react-tabs and sonner dependencies; integrate Toaster component in layout for notifications --- apps/web/bun.lock | 6 ++++++ apps/web/package.json | 2 ++ apps/web/src/app/layout.tsx | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/web/bun.lock b/apps/web/bun.lock index 43f8550..af6dd65 100644 --- a/apps/web/bun.lock +++ b/apps/web/bun.lock @@ -9,6 +9,7 @@ "@hugeicons/react": "^1.1.4", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-tabs": "^1.1.13", "@tanstack/react-query": "^5.90.12", "@tanstack/react-query-devtools": "^5.91.1", "axios": "^1.13.2", @@ -22,6 +23,7 @@ "react-hook-form": "^7.70.0", "react-syntax-highlighter": "^16.1.0", "recharts": "^3.6.0", + "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", "zod": "^4.1.13", }, @@ -293,6 +295,8 @@ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], @@ -1317,6 +1321,8 @@ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], diff --git a/apps/web/package.json b/apps/web/package.json index fb2e65d..a99150c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,6 +17,7 @@ "@hugeicons/react": "^1.1.4", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-tabs": "^1.1.13", "@tanstack/react-query": "^5.90.12", "@tanstack/react-query-devtools": "^5.91.1", "axios": "^1.13.2", @@ -30,6 +31,7 @@ "react-hook-form": "^7.70.0", "react-syntax-highlighter": "^16.1.0", "recharts": "^3.6.0", + "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", "zod": "^4.1.13" }, diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index bceb28a..bc01254 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata, Viewport } from "next"; import { Sora, JetBrains_Mono } from "next/font/google"; +import { Toaster } from "sonner"; import "./globals.css"; import QueryProvider from "@/authly/components/providers/QueryProvider"; import AuthProvider from "@/authly/components/providers/AuthProvider"; @@ -48,7 +49,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - {children} + + {children} + + From ff67a334387d6399e2de7fbb1865d3290a84e331 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:33:33 +0100 Subject: [PATCH 17/53] refactor(api): rename and enhance error handling functions to improve API error parsing and form integration --- apps/web/src/lib/api/utils.ts | 59 ++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/apps/web/src/lib/api/utils.ts b/apps/web/src/lib/api/utils.ts index 2ce6c82..dc37bbc 100644 --- a/apps/web/src/lib/api/utils.ts +++ b/apps/web/src/lib/api/utils.ts @@ -1,18 +1,23 @@ import { UseFormSetError, Path } from "react-hook-form"; /** - * Handles API errors by mapping backend validation details to form fields. - * If no specific fields are matched, returns a general error message. + * Parses API error structure and extracts error details. * * @param error - The error object returned from the API client (openapi-fetch error) - * @param setError - The setError function from react-hook-form - * @returns A string containing the general error message (if any), or null if all errors were handled as field errors. + * @returns Object containing error message and details map */ -export const handleApiErrors = >( +export const parseApiError = ( error: unknown, - setError: UseFormSetError, -): string | null => { - if (!error) return "An unexpected error occurred."; +): { + message: string; + details: Record; +} => { + if (!error) { + return { + message: "An unexpected error occurred.", + details: {}, + }; + } const errorData = error as { error?: { message?: string; details?: Record }; @@ -21,20 +26,38 @@ export const handleApiErrors = >( }; const errorBody = errorData?.error || errorData; - if (errorBody?.details && typeof errorBody.details === "object") { - let hasFieldErrors = false; - Object.entries(errorBody.details).forEach(([key, message]) => { + return { + message: errorBody?.message || "An unexpected error occurred.", + details: (errorBody?.details && typeof errorBody.details === "object" ? errorBody.details : {}) as Record< + string, + string + >, + }; +}; + +/** + * Handles API errors by mapping backend validation details to form fields. + * If no specific fields are matched, returns a general error message. + * + * @param error - The error object returned from the API client (openapi-fetch error) + * @param setError - The setError function from react-hook-form + * @returns A string containing the general error message (if any), or null if all errors were handled as field errors. + */ +export const handleApiErrors = >( + error: unknown, + setError: UseFormSetError, +): string | null => { + const { message, details } = parseApiError(error); + + if (Object.keys(details).length > 0) { + Object.entries(details).forEach(([key, msg]) => { setError(key as Path, { type: "server", - message: message as string, + message: msg, }); - hasFieldErrors = true; }); - - if (hasFieldErrors) { - return null; - } + return null; } - return errorBody?.message || "An unexpected error occurred."; + return message; }; From 7f24c04d78ad09da3b88572ebdf403eac964a071 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:33:39 +0100 Subject: [PATCH 18/53] chore: format --- apps/web/src/components/ui/Switch.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/ui/Switch.tsx b/apps/web/src/components/ui/Switch.tsx index 009ae48..b637465 100644 --- a/apps/web/src/components/ui/Switch.tsx +++ b/apps/web/src/components/ui/Switch.tsx @@ -23,7 +23,7 @@ const Switch = React.forwardRef( "focus:outline-none focus:ring-2 focus:ring-white/20 focus:ring-offset-2 focus:ring-offset-black", checked ? "bg-emerald-500 border-emerald-600" : "bg-white/10 border-white/20", disabled && "opacity-50 cursor-not-allowed", - className + className, )} ref={ref} {...props} @@ -31,12 +31,12 @@ const Switch = React.forwardRef(
); - } + }, ); Switch.displayName = "Switch"; From 448b6d2b6a0a0c4753f60abb98a3a12ac60b4911 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:57:33 +0100 Subject: [PATCH 19/53] feat(CreateServiceModal): integrate FormField component for improved form structure and validation --- .../admin/services/CreateServiceModal.tsx | 141 +++++++++--------- 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx b/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx index 8516f36..d6e2dbb 100644 --- a/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx +++ b/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx @@ -5,6 +5,7 @@ import { HugeiconsIcon } from "@hugeicons/react"; import { AlertCircleIcon } from "@hugeicons/core-free-icons"; import Button from "@/authly/components/ui/Button"; import Input from "@/authly/components/ui/Input"; +import { FormField } from "@/authly/components/ui/FormField"; import { ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle } from "@/authly/components/ui/Modal"; import { useState } from "react"; import { createServiceRequestSchema } from "@/authly/lib/schemas/admin/services"; @@ -132,81 +133,87 @@ export function CreateServiceModalContent({ onClose }: { onClose: () => void })
)}
+ + + + + + +
+ + setFormData((prev) => ({ ...prev, description: e.target.value }))} + /> + + { + setFormData((prev) => ({ ...prev, domain: e.target.value })); + if (errors.domain) + setErrors((prev) => { + const newErrors = { ...prev }; + delete newErrors.domain; + return newErrors; + }); + }} + error={!!errors.domain} required /> + + { + setFormData((prev) => ({ ...prev, redirectUri: e.target.value })); + if (errors.redirectUri) + setErrors((prev) => { + const newErrors = { ...prev }; + delete newErrors.redirectUri; + return newErrors; + }); + }} + error={!!errors.redirectUri} required /> -
- setFormData((prev) => ({ ...prev, description: e.target.value }))} - /> - { - setFormData((prev) => ({ ...prev, domain: e.target.value })); - if (errors.domain) - setErrors((prev) => { - const newErrors = { ...prev }; - delete newErrors.domain; - return newErrors; - }); - }} - error={errors.domain} - helperText="Verified domain for this service" - required - /> - { - setFormData((prev) => ({ ...prev, redirectUri: e.target.value })); - if (errors.redirectUri) - setErrors((prev) => { - const newErrors = { ...prev }; - delete newErrors.redirectUri; - return newErrors; - }); - }} - error={errors.redirectUri} - helperText="Comma separated for multiple URIs" - required - /> - + { - setFormData((prev) => ({ ...prev, allowedScopes: e.target.value })); - if (errors.allowedScopes) - setErrors((prev) => { - const newErrors = { ...prev }; - delete newErrors.allowedScopes; - return newErrors; - }); - }} error={errors.allowedScopes} - helperText="Comma separated list of OAuth2 scopes" - required - /> + hint="Comma separated list of OAuth2 scopes" + > + { + setFormData((prev) => ({ ...prev, allowedScopes: e.target.value })); + if (errors.allowedScopes) + setErrors((prev) => { + const newErrors = { ...prev }; + delete newErrors.allowedScopes; + return newErrors; + }); + }} + error={!!errors.allowedScopes} + required + /> +
+ + + ); + } + + return ( +
+
+ + + Back to Services + +
+
+
+

{service.name}

+ {service.is_system && ( + + SYSTEM + + )} + + {service.active ? "ACTIVE" : "DISABLED"} + +
+

{service.client_id}

+
+
+
+ + + + + + Overview + + + + Permissions + + + + Roles + + + +
+ + + + {/* TODO: Add Permissions Tab */} + {/* TODO: Add Roles Tab */} +
+
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx b/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx new file mode 100644 index 0000000..5ce6d88 --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { useEffect } from "react"; +import { useAdminService, useUpdateService } from "@/authly/lib/hooks/admin/useServices"; +import { toast } from "sonner"; +import Loader from "@/authly/components/ui/Loader"; +import { ServiceForm } from "./overview/ServiceForm"; + +interface OverviewTabProps { + serviceId: string; +} + +export function OverviewTab({ serviceId }: OverviewTabProps) { + const { data: response, isLoading, isError } = useAdminService(serviceId); + const { mutate: updateService, isPending: isUpdating } = useUpdateService(); + const service = response?.data?.service; + + useEffect(() => { + if (isError) { + toast.error("Failed to load service. Please try again."); + } + }, [isError]); + + if (isLoading) + return ( +
+ +
+ ); + if (isError || !service) return
Failed to load service
; + + return ( + + ); +} diff --git a/apps/web/src/components/dashboard/admin/services/ServicesToolbar.tsx b/apps/web/src/components/dashboard/admin/services/ServicesToolbar.tsx index d8a258f..e7fdf52 100644 --- a/apps/web/src/components/dashboard/admin/services/ServicesToolbar.tsx +++ b/apps/web/src/components/dashboard/admin/services/ServicesToolbar.tsx @@ -50,7 +50,7 @@ export function ServicesToolbar({
} + startIcon={} className="bg-transparent border-white/10 h-9 text-sm" value={search} onChange={(e) => onSearchChange(e.target.value)} diff --git a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx new file mode 100644 index 0000000..0ca7e5c --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { HugeiconsIcon } from "@hugeicons/react"; +import { PlusSignIcon, Delete02Icon, Link01Icon } from "@hugeicons/core-free-icons"; +import Button from "@/authly/components/ui/Button"; +import Input from "@/authly/components/ui/Input"; +import { TagInput } from "@/authly/components/ui/TagInput"; +import { FormField } from "@/authly/components/ui/FormField"; +import { AnimatePresence, motion } from "framer-motion"; + +interface AuthenticationSectionProps { + redirectURIs: string[]; + allowedScopes: string[]; + newRedirectURI: string; + onAllowedScopesChange: (scopes: string[]) => void; + onNewRedirectURIChange: (uri: string) => void; + onAddRedirect: () => void; + onRemoveRedirect: (index: number) => void; +} + +export function AuthenticationSection({ + redirectURIs, + allowedScopes, + newRedirectURI, + onAllowedScopesChange, + onNewRedirectURIChange, + onAddRedirect, + onRemoveRedirect, +}: AuthenticationSectionProps) { + return ( +
+
+

Authentication

+
+ +
+
+ +
+ + {redirectURIs.map((uri, idx) => ( + +
+ + {uri} +
+ +
+ ))} +
+ +
+ onNewRedirectURIChange(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), onAddRedirect())} + placeholder="https://..." + className="text-xs" + /> + +
+

Callback URLs allowed for this client.

+
+
+ + + + +
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx new file mode 100644 index 0000000..a5a2fd4 --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx @@ -0,0 +1,52 @@ +"use client"; + +import { HugeiconsIcon } from "@hugeicons/react"; +import { Globe02Icon } from "@hugeicons/core-free-icons"; +import Input from "@/authly/components/ui/Input"; +import { FormField } from "@/authly/components/ui/FormField"; + +interface BasicDetailsSectionProps { + name: string; + description: string; + domain: string; + onNameChange: (value: string) => void; + onDescriptionChange: (value: string) => void; + onDomainChange: (value: string) => void; +} + +export function BasicDetailsSection({ + name, + description, + domain, + onNameChange, + onDescriptionChange, + onDomainChange, +}: BasicDetailsSectionProps) { + return ( +
+
+

Basic Details

+
+
+ + onNameChange(e.target.value)} placeholder="My Awesome App" /> + + + onDescriptionChange(e.target.value)} + placeholder="What does this service do?" + /> + + + onDomainChange(e.target.value)} + placeholder="https://app.example.com" + startIcon={} + /> + +
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx new file mode 100644 index 0000000..e3067bd --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { HugeiconsIcon } from "@hugeicons/react"; +import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons"; +import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; +import { toast } from "sonner"; + +interface CredentialsCardProps { + clientId: string; + clientSecret: string | null; +} + +export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps) { + const [copiedId, copyId] = useCopyToClipboard(); + const [copiedSecret, copySecret] = useCopyToClipboard(); + + return ( +
+

Credentials

+ +
+
+ +
+
+ {clientId} +
+ +
+
+ +
+ +
+
+ {clientSecret ? "••••••••••••••••••••••••••••" : "No Secret (Public)"} +
+ {clientSecret && ( + + )} +
+
+
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx new file mode 100644 index 0000000..241f9d0 --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { HugeiconsIcon } from "@hugeicons/react"; +import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons"; +import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; + +interface MetadataCardProps { + createdAt: string; + updatedAt: string; + serviceId: string; +} + +export function MetadataCard({ createdAt, updatedAt, serviceId }: MetadataCardProps) { + const [copiedId, copyId] = useCopyToClipboard(); + + return ( +
+

Metadata

+
+
+ Created At + {new Date(createdAt).toLocaleString()} +
+
+ Updated At + {new Date(updatedAt).toLocaleString()} +
+
+ Service ID +
+
+ {serviceId} +
+ +
+
+
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx new file mode 100644 index 0000000..bd667cb --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { HugeiconsIcon } from "@hugeicons/react"; +import { FloppyDiskIcon } from "@hugeicons/core-free-icons"; +import Button from "@/authly/components/ui/Button"; +import Loader from "@/authly/components/ui/Loader"; +import { cn } from "@/authly/lib/utils"; + +interface SaveActionsProps { + hasChanges: boolean; + isSaving: boolean; + onSave: (e: React.FormEvent) => void; +} + +export function SaveActions({ hasChanges, isSaving, onSave }: SaveActionsProps) { + return ( +
+
+ {hasChanges ? ( + +
+ Unsaved changes + + ) : ( + "All changes saved" + )} +
+ +
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx b/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx new file mode 100644 index 0000000..9c790d1 --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx @@ -0,0 +1,154 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { toast } from "sonner"; +import { parseApiError } from "@/authly/lib/api/utils"; +import { BasicDetailsSection } from "./BasicDetailsSection"; +import { AuthenticationSection } from "./AuthenticationSection"; +import { CredentialsCard } from "./CredentialsCard"; +import { StatusCard } from "./StatusCard"; +import { MetadataCard } from "./MetadataCard"; +import { SaveActions } from "./SaveActions"; +import { Service } from "@/authly/lib/schemas/admin/services"; +import { useUpdateService } from "@/authly/lib/hooks/admin/useServices"; + +type UpdateServiceMutation = ReturnType; +type UpdateServiceMutate = UpdateServiceMutation["mutate"]; + +interface ServiceFormProps { + service: Service; + serviceId: string; + updateService: UpdateServiceMutate; + isUpdating: boolean; +} + +export function ServiceForm({ service, serviceId, updateService, isUpdating }: ServiceFormProps) { + const [name, setName] = useState(service.name || ""); + const [description, setDescription] = useState(service.description || ""); + const [domain, setDomain] = useState(service.domain || ""); + const [isActive, setIsActive] = useState(service.active); + + const [redirectURIs, setRedirectURIs] = useState(service.redirect_uris || []); + const [allowedScopes, setAllowedScopes] = useState(service.allowed_scopes || []); + + const [newRedirectURI, setNewRedirectURI] = useState(""); + + const hasChanges = useMemo(() => { + const currentRedirects = [...redirectURIs].sort().join(","); + const originalRedirects = [...(service.redirect_uris || [])].sort().join(","); + const currentScopes = [...allowedScopes].sort().join(","); + const originalScopes = [...(service.allowed_scopes || [])].sort().join(","); + + return ( + name !== service.name || + description !== (service.description || "") || + domain !== (service.domain || "") || + isActive !== service.active || + currentRedirects !== originalRedirects || + currentScopes !== originalScopes + ); + }, [name, description, domain, redirectURIs, allowedScopes, isActive, service]); + + const handleAddRedirect = () => { + if (!newRedirectURI.trim()) return; + try { + new URL(newRedirectURI); + } catch { + toast.error("Invalid URL format"); + return; + } + if (redirectURIs.includes(newRedirectURI.trim())) { + toast.error("URI already exists"); + return; + } + setRedirectURIs([...redirectURIs, newRedirectURI.trim()]); + setNewRedirectURI(""); + toast.success("Redirect URI added"); + }; + + const handleRemoveRedirect = (index: number) => { + setRedirectURIs(redirectURIs.filter((_, i) => i !== index)); + toast.success("Redirect URI removed"); + }; + + const handleSave = (e: React.FormEvent) => { + e.preventDefault(); + + updateService( + { + params: { path: { id: serviceId } }, + body: { + name, + description, + domain, + active: isActive, + redirect_uris: redirectURIs, + allowed_scopes: allowedScopes, + }, + }, + { + onSuccess: () => { + toast.success("Service updated successfully"); + }, + onError: (err) => { + const { message, details } = parseApiError(err); + + if (Object.keys(details).length > 0) { + Object.values(details).forEach((detailMessage) => { + toast.error(detailMessage); + }); + } else { + toast.error(message || "Failed to update service"); + } + }, + }, + ); + }; + + return ( +
+
+
+ + + + + +
+ +
+ + + { + setIsActive(!isActive); + toast.success(`Service ${!isActive ? "activated" : "deactivated"}`); + }} + /> + + +
+
+
+ ); +} diff --git a/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx new file mode 100644 index 0000000..de6cc0c --- /dev/null +++ b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { cn } from "@/authly/lib/utils"; +import Switch from "@/authly/components/ui/Switch"; + +interface StatusCardProps { + isActive: boolean; + onToggle: () => void; +} + +export function StatusCard({ isActive, onToggle }: StatusCardProps) { + return ( +
+

Status

+ +
+ + {isActive ? "Active" : "Disabled"} + + onToggle()} /> +
+
+ ); +} diff --git a/apps/web/src/components/dashboard/overview/RecentActivityToolbar.tsx b/apps/web/src/components/dashboard/overview/RecentActivityToolbar.tsx index c0bac14..f003ccd 100644 --- a/apps/web/src/components/dashboard/overview/RecentActivityToolbar.tsx +++ b/apps/web/src/components/dashboard/overview/RecentActivityToolbar.tsx @@ -44,7 +44,7 @@ export function RecentActivityToolbar({ search, onSearchChange, filter, onFilter
} + startIcon={} className="bg-transparent border-white/10 h-9 text-sm" value={search} onChange={(e) => onSearchChange(e.target.value)} diff --git a/apps/web/src/components/ui/TagInput.tsx b/apps/web/src/components/ui/TagInput.tsx new file mode 100644 index 0000000..eb926e4 --- /dev/null +++ b/apps/web/src/components/ui/TagInput.tsx @@ -0,0 +1,90 @@ +"use client"; + +import * as React from "react"; +import { cn } from "@/authly/lib/utils"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Cancel01Icon } from "@hugeicons/core-free-icons"; + +interface TagInputProps { + value: string[]; + onChange: (value: string[]) => void; + placeholder?: string; + className?: string; + disabled?: boolean; + error?: boolean; +} + +export function TagInput({ value, onChange, placeholder, className, disabled, error }: TagInputProps) { + const [inputValue, setInputValue] = React.useState(""); + const inputRef = React.useRef(null); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (disabled) return; + + const val = inputValue.trim(); + + if ((e.key === "Enter" || e.key === " " || e.key === ",") && val) { + e.preventDefault(); + if (!value.includes(val)) { + onChange([...value, val]); + } + setInputValue(""); + } else if (e.key === "Backspace" && !inputValue && value.length > 0) { + e.preventDefault(); + onChange(value.slice(0, -1)); + } + }; + + const removeTag = (tagToRemove: string) => { + if (disabled) return; + onChange(value.filter((tag) => tag !== tagToRemove)); + }; + + return ( +
inputRef.current?.focus()} + > + {value.map((tag) => ( + + {tag} + + + ))} + + setInputValue(e.target.value)} + onKeyDown={handleKeyDown} + className="flex-1 bg-transparent text-sm text-white placeholder:text-white/20 focus:outline-none min-w-[100px] h-6" + placeholder={value.length === 0 ? placeholder : ""} + disabled={disabled} + /> +
+ ); +} diff --git a/apps/web/src/lib/hooks/admin/usePermissions.ts b/apps/web/src/lib/hooks/admin/usePermissions.ts new file mode 100644 index 0000000..4c9b708 --- /dev/null +++ b/apps/web/src/lib/hooks/admin/usePermissions.ts @@ -0,0 +1,73 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { $api } from "@/authly/lib/api/client"; + +/** + * Hook to fetch permissions for a service. + */ +export function useServicePermissions( + serviceId: string, + params?: { resource?: string; limit?: number; offset?: number }, +) { + return $api.useQuery( + "get", + "/admin/permissions", + { + params: { + query: { + service_id: serviceId, + ...params, + }, + }, + }, + { + enabled: !!serviceId, + }, + ); +} + +/** + * Hook to create a new permission definition. + */ +export function useCreatePermission() { + const queryClient = useQueryClient(); + + return $api.useMutation("post", "/admin/permissions", { + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ + queryKey: [ + "get", + "/admin/permissions", + { params: { query: { service_id: variables.body.service_id } } }, + ], + }); + // Also invalidate without specific params just in case + queryClient.invalidateQueries({ queryKey: ["get", "/admin/permissions"] }); + }, + }); +} + +/** + * Hook to update a permission definition. + */ +export function useUpdatePermission() { + const queryClient = useQueryClient(); + + return $api.useMutation("put", "/admin/permissions/{id}", { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["get", "/admin/permissions"] }); + }, + }); +} + +/** + * Hook to delete a permission definition. + */ +export function useDeletePermission() { + const queryClient = useQueryClient(); + + return $api.useMutation("delete", "/admin/permissions/{id}", { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["get", "/admin/permissions"] }); + }, + }); +} diff --git a/apps/web/src/lib/hooks/admin/useRoles.ts b/apps/web/src/lib/hooks/admin/useRoles.ts new file mode 100644 index 0000000..b8dc8bb --- /dev/null +++ b/apps/web/src/lib/hooks/admin/useRoles.ts @@ -0,0 +1,64 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { $api } from "@/authly/lib/api/client"; + +/** + * Hook to fetch roles for a service. + */ +export function useServiceRoles(serviceId: string) { + return $api.useQuery( + "get", + "/admin/roles", + { + params: { + query: { + service_id: serviceId, + }, + }, + }, + { + enabled: !!serviceId, + }, + ); +} + +/** + * Hook to create a new role. + */ +export function useCreateRole() { + const queryClient = useQueryClient(); + + return $api.useMutation("post", "/admin/roles", { + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ + queryKey: ["get", "/admin/roles", { params: { query: { service_id: variables.body.service_id } } }], + }); + queryClient.invalidateQueries({ queryKey: ["get", "/admin/roles"] }); + }, + }); +} + +/** + * Hook to update a role. + */ +export function useUpdateRole() { + const queryClient = useQueryClient(); + + return $api.useMutation("put", "/admin/roles/{id}", { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["get", "/admin/roles"] }); + }, + }); +} + +/** + * Hook to delete a role. + */ +export function useDeleteRole() { + const queryClient = useQueryClient(); + + return $api.useMutation("delete", "/admin/roles/{id}", { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["get", "/admin/roles"] }); + }, + }); +} From 7be0d741a529555fd938813ce4aea638becae6c3 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 20:12:53 +0100 Subject: [PATCH 21/53] feat(ServiceForm): add disabled state to sections and buttons for default Authly service; enhance user experience by preventing modifications --- .../overview/AuthenticationSection.tsx | 35 ++++++++++++------- .../services/overview/BasicDetailsSection.tsx | 11 +++++- .../admin/services/overview/SaveActions.tsx | 5 +-- .../admin/services/overview/ServiceForm.tsx | 33 +++++++++++++++-- .../admin/services/overview/StatusCard.tsx | 5 +-- apps/web/src/components/ui/TagInput.tsx | 30 ++++++++-------- apps/web/src/lib/config.ts | 8 +++++ 7 files changed, 93 insertions(+), 34 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx index 0ca7e5c..f8ebf6a 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx @@ -16,6 +16,7 @@ interface AuthenticationSectionProps { onNewRedirectURIChange: (uri: string) => void; onAddRedirect: () => void; onRemoveRedirect: (index: number) => void; + disabled?: boolean; } export function AuthenticationSection({ @@ -26,6 +27,7 @@ export function AuthenticationSection({ onNewRedirectURIChange, onAddRedirect, onRemoveRedirect, + disabled = false, }: AuthenticationSectionProps) { return (
@@ -50,17 +52,19 @@ export function AuthenticationSection({ {uri}
- + {!disabled && ( + + )} ))} @@ -72,6 +76,7 @@ export function AuthenticationSection({ onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), onAddRedirect())} placeholder="https://..." className="text-xs" + disabled={disabled} /> @@ -88,7 +94,12 @@ export function AuthenticationSection({
- +
diff --git a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx index a5a2fd4..472e3b7 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx @@ -12,6 +12,7 @@ interface BasicDetailsSectionProps { onNameChange: (value: string) => void; onDescriptionChange: (value: string) => void; onDomainChange: (value: string) => void; + disabled?: boolean; } export function BasicDetailsSection({ @@ -21,6 +22,7 @@ export function BasicDetailsSection({ onNameChange, onDescriptionChange, onDomainChange, + disabled = false, }: BasicDetailsSectionProps) { return (
@@ -29,13 +31,19 @@ export function BasicDetailsSection({
- onNameChange(e.target.value)} placeholder="My Awesome App" /> + onNameChange(e.target.value)} + placeholder="My Awesome App" + disabled={disabled} + /> onDescriptionChange(e.target.value)} placeholder="What does this service do?" + disabled={disabled} /> @@ -44,6 +52,7 @@ export function BasicDetailsSection({ onChange={(e) => onDomainChange(e.target.value)} placeholder="https://app.example.com" startIcon={} + disabled={disabled} />
diff --git a/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx index bd667cb..1d156e8 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx @@ -10,9 +10,10 @@ interface SaveActionsProps { hasChanges: boolean; isSaving: boolean; onSave: (e: React.FormEvent) => void; + disabled?: boolean; } -export function SaveActions({ hasChanges, isSaving, onSave }: SaveActionsProps) { +export function SaveActions({ hasChanges, isSaving, onSave, disabled = false }: SaveActionsProps) { return (
@@ -27,7 +28,7 @@ export function SaveActions({ hasChanges, isSaving, onSave }: SaveActionsProps)
+ {!disabled && ( + + )} ))} diff --git a/apps/web/src/lib/config.ts b/apps/web/src/lib/config.ts index 31a72af..c26baea 100644 --- a/apps/web/src/lib/config.ts +++ b/apps/web/src/lib/config.ts @@ -7,3 +7,11 @@ export const OIDC_CONFIG = { response_type: "code", scope: "openid profile email", }; + +/** + * Check if a service ID or client ID is the default Authly service + */ +export const isDefaultAuthlyService = (id: string | undefined | null): boolean => { + if (!id) return false; + return id.startsWith("authly_authly_") || id === OIDC_CONFIG.client_id; +}; From 6196eeb84327b60beff5d6de43ce117d3d324e98 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 20:30:22 +0100 Subject: [PATCH 22/53] fix(RoleService): update permissions associations only when explicitly provided to prevent unintended overwrites --- apps/api/internal/domain/role/service.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/api/internal/domain/role/service.go b/apps/api/internal/domain/role/service.go index c59f229..2d99668 100644 --- a/apps/api/internal/domain/role/service.go +++ b/apps/api/internal/domain/role/service.go @@ -104,9 +104,11 @@ func (s *service) UpdateRole(updatedRole *Role) error { return err } - // Update permissions associations - if err := tx.Model(updatedRole).Association("Permissions").Replace(updatedRole.Permissions); err != nil { - return err + // Update permissions associations only when explicitly provided + if updatedRole.Permissions != nil { + if err := tx.Model(updatedRole).Association("Permissions").Replace(updatedRole.Permissions); err != nil { + return err + } } return txSvc.propagateRoleChanges(oldRole, updatedRole) From 9c73c17cf04a51304eb0c0deed31467e91bf2545 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:31:40 +0100 Subject: [PATCH 23/53] fix(RoleService): improve error handling in propagateRoleChanges method to ensure proper permission version incrementing --- apps/api/internal/domain/role/service.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/api/internal/domain/role/service.go b/apps/api/internal/domain/role/service.go index 2d99668..fdf446a 100644 --- a/apps/api/internal/domain/role/service.go +++ b/apps/api/internal/domain/role/service.go @@ -1,6 +1,7 @@ package role import ( + "errors" "fmt" "github.com/Anvoria/authly/internal/domain/permission" @@ -194,6 +195,9 @@ func (s *service) propagateRoleChanges(oldRole, newRole *Role) error { userPerm, err := s.permissionRepo.FindUserPermission(userID, oldRole.ServiceID.String(), resPtr) if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } newPerm := &permission.UserPermission{ UserID: uid, ServiceID: oldRole.ServiceID, @@ -221,7 +225,9 @@ func (s *service) propagateRoleChanges(oldRole, newRole *Role) error { return err } for _, uid := range userIDs { - _ = s.permissionRepo.IncrementPermissionVersion(uid) + if err := s.permissionRepo.IncrementPermissionVersion(uid); err != nil { + return fmt.Errorf("failed to increment permission version for user %s in role %s: %w", uid, oldRole.ID.String(), err) + } } return nil From 5664bdad06fdab45fca069ba7398f8329a1f6b3a Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:33:55 +0100 Subject: [PATCH 24/53] fix(RoleService): enhance role assignment logic to handle global resource permissions correctly --- apps/api/internal/domain/role/service.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/api/internal/domain/role/service.go b/apps/api/internal/domain/role/service.go index fdf446a..1a8bf15 100644 --- a/apps/api/internal/domain/role/service.go +++ b/apps/api/internal/domain/role/service.go @@ -369,7 +369,12 @@ func (s *service) AssignRole(userID, roleID string) error { var targetPerm *permission.UserPermission for _, up := range userPerms { - if up.Resource != nil && *up.Resource == resourceKey { + if resourceKey == "__global__" { + if up.Resource == nil { + targetPerm = up + break + } + } else if up.Resource != nil && *up.Resource == resourceKey { targetPerm = up break } From 51dbd0dd0bc3c9462d67b395ff8434ff06a873d3 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:38:39 +0100 Subject: [PATCH 25/53] feat: enhance accessibility by adding aria-labels to various components in the dashboard overview --- .../services/overview/AuthenticationSection.tsx | 4 ++++ .../admin/services/overview/BasicDetailsSection.tsx | 3 +++ .../admin/services/overview/CredentialsCard.tsx | 2 ++ .../admin/services/overview/MetadataCard.tsx | 1 + .../admin/services/overview/SaveActions.tsx | 1 + .../admin/services/overview/StatusCard.tsx | 7 ++++++- apps/web/src/components/ui/TagInput.tsx | 13 ++++++++++++- 7 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx index f8ebf6a..a2cdce5 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx @@ -77,6 +77,7 @@ export function AuthenticationSection({ placeholder="https://..." className="text-xs" disabled={disabled} + aria-label="Add redirect URI" />

Callback URLs allowed for this client.

@@ -99,6 +102,7 @@ export function AuthenticationSection({ onChange={onAllowedScopesChange} placeholder="Add scope..." disabled={disabled} + aria-label="Add allowed scopes" /> diff --git a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx index 472e3b7..7123603 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx @@ -36,6 +36,7 @@ export function BasicDetailsSection({ onChange={(e) => onNameChange(e.target.value)} placeholder="My Awesome App" disabled={disabled} + aria-label="Service Name" /> @@ -44,6 +45,7 @@ export function BasicDetailsSection({ onChange={(e) => onDescriptionChange(e.target.value)} placeholder="What does this service do?" disabled={disabled} + aria-label="Description" /> @@ -53,6 +55,7 @@ export function BasicDetailsSection({ placeholder="https://app.example.com" startIcon={} disabled={disabled} + aria-label="Primary Domain" /> diff --git a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx index e3067bd..d5ae92f 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx @@ -28,6 +28,7 @@ export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps diff --git a/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx index 65e5173..031e800 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx @@ -18,7 +18,12 @@ export function StatusCard({ isActive, onToggle, disabled = false }: StatusCardP {isActive ? "Active" : "Disabled"} - onToggle()} disabled={disabled} /> + onToggle()} + disabled={disabled} + aria-label={`Service status is ${isActive ? "active" : "disabled"}. Click to ${isActive ? "deactivate" : "activate"}.`} + /> ); diff --git a/apps/web/src/components/ui/TagInput.tsx b/apps/web/src/components/ui/TagInput.tsx index 8e7118a..d00b746 100644 --- a/apps/web/src/components/ui/TagInput.tsx +++ b/apps/web/src/components/ui/TagInput.tsx @@ -12,9 +12,18 @@ interface TagInputProps { className?: string; disabled?: boolean; error?: boolean; + "aria-label"?: string; } -export function TagInput({ value, onChange, placeholder, className, disabled, error }: TagInputProps) { +export function TagInput({ + value, + onChange, + placeholder, + className, + disabled, + error, + "aria-label": ariaLabel, +}: TagInputProps) { const [inputValue, setInputValue] = React.useState(""); const inputRef = React.useRef(null); @@ -66,6 +75,7 @@ export function TagInput({ value, onChange, placeholder, className, disabled, er removeTag(tag); }} className="ml-1 p-0.5 rounded-md outline-none transition-colors hover:bg-white/20 cursor-pointer" + aria-label={`Remove ${tag}`} > ); From fc1eb03340760c944ae73dfe4b509ade875841cd Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:40:40 +0100 Subject: [PATCH 26/53] fix(Switch): improve click handling to prevent state changes when disabled and ensure onClick is called --- apps/web/src/components/ui/Switch.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/ui/Switch.tsx b/apps/web/src/components/ui/Switch.tsx index b637465..90e6aeb 100644 --- a/apps/web/src/components/ui/Switch.tsx +++ b/apps/web/src/components/ui/Switch.tsx @@ -10,13 +10,20 @@ export interface SwitchProps extends Omit( - ({ className, checked, onCheckedChange, disabled, ...props }, ref) => { + ({ className, checked, onCheckedChange, disabled, onClick, ...props }, ref) => { + const handleClick: React.MouseEventHandler = (event) => { + onClick?.(event); + if (event.defaultPrevented || disabled) return; + onCheckedChange(!checked); + }; + return ( + {service.active ? ( diff --git a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx index 0fc63a1..a52eae4 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx @@ -1,9 +1,6 @@ "use client"; -import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { toast } from "sonner"; -import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; +import { CopyButton } from "@/authly/components/ui/CopyButton"; interface CredentialsCardProps { clientId: string; @@ -11,9 +8,6 @@ interface CredentialsCardProps { } export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps) { - const [copiedId, copyId] = useCopyToClipboard(); - const [copiedSecret, copySecret] = useCopyToClipboard(); - return (

Credentials

@@ -25,17 +19,11 @@ export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps
{clientId}
- + />
@@ -46,20 +34,11 @@ export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps {clientSecret ? "••••••••••••••••••••••••••••" : "No Secret (Public)"} {clientSecret && ( - + /> )} diff --git a/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx index 1ddb909..0361ad7 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx @@ -1,8 +1,6 @@ "use client"; -import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; +import { CopyButton } from "@/authly/components/ui/CopyButton"; interface MetadataCardProps { createdAt: string; @@ -11,8 +9,6 @@ interface MetadataCardProps { } export function MetadataCard({ createdAt, updatedAt, serviceId }: MetadataCardProps) { - const [copiedId, copyId] = useCopyToClipboard(); - return (

Metadata

@@ -31,17 +27,11 @@ export function MetadataCard({ createdAt, updatedAt, serviceId }: MetadataCardPr
{serviceId}
- + />
diff --git a/apps/web/src/components/ui/CopyButton.tsx b/apps/web/src/components/ui/CopyButton.tsx new file mode 100644 index 0000000..ad1f142 --- /dev/null +++ b/apps/web/src/components/ui/CopyButton.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { toast } from "sonner"; +import { useCopyToClipboard } from "@/authly/lib/hooks/useCopyToClipboard"; + +interface CopyButtonProps { + value: string; + ariaLabel: string; + onCopy?: () => void; + className?: string; +} + +export function CopyButton({ value, ariaLabel, onCopy, className }: CopyButtonProps) { + const [copied, copy] = useCopyToClipboard(); + + const handleClick = () => { + copy(value); + toast.success(`${ariaLabel} copied!`); + onCopy?.(); + }; + + return ( + + ); +} From ef9115762e74c43dfc2c635e692af9be3a351642 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:41:26 +0100 Subject: [PATCH 45/53] refactor(ServiceRow): remove unused utility import to streamline component code --- apps/web/src/components/dashboard/admin/services/ServiceRow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx index 9551de9..9ac24d0 100644 --- a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx +++ b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx @@ -7,7 +7,6 @@ import Button from "@/authly/components/ui/Button"; import { CopyButton } from "@/authly/components/ui/CopyButton"; import { TableCell, TableRow } from "@/authly/components/ui/Table"; import { Service } from "@/authly/lib/schemas/admin/services"; -import { cn } from "@/authly/lib/utils"; interface ServiceRowProps { service: Service; From 5aa7ed9515ad160211e6112bb2a7f7fe2b30482c Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:46:35 +0100 Subject: [PATCH 46/53] fix(repository): add ordering by creation date to user retrieval query for consistent results --- apps/api/internal/domain/user/repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/internal/domain/user/repository.go b/apps/api/internal/domain/user/repository.go index 266c4eb..80a13b9 100644 --- a/apps/api/internal/domain/user/repository.go +++ b/apps/api/internal/domain/user/repository.go @@ -87,7 +87,7 @@ func (r *repository) FindAll(limit, offset int, search string) ([]*User, int64, return nil, 0, err } - if err := query.Limit(limit).Offset(offset).Find(&users).Error; err != nil { + if err := query.Order("created_at ASC").Limit(limit).Offset(offset).Find(&users).Error; err != nil { return nil, 0, err } From b2b0c9eb67c0eb8a4b89da8ef300ed19b234c03d Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:46:51 +0100 Subject: [PATCH 47/53] refactor(AuthorizePage): simplify consent handling logic by extracting client ID to a variable and updating useEffect dependencies --- apps/web/src/app/authorize/page.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app/authorize/page.tsx b/apps/web/src/app/authorize/page.tsx index 83b9772..c20a049 100644 --- a/apps/web/src/app/authorize/page.tsx +++ b/apps/web/src/app/authorize/page.tsx @@ -181,21 +181,13 @@ function AuthorizePageContent() { window.location.href = errorRedirect; }; + const consentClientId = state.type === "consent" ? state.client.client_id : undefined; + useEffect(() => { - if ( - state.type === "consent" && - state.client.client_id === OIDC_CONFIG.client_id && - !confirmMutation.isPending - ) { + if (state.type === "consent" && consentClientId === OIDC_CONFIG.client_id && !confirmMutation.isPending) { handleApprove(); } - }, [ - state.type, - confirmMutation.isPending, - handleApprove, - // @ts-expect-error - state.client.client_id is safe here because we check state.type === "consent" in the effect - state.client.client_id, - ]); + }, [state.type, confirmMutation.isPending, handleApprove, consentClientId]); if ( state.type === "validating" || From 87c5d7711eb5c78b421fc061d4fe96eeceaea6af Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:47:53 +0100 Subject: [PATCH 48/53] chore(dependencies): remove unused eslint and prettier dependencies from package.json and bun.lock for cleaner project setup --- apps/web/bun.lock | 603 ++---------------------------------------- apps/web/package.json | 3 - 2 files changed, 16 insertions(+), 590 deletions(-) diff --git a/apps/web/bun.lock b/apps/web/bun.lock index 1e22476..39a5047 100644 --- a/apps/web/bun.lock +++ b/apps/web/bun.lock @@ -39,12 +39,9 @@ "@types/react-dom": "^19", "@types/react-syntax-highlighter": "^15.5.13", "ajv": "^8.17.1", - "eslint": "^9", - "eslint-config-next": "16.0.10", "husky": "^9.1.7", "lint-staged": "^16.2.7", "openapi-typescript": "^7.10.1", - "prettier": "^3.7.4", "swagger2openapi": "^7.0.8", "tailwindcss": "^4", "typescript": "^5", @@ -58,38 +55,10 @@ "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="], - - "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="], - - "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], - - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - - "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], - - "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], - "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], - "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], - - "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], - - "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], - "@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="], @@ -112,30 +81,8 @@ "@clack/prompts": ["@clack/prompts@1.0.0-alpha.8", "", { "dependencies": { "@clack/core": "1.0.0-alpha.7", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-YZGC4BmTKSF5OturNKEz/y4xNjYGmGk6NI785CQucJ7OEdX0qbMmL/zok+9bL6c7qE3WSYffyK5grh2RnkGNtQ=="], - "@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], - "@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], - - "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], - - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], - - "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], - - "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], - - "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], - "@exodus/schemasafe": ["@exodus/schemasafe@1.3.0", "", {}, "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw=="], "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], @@ -152,16 +99,8 @@ "@hugeicons/react": ["@hugeicons/react@1.1.4", "", { "peerDependencies": { "react": ">=16.0.0" } }, "sha512-gsc3eZyd2fGqRUThW9+lfjxxsOkz6KNVmRXRgJjP32GL0OnnLJnl3hytKt47CBbiQj2xE2kCw+rnP3UQCThcKw=="], - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], - - "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - "@humanwhocodes/momoa": ["@humanwhocodes/momoa@2.0.4", "", {}, "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA=="], - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -242,12 +181,8 @@ "@kubb/react-fabric": ["@kubb/react-fabric@0.9.2", "", { "dependencies": { "@kubb/fabric-core": "0.9.2", "dedent": "^1.7.1", "execa": "^9.6.1", "indent-string": "^5.0.0", "natural-orderby": "^5.0.0", "react-devtools-core": "6.1.2", "signal-exit": "^4.1.0", "ws": "8.18.0" } }, "sha512-67Bkn2+w0Ij8pDHsIHl8Y+0Db8pbQhS9FQy3oCAVaZAITs18Dc95TPOL36ZD7qqBzxlu9QxLlzEjFLUG69NgXA=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - "@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="], - "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.0.10", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-b2NlWN70bbPLmfyoLvvidPKWENBYYIe017ZGUpElvQjDytCWgxPJx7L9juxHt0xHvNVA08ZHJdOyhGzon/KJuw=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="], "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="], @@ -264,14 +199,6 @@ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - - "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], - "@pnpm/config.env-replace": ["@pnpm/config.env-replace@1.1.0", "", {}, "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="], "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], @@ -352,8 +279,6 @@ "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], - "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], - "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], @@ -402,8 +327,6 @@ "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.1", "", { "dependencies": { "@tanstack/query-devtools": "5.91.1" }, "peerDependencies": { "@tanstack/react-query": "^5.90.10", "react": "^18 || ^19" } }, "sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], @@ -422,14 +345,10 @@ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], - "@types/node": ["@types/node@20.19.26", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg=="], "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="], @@ -446,68 +365,6 @@ "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.49.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/type-utils": "8.49.0", "@typescript-eslint/utils": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.49.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA=="], - - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.49.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.49.0", "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0" } }, "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg=="], - - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.49.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA=="], - - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@8.49.0", "", {}, "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.49.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.49.0", "@typescript-eslint/tsconfig-utils": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.49.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA=="], - - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA=="], - - "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], - - "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], - - "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], - - "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], - - "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], - - "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], - - "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], - - "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], - - "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], - - "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], - - "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], - - "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], - - "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], - - "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], - - "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], - - "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], - - "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], - - "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], - - "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], - - "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -522,62 +379,26 @@ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], - "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], - - "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], - - "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], - - "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], - - "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], - - "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], - - "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], - - "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], - - "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], - - "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], - - "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - - "axe-core": ["axe-core@4.11.0", "", {}, "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ=="], - "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="], - "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.7", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg=="], - - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - "bytes": ["bytes@3.0.0", "", {}, "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "call-me-maybe": ["call-me-maybe@1.0.2", "", {}, "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], @@ -586,7 +407,7 @@ "caniuse-lite": ["caniuse-lite@1.0.30001760", "", {}, "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="], @@ -634,8 +455,6 @@ "content-disposition": ["content-disposition@0.5.2", "", {}, "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -666,14 +485,6 @@ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], - "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], - - "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], - - "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], - - "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], @@ -684,25 +495,15 @@ "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], - - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - - "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], - "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="], - - "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], @@ -712,22 +513,14 @@ "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], - "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], - - "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "es-toolkit": ["es-toolkit@1.43.0", "", {}, "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA=="], "es5-ext": ["es5-ext@0.10.64", "", { "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg=="], @@ -742,42 +535,8 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], - - "eslint-config-next": ["eslint-config-next@16.0.10", "", { "dependencies": { "@next/eslint-plugin-next": "16.0.10", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-BxouZUm0I45K4yjOOIzj24nTi0H2cGo0y7xUmk+Po/PYtJXFBYVDS1BguE7t28efXjKdcN0tmiLivxQy//SsZg=="], - - "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], - - "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], - - "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], - - "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], - - "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], - - "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], - - "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], - - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - "esniff": ["esniff@2.0.1", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg=="], - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - "event-emitter": ["event-emitter@0.3.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], @@ -788,38 +547,20 @@ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], - "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - "fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + "find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="], "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], @@ -830,14 +571,6 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], - - "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], - - "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], @@ -850,30 +583,12 @@ "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], - "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], - - "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], - - "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "gradient-string": ["gradient-string@3.0.0", "", { "dependencies": { "chalk": "^5.3.0", "tinygradient": "^1.1.5" } }, "sha512-frdKI4Qi8Ihp4C6wZNB565de/THpIaw3DjP5ku87M+N9rNSGmPTjfkq61SdRXB7eCaL8O1hkKDvf6CDMtOzIAg=="], - "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - - "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], @@ -884,10 +599,6 @@ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], - "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - - "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], "highlightjs-vue": ["highlightjs-vue@1.0.0", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="], @@ -900,102 +611,42 @@ "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="], "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], - "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], - "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], - "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], - - "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], - - "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], - - "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], - - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], - - "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], - "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], - "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], - - "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], "is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="], - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], - - "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], - - "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], - "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], - "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], - - "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], - - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], - "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], - - "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], - - "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], - - "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], "js-levenshtein": ["js-levenshtein@1.1.6", "", {}, "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="], @@ -1008,10 +659,6 @@ "jsep": ["jsep@1.4.0", "", {}, "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw=="], - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], - "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], "json-schema-compare": ["json-schema-compare@0.2.2", "", { "dependencies": { "lodash": "^4.17.4" } }, "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ=="], @@ -1022,10 +669,6 @@ "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - - "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], - "jsonc-parser": ["jsonc-parser@3.2.0", "", {}, "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="], "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], @@ -1034,22 +677,12 @@ "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], - "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], - - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - "ky": ["ky@1.14.2", "", {}, "sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug=="], - "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], - - "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], - "latest-version": ["latest-version@9.0.0", "", { "dependencies": { "package-json": "^10.0.0" } }, "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA=="], "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], @@ -1080,22 +713,16 @@ "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="], - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - "lru-queue": ["lru-queue@0.1.0", "", { "dependencies": { "es5-ext": "~0.10.2" } }, "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], @@ -1106,8 +733,6 @@ "memoizee": ["memoizee@0.4.17", "", { "dependencies": { "d": "^1.0.2", "es5-ext": "^0.10.64", "es6-weak-map": "^2.0.3", "event-emitter": "^0.3.5", "is-promise": "^2.2.2", "lru-queue": "^0.1.0", "next-tick": "^1.1.0", "timers-ext": "^0.1.7" } }, "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA=="], - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -1116,7 +741,7 @@ "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -1132,10 +757,6 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], - - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "natural-orderby": ["natural-orderby@5.0.0", "", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="], "next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="], @@ -1148,8 +769,6 @@ "node-readfiles": ["node-readfiles@0.2.0", "", { "dependencies": { "es6-promise": "^3.2.1" } }, "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA=="], - "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], - "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], "oas": ["oas@28.8.3", "", { "dependencies": { "@readme/openapi-parser": "^5.5.0", "@types/json-schema": "^7.0.11", "json-schema-merge-allof": "^0.8.1", "jsonpath-plus": "^10.0.0", "jsonpointer": "^5.0.0", "memoizee": "^0.4.16", "openapi-types": "^12.1.1", "path-to-regexp": "^8.1.0", "remove-undefined-objects": "^7.0.0" } }, "sha512-9cAUBz6BomISCoClqJ09aBjBklhNIXC3Ax4F1HVWskfgnmzBsJECSXzQSXBDumusoD/ee2PNVUuMjw0MA+fieQ=="], @@ -1166,22 +785,6 @@ "oas-validator": ["oas-validator@5.0.8", "", { "dependencies": { "call-me-maybe": "^1.0.1", "oas-kit-common": "^1.0.8", "oas-linter": "^3.2.2", "oas-resolver": "^2.5.6", "oas-schema-walker": "^1.1.5", "reftools": "^1.1.9", "should": "^13.2.1", "yaml": "^1.10.0" } }, "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - - "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], - - "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], - - "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], - - "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], - - "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], - "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "openapi-fetch": ["openapi-fetch@0.15.0", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-OjQUdi61WO4HYhr9+byCPMj0+bgste/LtSBEcV6FzDdONTs7x0fWn8/ndoYwzqCsKWIxEZwo4FN/TG1c1rI8IQ=="], @@ -1194,13 +797,9 @@ "openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.15", "", {}, "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw=="], - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - - "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "p-limit": ["p-limit@7.2.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ=="], - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "package-json": ["package-json@10.0.1", "", { "dependencies": { "ky": "^1.2.0", "registry-auth-token": "^5.0.2", "registry-url": "^6.0.1", "semver": "^7.6.0" } }, "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg=="], @@ -1212,14 +811,12 @@ "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], "path-is-inside": ["path-is-inside@1.0.2", "", {}, "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -1230,30 +827,18 @@ "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], - "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - - "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], - "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], - "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="], "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "range-parser": ["range-parser@1.2.0", "", {}, "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A=="], "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], @@ -1286,14 +871,10 @@ "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], - "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], - "refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="], "reftools": ["reftools@1.1.9", "", {}, "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w=="], - "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], - "registry-auth-token": ["registry-auth-token@5.1.0", "", { "dependencies": { "@pnpm/npm-conf": "^2.1.0" } }, "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw=="], "registry-url": ["registry-url@6.0.1", "", { "dependencies": { "rc": "1.2.8" } }, "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q=="], @@ -1308,26 +889,12 @@ "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - - "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], - - "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], - - "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="], @@ -1336,12 +903,6 @@ "serve-handler": ["serve-handler@6.1.6", "", { "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", "minimatch": "3.1.2", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - - "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], - - "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], - "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -1362,14 +923,6 @@ "should-util": ["should-util@1.0.1", "", {}, "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], @@ -1382,40 +935,20 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], - "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], - - "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], - "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], - - "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], - - "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], - - "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], - - "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], - - "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "swagger2openapi": ["swagger2openapi@7.0.8", "", { "dependencies": { "call-me-maybe": "^1.0.1", "node-fetch": "^2.6.1", "node-fetch-h2": "^2.3.0", "node-readfiles": "^0.2.0", "oas-kit-common": "^1.0.8", "oas-resolver": "^2.5.6", "oas-schema-walker": "^1.1.5", "oas-validator": "^5.0.8", "reftools": "^1.1.9", "yaml": "^1.10.0", "yargs": "^17.0.1" }, "bin": { "swagger2openapi": "swagger2openapi.js", "oas-validate": "oas-validate.js", "boast": "boast.js" } }, "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g=="], "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], @@ -1430,8 +963,6 @@ "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "tinygradient": ["tinygradient@1.1.5", "", { "dependencies": { "@types/tinycolor2": "^1.4.0", "tinycolor2": "^1.0.0" } }, "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1440,44 +971,20 @@ "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], - "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], - - "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="], - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], - - "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], - - "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], - - "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "typescript-eslint": ["typescript-eslint@8.49.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.49.0", "@typescript-eslint/parser": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/utils": "8.49.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg=="], - - "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="], "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], - - "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="], - - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], @@ -1502,24 +1009,12 @@ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], - - "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], - - "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], - - "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], "yaml-ast-parser": ["yaml-ast-parser@0.0.43", "", {}, "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="], @@ -1534,22 +1029,6 @@ "zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], - "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], - - "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "@eslint/eslintrc/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - - "@kubb/core/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="], - "@kubb/fabric-core/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "@kubb/oas/@redocly/openapi-core": ["@redocly/openapi-core@2.14.4", "", { "dependencies": { "@redocly/ajv": "^8.17.1", "@redocly/config": "^0.41.1", "ajv-formats": "^3.0.1", "colorette": "^1.2.0", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "picomatch": "^4.0.3", "pluralize": "^8.0.0", "yaml-ast-parser": "0.0.43" } }, "sha512-FqYf8pBXrZlbhjgcqEpgWrYk3E5j04I4nx0Pn2rMMlDe9S8N9T6axemJGHC6HvrzVJrTWLsUIsV6ndpBICnR2g=="], @@ -1558,8 +1037,6 @@ "@redocly/openapi-core/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], - "@redocly/openapi-core/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], - "@reduxjs/toolkit/immer": ["immer@11.1.3", "", {}, "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], @@ -1574,12 +1051,6 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -1588,26 +1059,6 @@ "cosmiconfig/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], - "eslint/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - - "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], - - "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "gradient-string/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -1620,68 +1071,46 @@ "oas-validator/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], - "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + "p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "serve-handler/mime-types": ["mime-types@2.1.18", "", { "dependencies": { "mime-db": "~1.33.0" } }, "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ=="], - "serve-handler/path-to-regexp": ["path-to-regexp@3.3.0", "", {}, "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw=="], + "serve-handler/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "serve-handler/path-to-regexp": ["path-to-regexp@3.3.0", "", {}, "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw=="], "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - "string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "swagger2openapi/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "@kubb/core/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], - - "@kubb/core/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], - "@kubb/oas/@redocly/openapi-core/@redocly/config": ["@redocly/config@0.41.1", "", { "dependencies": { "json-schema-to-ts": "2.7.2" } }, "sha512-LcMCzFbP/sqkCLSG3YswmeScP4fM5SjDCQizwa+psZ0PhYrKOMF7azZ6ZBkWs115uv5RfOk+jYAWLdKkZGGGXg=="], "@kubb/oas/@redocly/openapi-core/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], "@kubb/oas/@redocly/openapi-core/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "@redocly/openapi-core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "serve-handler/mime-types/mime-db": ["mime-db@1.33.0", "", {}, "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="], + "serve-handler/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "@kubb/core/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], - "@kubb/oas/@redocly/openapi-core/@redocly/config/json-schema-to-ts": ["json-schema-to-ts@2.7.2", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@types/json-schema": "^7.0.9", "ts-algebra": "^1.2.0" } }, "sha512-R1JfqKqbBR4qE8UyBR56Ms30LL62/nlhoz+1UkfI/VE7p54Awu919FZ6ZUPG8zIa3XB65usPJgr1ONVncUGSaQ=="], - "@kubb/core/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], - "@kubb/oas/@redocly/openapi-core/@redocly/config/json-schema-to-ts/ts-algebra": ["ts-algebra@1.2.2", "", {}, "sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA=="], } } diff --git a/apps/web/package.json b/apps/web/package.json index 373fcda..e57bcda 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -51,12 +51,9 @@ "@types/react-dom": "^19", "@types/react-syntax-highlighter": "^15.5.13", "ajv": "^8.17.1", - "eslint": "^9", - "eslint-config-next": "16.0.10", "husky": "^9.1.7", "lint-staged": "^16.2.7", "openapi-typescript": "^7.10.1", - "prettier": "^3.7.4", "swagger2openapi": "^7.0.8", "tailwindcss": "^4", "typescript": "^5" From 1fa50f33a6661275f250df0a0141e1f2db045c29 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:49:00 +0100 Subject: [PATCH 49/53] fix(CreateServiceModal): improve description field error handling by clearing errors on input change --- .../dashboard/admin/services/CreateServiceModal.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx b/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx index 62466fc..fa13021 100644 --- a/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx +++ b/apps/web/src/components/dashboard/admin/services/CreateServiceModal.tsx @@ -152,11 +152,20 @@ export function CreateServiceModalContent({ onClose }: { onClose: () => void }) /> - + setFormData((prev) => ({ ...prev, description: e.target.value }))} + onChange={(e) => { + setFormData((prev) => ({ ...prev, description: e.target.value })); + if (errors.description) + setErrors((prev) => { + const newErrors = { ...prev }; + delete newErrors.description; + return newErrors; + }); + }} + error={!!errors.description} /> From 85e22349d83d49934875209da9ca3722e7b186f4 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:52:09 +0000 Subject: [PATCH 50/53] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`f?= =?UTF-8?q?eat/services-subpage`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @zeedivx. * https://github.com/Anvoria/authly/pull/49#issuecomment-3769764544 The following files were modified: * `apps/api/internal/domain/service/handler.go` * `apps/web/src/app/auth/login/page.tsx` * `apps/web/src/app/authorize/page.tsx` * `apps/web/src/app/dashboard/admin/services/[id]/page.tsx` * `apps/web/src/app/dashboard/admin/users/page.tsx` * `apps/web/src/app/layout.tsx` * `apps/web/src/app/page.tsx` * `apps/web/src/components/dashboard/admin/services/OverviewTab.tsx` * `apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx` * `apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx` * `apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx` * `apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx` * `apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx` * `apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx` * `apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx` * `apps/web/src/components/dashboard/admin/users/EditUserModal.tsx` * `apps/web/src/components/ui/CopyButton.tsx` * `apps/web/src/components/ui/FormField.tsx` * `apps/web/src/components/ui/TagInput.tsx` * `apps/web/src/lib/hooks/admin/usePermissions.ts` * `apps/web/src/lib/hooks/admin/useRoles.ts` * `apps/web/src/proxy.ts` --- apps/api/internal/domain/service/handler.go | 7 +++--- apps/web/src/app/auth/login/page.tsx | 9 +++++++- apps/web/src/app/authorize/page.tsx | 11 ++++++++- .../dashboard/admin/services/[id]/page.tsx | 8 ++++++- .../src/app/dashboard/admin/users/page.tsx | 4 ++-- apps/web/src/app/layout.tsx | 6 ++--- apps/web/src/app/page.tsx | 7 +++++- .../dashboard/admin/services/OverviewTab.tsx | 10 +++++++- .../overview/AuthenticationSection.tsx | 17 +++++++++++++- .../services/overview/BasicDetailsSection.tsx | 14 ++++++++++- .../services/overview/CredentialsCard.tsx | 11 ++++++++- .../admin/services/overview/MetadataCard.tsx | 12 +++++++++- .../admin/services/overview/SaveActions.tsx | 11 ++++++++- .../admin/services/overview/ServiceForm.tsx | 16 ++++++++++++- .../admin/services/overview/StatusCard.tsx | 10 +++++++- .../dashboard/admin/users/EditUserModal.tsx | 15 ++++++------ apps/web/src/components/ui/CopyButton.tsx | 11 ++++++++- apps/web/src/components/ui/FormField.tsx | 12 +++++++++- apps/web/src/components/ui/TagInput.tsx | 16 ++++++++++++- .../web/src/lib/hooks/admin/usePermissions.ts | 23 +++++++++++++++---- apps/web/src/lib/hooks/admin/useRoles.ts | 19 +++++++++++---- apps/web/src/proxy.ts | 10 +++++++- 22 files changed, 217 insertions(+), 42 deletions(-) diff --git a/apps/api/internal/domain/service/handler.go b/apps/api/internal/domain/service/handler.go index d295548..bdf5196 100644 --- a/apps/api/internal/domain/service/handler.go +++ b/apps/api/internal/domain/service/handler.go @@ -10,14 +10,15 @@ type Handler struct { serviceService ServiceInterface } -// NewHandler creates a Handler configured with the provided ServiceInterface. +// NewHandler creates a new Handler using the provided ServiceInterface. func NewHandler(s ServiceInterface) *Handler { return &Handler{serviceService: s} } // validateServiceID validates the service ID parameter from the request. // It checks if the ID is present and has a valid UUID format. -// Returns the validated ID or an error response. +// validateServiceID validates the "id" route parameter and returns it. +// If the parameter is missing or not a valid UUID, it sends a 400 validation error response and returns a non-nil error. func validateServiceID(c *fiber.Ctx) (string, error) { id := c.Params("id") if id == "" { @@ -201,4 +202,4 @@ func (h *Handler) DeleteService(c *fiber.Ctx) error { } return utils.SuccessResponse(c, nil, "Service deleted successfully") -} +} \ No newline at end of file diff --git a/apps/web/src/app/auth/login/page.tsx b/apps/web/src/app/auth/login/page.tsx index d596a0b..9da5d51 100644 --- a/apps/web/src/app/auth/login/page.tsx +++ b/apps/web/src/app/auth/login/page.tsx @@ -17,6 +17,13 @@ import LocalStorageTokenService from "@/authly/lib/services/TokenService"; type LoginFormData = z.infer; +/** + * Renders the login form UI and handles user authentication plus optional OpenID Connect (OIDC) redirect flow. + * + * The component validates credentials, calls the login mutation, displays API and field errors, and upon successful login initiates an OIDC authorization redirect (including PKCE code verifier generation when needed). It also provides a link to the registration page that preserves OIDC parameters when present. + * + * @returns The JSX element for the sign-in page content + */ function LoginPageContent() { const searchParams = useSearchParams(); const router = useRouter(); @@ -172,4 +179,4 @@ export default function LoginPage() { ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/authorize/page.tsx b/apps/web/src/app/authorize/page.tsx index c20a049..cc8582e 100644 --- a/apps/web/src/app/authorize/page.tsx +++ b/apps/web/src/app/authorize/page.tsx @@ -28,6 +28,15 @@ type AuthPageState = | { type: "consent"; client: { name: string; logo_url?: string; client_id: string }; scopes: string[] } | { type: "redirecting" }; +/** + * Render the OIDC authorization page UI and manage the authorization flow lifecycle. + * + * This component validates the incoming OIDC request, checks the IdP session, redirects to login when required, + * presents an error view for invalid requests or clients, shows a consent screen for user approval, and performs + * the confirm/deny actions that ultimately redirect back to the client application. + * + * @returns The component's UI: a loading layout during validation/redirecting or auto-approval, an error view when validation fails, a consent screen when user approval is required, or `null` if no view applies. + */ function AuthorizePageContent() { const searchParams = useSearchParams(); const router = useRouter(); @@ -260,4 +269,4 @@ export default function AuthorizePage() { ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/dashboard/admin/services/[id]/page.tsx b/apps/web/src/app/dashboard/admin/services/[id]/page.tsx index 8d73305..a39672e 100644 --- a/apps/web/src/app/dashboard/admin/services/[id]/page.tsx +++ b/apps/web/src/app/dashboard/admin/services/[id]/page.tsx @@ -14,6 +14,12 @@ interface ServicePageProps { params: Promise<{ id: string }>; } +/** + * Renders the admin service detail page for the service identified by the route `id`. + * + * @param params - Promise resolving to an object with the route `id` string used to load the service + * @returns The React element for the service details view, including loading and error states and tabbed content + */ export default function ServicePage({ params }: ServicePageProps) { const { id } = use(params); const { data: response, isLoading, isError } = useAdminService(id); @@ -113,4 +119,4 @@ export default function ServicePage({ params }: ServicePageProps) { ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/dashboard/admin/users/page.tsx b/apps/web/src/app/dashboard/admin/users/page.tsx index c5ca8b6..ef0f55c 100644 --- a/apps/web/src/app/dashboard/admin/users/page.tsx +++ b/apps/web/src/app/dashboard/admin/users/page.tsx @@ -16,7 +16,7 @@ const PAGE_SIZE = 50; /** * Render the admin Users management page with a searchable, paginated list of users. * - * The component maintains local search and page state, debounces the search input, resets to page 1 when the debounced search changes, and fetches users with page-size pagination. The UI adapts to loading, error, empty, and populated states and provides controls for searching, clearing the search, retrying failed loads, and changing pages. + * The component maintains local `search` and `page` state, applies a debounced search input for data fetching, and displays appropriate UI for loading, error, empty, and populated states. It provides controls for searching, clearing the search, retrying failed loads, and changing pages. * * @returns The Users management page UI as a JSX element */ @@ -99,4 +99,4 @@ export default function UsersPage() { ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 99136ec..23b1808 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -39,10 +39,10 @@ export const viewport: Viewport = { }; /** - * Provide the application's root HTML structure, apply global font variables, and wrap page content with the QueryProvider. + * Render the application's root HTML structure, apply global font variables, and wrap page content with QueryProvider and AuthProvider. * * @param children - The application content to render inside the layout. - * @returns The `` element (lang="en") containing a `` whose children are wrapped by the QueryProvider. + * @returns The `` element (lang="en") whose `` applies the global font variables and contains `children` wrapped by `QueryProvider` and `AuthProvider`; includes a top-right `Toaster`. */ export default function RootLayout({ children }: { children: React.ReactNode }) { return ( @@ -57,4 +57,4 @@ export default function RootLayout({ children }: { children: React.ReactNode }) ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 910dad5..f90127b 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -4,6 +4,11 @@ import Footer from "@/authly/components/landing/Footer"; import Hero from "@/authly/components/landing/Hero"; import Navbar from "@/authly/components/landing/Navbar"; +/** + * Top-level home page component that composes the landing page layout. + * + * @returns The page's root JSX element containing Navbar, Hero, Features, CodeExamples, and Footer arranged vertically within a main container. + */ export default function Home() { return (
@@ -18,4 +23,4 @@ export default function Home() {
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx b/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx index 6c7d6b4..078ecc1 100644 --- a/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx +++ b/apps/web/src/components/dashboard/admin/services/OverviewTab.tsx @@ -10,6 +10,14 @@ interface OverviewTabProps { serviceId: string; } +/** + * Renders the overview tab for an admin service: loads service data, handles loading and error states, and presents an editable ServiceForm. + * + * Shows an error toast when the service fails to load. + * + * @param serviceId - The ID of the service to load and edit. + * @returns The OverviewTab React element. + */ export function OverviewTab({ serviceId }: OverviewTabProps) { const { data: response, isLoading, isError } = useAdminService(serviceId); const { mutate: updateService, isPending: isUpdating } = useUpdateService(); @@ -38,4 +46,4 @@ export function OverviewTab({ serviceId }: OverviewTabProps) { isUpdating={isUpdating} /> ); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx index 8f6ca7e..536e796 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/AuthenticationSection.tsx @@ -19,6 +19,21 @@ interface AuthenticationSectionProps { disabled?: boolean; } +/** + * Render the Authentication section of the client settings UI, showing and managing redirect URIs and allowed scopes. + * + * Renders an animated list of `redirectURIs` with optional remove buttons, an input and Add control for new redirect URIs (Enter key triggers add), and a TagInput for `allowedScopes`. + * + * @param redirectURIs - List of redirect URIs to display + * @param allowedScopes - Current allowed OAuth scopes + * @param newRedirectURI - Current value of the new redirect URI input + * @param onAllowedScopesChange - Called with the updated scopes when the TagInput changes + * @param onNewRedirectURIChange - Called with the input value when the new redirect URI changes + * @param onAddRedirect - Called to add the value from `newRedirectURI` to the list + * @param onRemoveRedirect - Called with the index of a redirect URI to remove + * @param disabled - When true, disables inputs and hides remove controls (default: false) + * @returns The React element for the Authentication section + */ export function AuthenticationSection({ redirectURIs, allowedScopes, @@ -108,4 +123,4 @@ export function AuthenticationSection({
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx index 9adee83..bae46be 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/BasicDetailsSection.tsx @@ -15,6 +15,18 @@ interface BasicDetailsSectionProps { disabled?: boolean; } +/** + * Render the Basic Details form section for editing a service's name, description, and primary domain. + * + * @param name - The current service name displayed in the Service Name input. + * @param description - The current service description displayed in the Description input. + * @param domain - The current primary domain displayed in the Primary Domain input. + * @param onNameChange - Callback invoked with the updated name when the Service Name input changes. + * @param onDescriptionChange - Callback invoked with the updated description when the Description input changes. + * @param onDomainChange - Callback invoked with the updated domain when the Primary Domain input changes. + * @param disabled - When `true`, disables all inputs (defaults to `false`). + * @returns A React element containing the titled section with three labeled inputs and associated hints/icons. + */ export function BasicDetailsSection({ name, description, @@ -61,4 +73,4 @@ export function BasicDetailsSection({ ); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx index a52eae4..d97e97e 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/CredentialsCard.tsx @@ -7,6 +7,15 @@ interface CredentialsCardProps { clientSecret: string | null; } +/** + * Render a credentials card showing the client ID and client secret with optional copy controls. + * + * The client ID is shown in plain text with a copy button. The client secret, when provided, is displayed masked and includes a copy button; when `null`, the card indicates the client is public. + * + * @param clientId - The client identifier to display and copy + * @param clientSecret - The client secret to display (masked) and copy, or `null` to indicate a public client + * @returns The JSX element representing the credentials card + */ export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps) { return (
@@ -45,4 +54,4 @@ export function CredentialsCard({ clientId, clientSecret }: CredentialsCardProps
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx index 0361ad7..f490f49 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/MetadataCard.tsx @@ -8,6 +8,16 @@ interface MetadataCardProps { serviceId: string; } +/** + * Render a compact metadata panel showing creation time, last update, and the service identifier with a copy control. + * + * Created and updated timestamps are parsed with the `Date` constructor and displayed using the runtime locale. + * + * @param createdAt - Timestamp string for when the resource was created; displayed as a localized date/time string + * @param updatedAt - Timestamp string for the last update; displayed as a localized date/time string + * @param serviceId - Service identifier shown in a truncated field with a button to copy the value + * @returns A React element that renders the metadata card UI + */ export function MetadataCard({ createdAt, updatedAt, serviceId }: MetadataCardProps) { return (
@@ -37,4 +47,4 @@ export function MetadataCard({ createdAt, updatedAt, serviceId }: MetadataCardPr
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx index 3d2584c..1f9f78f 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/SaveActions.tsx @@ -13,6 +13,15 @@ interface SaveActionsProps { disabled?: boolean; } +/** + * Render an action bar that displays an unsaved changes indicator and a save button. + * + * @param hasChanges - Whether there are unsaved changes to save + * @param isSaving - Whether a save operation is currently in progress + * @param onSave - Handler invoked when the save button is clicked (receives the form event) + * @param disabled - If true, disables the save button regardless of change or saving state + * @returns A JSX element containing the save actions bar + */ export function SaveActions({ hasChanges, isSaving, onSave, disabled = false }: SaveActionsProps) { return (
@@ -40,4 +49,4 @@ export function SaveActions({ hasChanges, isSaving, onSave, disabled = false }:
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx b/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx index 22e4984..9230010 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/ServiceForm.tsx @@ -23,6 +23,20 @@ interface ServiceFormProps { isUpdating: boolean; } +/** + * Renders a form for viewing and editing a service's details in the admin UI. + * + * The form initializes fields from `service`, allows adding/removing redirect URIs (with URL format and duplicate validation), + * and submits changes via the provided `updateService` mutation. It disables editing and prevents updates for the default Authly service + * and surfaces success and API error messages via toasts. + * + * @param service - The service object providing initial field values and metadata. + * @param serviceId - The identifier used when submitting updates for the service. + * @param updateService - Mutation function used to persist changes to the service. + * @param isUpdating - Whether an update request is in progress; disables save actions while true. + * + * @returns The React element for the service edit form. + */ export function ServiceForm({ service, serviceId, updateService, isUpdating }: ServiceFormProps) { const isDefaultService = isDefaultAuthlyService(service.id) || isDefaultAuthlyService(service.client_id); @@ -179,4 +193,4 @@ export function ServiceForm({ service, serviceId, updateService, isUpdating }: S ); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx index e7af411..7803065 100644 --- a/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx +++ b/apps/web/src/components/dashboard/admin/services/overview/StatusCard.tsx @@ -9,6 +9,14 @@ interface StatusCardProps { disabled?: boolean; } +/** + * Renders a status card with a label and a toggle switch for activating or deactivating a service. + * + * @param isActive - Whether the service is currently active + * @param onToggle - Callback invoked when the switch is toggled + * @param disabled - If true, disables user interaction with the switch + * @returns The rendered React element for the status card + */ export function StatusCard({ isActive, onToggle, disabled = false }: StatusCardProps) { return (
@@ -27,4 +35,4 @@ export function StatusCard({ isActive, onToggle, disabled = false }: StatusCardP
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/admin/users/EditUserModal.tsx b/apps/web/src/components/dashboard/admin/users/EditUserModal.tsx index 50df16d..27a96cb 100644 --- a/apps/web/src/components/dashboard/admin/users/EditUserModal.tsx +++ b/apps/web/src/components/dashboard/admin/users/EditUserModal.tsx @@ -17,15 +17,14 @@ interface EditUserModalProps { } /** - * Renders a modal containing a form to edit a user's username, email, and active status. + * Render a modal form for editing a user's username, email, and active status. * - * Validates input with the updateUserRequestSchema, displays field-level validation messages - * and server update errors, and submits changes via the useUpdateUser hook. Calls `onClose` - * when the update succeeds or when the Cancel button is clicked. + * Validates input, displays field-level and global update errors, submits changes, + * and closes the modal on successful update or when the Cancel button is clicked. * - * @param user - The user object to edit (pre-fills the form). - * @param onClose - Callback invoked to close the modal. - * @returns The modal content element for editing the user. + * @param user - The user to edit; used to pre-fill form fields + * @param onClose - Callback invoked to close the modal + * @returns The modal content element for editing the user */ export function EditUserModalContent({ user, onClose }: EditUserModalProps) { const { mutate: updateUser, isPending, error: updateError } = useUpdateUser(); @@ -129,4 +128,4 @@ export function EditUserModalContent({ user, onClose }: EditUserModalProps) {
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/ui/CopyButton.tsx b/apps/web/src/components/ui/CopyButton.tsx index ad1f142..4009920 100644 --- a/apps/web/src/components/ui/CopyButton.tsx +++ b/apps/web/src/components/ui/CopyButton.tsx @@ -12,6 +12,15 @@ interface CopyButtonProps { className?: string; } +/** + * Renders a button that copies a provided string to the clipboard and shows a success toast. + * + * @param value - The string to copy to the clipboard when the button is activated. + * @param ariaLabel - Accessible label used for the button and the success message. + * @param onCopy - Optional callback invoked after a successful copy. + * @param className - Optional CSS class(es) applied to the button element. + * @returns A button element that copies `value` when activated and updates its icon and aria-label to reflect the copied state. + */ export function CopyButton({ value, ariaLabel, onCopy, className }: CopyButtonProps) { const [copied, copy] = useCopyToClipboard(); @@ -35,4 +44,4 @@ export function CopyButton({ value, ariaLabel, onCopy, className }: CopyButtonPr /> ); -} +} \ No newline at end of file diff --git a/apps/web/src/components/ui/FormField.tsx b/apps/web/src/components/ui/FormField.tsx index 94d20e9..7960874 100644 --- a/apps/web/src/components/ui/FormField.tsx +++ b/apps/web/src/components/ui/FormField.tsx @@ -9,6 +9,16 @@ interface FormFieldProps { className?: string; } +/** + * Renders a labeled form field container with optional hint or error text. + * + * @param label - Optional label text displayed above the field. + * @param error - Optional error message; when present it is shown instead of the hint and styled as an error. + * @param hint - Optional helper text shown when `error` is not present. + * @param children - The field content (e.g., input, select, textarea). + * @param className - Additional CSS class names applied to the container. + * @returns A JSX element containing the composed form field. + */ export function FormField({ label, error, hint, children, className }: FormFieldProps) { return (
@@ -26,4 +36,4 @@ export function FormField({ label, error, hint, children, className }: FormField )}
); -} +} \ No newline at end of file diff --git a/apps/web/src/components/ui/TagInput.tsx b/apps/web/src/components/ui/TagInput.tsx index 0cbe21d..6c877a4 100644 --- a/apps/web/src/components/ui/TagInput.tsx +++ b/apps/web/src/components/ui/TagInput.tsx @@ -15,6 +15,20 @@ interface TagInputProps { "aria-label"?: string; } +/** + * Render a tag editor with inline removable tags and an input for adding new tags. + * + * Adds a trimmed tag when Enter, space, or comma is pressed (duplicates are ignored); when the input is empty, Backspace removes the last tag. + * + * @param value - Current list of tags + * @param onChange - Callback invoked with the updated tag list + * @param placeholder - Placeholder text shown only when there are no tags + * @param className - Additional CSS classes applied to the container + * @param disabled - When true, prevents adding/removing tags and styles the component as disabled + * @param error - When true, applies error styling + * @param aria-label - Accessibility label for the input element + * @returns The rendered React element for the tag input component + */ export function TagInput({ value, onChange, @@ -100,4 +114,4 @@ export function TagInput({ /> ); -} +} \ No newline at end of file diff --git a/apps/web/src/lib/hooks/admin/usePermissions.ts b/apps/web/src/lib/hooks/admin/usePermissions.ts index 4c9b708..06a8443 100644 --- a/apps/web/src/lib/hooks/admin/usePermissions.ts +++ b/apps/web/src/lib/hooks/admin/usePermissions.ts @@ -2,7 +2,11 @@ import { useQueryClient } from "@tanstack/react-query"; import { $api } from "@/authly/lib/api/client"; /** - * Hook to fetch permissions for a service. + * Fetches permission definitions for a given service, optionally filtered by resource and paginated. + * + * @param serviceId - The service identifier used as the `service_id` query parameter; the query is disabled when empty + * @param params - Optional query filters: `resource` to narrow by resource, `limit` and `offset` for pagination + * @returns The query result containing permissions that match the provided `service_id` and filters */ export function useServicePermissions( serviceId: string, @@ -26,7 +30,12 @@ export function useServicePermissions( } /** - * Hook to create a new permission definition. + * Creates a permission definition for a service and exposes a mutation hook. + * + * On successful creation, invalidates cached GET /admin/permissions queries scoped to the + * created permission's `service_id` and also invalidates the general permissions query to refresh lists. + * + * @returns The mutation result object for performing the create operation and observing its state. */ export function useCreatePermission() { const queryClient = useQueryClient(); @@ -47,7 +56,9 @@ export function useCreatePermission() { } /** - * Hook to update a permission definition. + * Updates an existing permission definition. + * + * @returns The mutation object for performing a PUT to `/admin/permissions/{id}`; on success it invalidates the cached permission list queries. */ export function useUpdatePermission() { const queryClient = useQueryClient(); @@ -60,7 +71,9 @@ export function useUpdatePermission() { } /** - * Hook to delete a permission definition. + * Provides a mutation hook for deleting an administrative permission definition. + * + * @returns A mutation result that deletes a permission by id; on success it invalidates the cached permission list (`["get", "/admin/permissions"]`) so list data is refreshed. */ export function useDeletePermission() { const queryClient = useQueryClient(); @@ -70,4 +83,4 @@ export function useDeletePermission() { queryClient.invalidateQueries({ queryKey: ["get", "/admin/permissions"] }); }, }); -} +} \ No newline at end of file diff --git a/apps/web/src/lib/hooks/admin/useRoles.ts b/apps/web/src/lib/hooks/admin/useRoles.ts index b8dc8bb..adc9702 100644 --- a/apps/web/src/lib/hooks/admin/useRoles.ts +++ b/apps/web/src/lib/hooks/admin/useRoles.ts @@ -2,7 +2,10 @@ import { useQueryClient } from "@tanstack/react-query"; import { $api } from "@/authly/lib/api/client"; /** - * Hook to fetch roles for a service. + * Fetches roles for the given service. + * + * @param serviceId - ID of the service used to filter roles + * @returns The query result containing the roles for the specified service */ export function useServiceRoles(serviceId: string) { return $api.useQuery( @@ -22,7 +25,9 @@ export function useServiceRoles(serviceId: string) { } /** - * Hook to create a new role. + * Creates a new role and exposes a mutation hook for performing that action. + * + * @returns A mutation hook that posts a new role; on success it invalidates the cached roles for the affected service and the overall roles list. */ export function useCreateRole() { const queryClient = useQueryClient(); @@ -38,7 +43,9 @@ export function useCreateRole() { } /** - * Hook to update a role. + * Creates a mutation hook for updating an existing admin role. + * + * @returns A mutation hook that updates a role at `/admin/roles/{id}` and invalidates the cached roles list (`["get", "/admin/roles"]`) on success. */ export function useUpdateRole() { const queryClient = useQueryClient(); @@ -51,7 +58,9 @@ export function useUpdateRole() { } /** - * Hook to delete a role. + * Deletes a role by id and refreshes the cached roles list when the deletion succeeds. + * + * @returns The mutation hook for deleting a role. Calling `mutate`/`mutateAsync` requires variables that include the role `id`. */ export function useDeleteRole() { const queryClient = useQueryClient(); @@ -61,4 +70,4 @@ export function useDeleteRole() { queryClient.invalidateQueries({ queryKey: ["get", "/admin/roles"] }); }, }); -} +} \ No newline at end of file diff --git a/apps/web/src/proxy.ts b/apps/web/src/proxy.ts index 4a2c5c2..05e9b91 100644 --- a/apps/web/src/proxy.ts +++ b/apps/web/src/proxy.ts @@ -6,6 +6,14 @@ const AUTH_PATHS = ["/auth/login", "/auth/register"]; import { OIDC_CONFIG } from "./lib/config"; +/** + * Enforces route-level authentication and performs OIDC redirect handling for incoming requests. + * + * For requests to protected paths, redirects unauthenticated users to the OIDC authorize endpoint with client, redirect, response type, and scope parameters. For authenticated requests to authentication pages, attempts to reconstruct and redirect to an authorize URL from an `oidc_params` query value; if reconstruction fails or `oidc_params` is absent, redirects to `/dashboard`. For all other requests, allows the request to continue unmodified. + * + * @param request - The incoming Next.js request to evaluate and possibly redirect. + * @returns A NextResponse that redirects to an OIDC authorize URL, redirects to `/dashboard`, or continues the request pipeline via `NextResponse.next()`. + */ export function proxy(request: NextRequest) { const { pathname } = request.nextUrl; const hasSession = request.cookies.has("session"); @@ -44,4 +52,4 @@ export function proxy(request: NextRequest) { export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico|images).*)"], -}; +}; \ No newline at end of file From bb61c27d1eadc234e5f2dfc734bb264689ca8080 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:53:07 +0100 Subject: [PATCH 51/53] fix(ServiceRow): add cursor pointer to CopyButton for better user interaction --- apps/web/src/components/dashboard/admin/services/ServiceRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx index 9ac24d0..cc72cc4 100644 --- a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx +++ b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx @@ -41,7 +41,7 @@ export function ServiceRow({ service }: ServiceRowProps) { From 2d8573e6056ec43f38b6fd1fadb1d2dada5cf0ba Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:54:45 +0100 Subject: [PATCH 52/53] fix(biome.json): update noBlankTarget security rule from 'off' to 'warn' for improved security practices --- apps/web/biome.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/biome.json b/apps/web/biome.json index b597b00..ad2f2c9 100644 --- a/apps/web/biome.json +++ b/apps/web/biome.json @@ -240,7 +240,7 @@ "useGoogleFontPreconnect": "warn" }, "security": { - "noBlankTarget": "off", + "noBlankTarget": "warn", "noDangerouslySetInnerHtmlWithChildren": "error" }, "style": { From 854794645bc44d0277df9266545ed61d60843946 Mon Sep 17 00:00:00 2001 From: Jakub <45879657+zeedivx@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:55:28 +0100 Subject: [PATCH 53/53] fix(ServiceRow): conditionally render CopyButton only when client ID is available to enhance UI clarity --- .../dashboard/admin/services/ServiceRow.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx index cc72cc4..35c95a8 100644 --- a/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx +++ b/apps/web/src/components/dashboard/admin/services/ServiceRow.tsx @@ -38,11 +38,13 @@ export function ServiceRow({ service }: ServiceRowProps) { {service.client_id || "No Client ID"} - + {service.client_id && ( + + )}