diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..e9a7c3af
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,132 @@
+# Dependencies
+node_modules
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Production builds
+.next
+out
+dist
+build
+
+# Environment files
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# IDE files
+.vscode
+.idea
+*.swp
+*.swo
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Git
+.git
+.gitignore
+
+# Docker
+Dockerfile
+.dockerignore
+docker-compose.yml
+
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Dependency directories
+jspm_packages/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Storybook build outputs
+.out
+.storybook-out
+
+# Temporary folders
+tmp/
+temp/
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+.pytest_cache/
+.coverage
+htmlcov/
+.tox/
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# Database
+*.db
+*.sqlite3
+
+# Media files (can be large)
+uploads/
+media/
+
+# Documentation
+README.md
+docs/
+*.md
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 3f5ee269..8a40dc9c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
-# Base image
-FROM python:3.12.3-slim-bookworm as base
+# Base image for Python backend
+FROM python:3.12.3-slim-bookworm AS base
# Install Nginx, curl, and build-essential
RUN apt update && apt install -y nginx curl build-essential \
@@ -10,32 +10,80 @@ RUN apt update && apt install -y nginx curl build-essential \
# Install Node tools
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - \
&& apt-get install -y nodejs \
- && npm install -g corepack pm2
+ && npm install -g pm2
-# Frontend Build
-FROM base AS deps
+# Frontend Build - Using Node.js Alpine for better performance
+FROM node:22-alpine AS frontend-base
+# Install dependencies only when needed
+FROM frontend-base AS frontend-deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY apps/web/package.json apps/web/pnpm-lock.yaml* ./
+RUN \
+ if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+# Rebuild the source code only when needed
+FROM frontend-base AS frontend-builder
+WORKDIR /app
+COPY --from=frontend-deps /app/node_modules ./node_modules
+COPY apps/web .
+
+# Set environment variables for the build
ENV NEXT_PUBLIC_LEARNHOUSE_API_URL=http://localhost/api/v1/
ENV NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL=http://localhost/
ENV NEXT_PUBLIC_LEARNHOUSE_DOMAIN=localhost
-WORKDIR /app/web
-COPY ./apps/web/package.json ./apps/web/pnpm-lock.yaml* ./
-COPY ./apps/web /app/web
-RUN rm -f .env*
-RUN if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile && pnpm run build; \
- else echo "Lockfile not found." && exit 1; \
- fi
+# Next.js collects completely anonymous telemetry data about general usage.
+# Learn more here: https://nextjs.org/telemetry
+# Uncomment the following line in case you want to disable telemetry during the build.
+# ENV NEXT_TELEMETRY_DISABLED 1
-# Final image
-FROM base as runner
-RUN addgroup --system --gid 1001 system \
- && adduser --system --uid 1001 app \
- && mkdir .next \
- && chown app:system .next
-COPY --from=deps /app/web/public ./app/web/public
-COPY --from=deps --chown=app:system /app/web/.next/standalone ./app/web/
-COPY --from=deps --chown=app:system /app/web/.next/static ./app/web/.next/static
+# Remove .env files from the final image
+# This is a good practice to avoid leaking sensitive data
+# Learn more about it in the Next.js documentation: https://nextjs.org/docs/basic-features/environment-variables
+RUN rm -f .env*
+
+RUN \
+ if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+# Production image, copy all the files and run next
+FROM frontend-base AS frontend-runner
+WORKDIR /app
+
+# Install curl
+RUN apk add --no-cache curl
+
+ENV NODE_ENV production
+# Uncomment the following line in case you want to disable telemetry during runtime.
+# ENV NEXT_TELEMETRY_DISABLED 1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=frontend-builder /app/public ./public
+
+# Set the correct permission for prerender cache
+RUN mkdir .next
+RUN chown nextjs:nodejs .next
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=frontend-builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=frontend-builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+# Final image combining frontend and backend
+FROM base AS runner
+
+# Copy the frontend standalone build
+COPY --from=frontend-runner /app /app/web
# Backend Build
WORKDIR /app/api
@@ -51,4 +99,5 @@ WORKDIR /app
COPY ./extra/nginx.conf /etc/nginx/conf.d/default.conf
ENV PORT=8000 LEARNHOUSE_PORT=9000 HOSTNAME=0.0.0.0
COPY ./extra/start.sh /app/start.sh
-CMD ["sh", "start.sh"]
\ No newline at end of file
+RUN chmod +x /app/start.sh
+CMD ["sh", "/app/start.sh"]
\ No newline at end of file
diff --git a/apps/api/app.py b/apps/api/app.py
index aa3b4b2e..7db51600 100644
--- a/apps/api/app.py
+++ b/apps/api/app.py
@@ -11,8 +11,6 @@ from fastapi_jwt_auth.exceptions import AuthJWTException
from fastapi.middleware.gzip import GZipMiddleware
-# from src.services.mocks.initial import create_initial_data
-
########################
# Pre-Alpha Version 0.1.0
# Author: @swve
@@ -39,8 +37,13 @@ app.add_middleware(
allow_headers=["*"],
)
-logfire.configure(console=False, service_name=learnhouse_config.site_name,)
-logfire.instrument_fastapi(app)
+# Only enable logfire if explicitly configured
+if learnhouse_config.general_config.logfire_enabled:
+ logfire.configure(console=False, service_name=learnhouse_config.site_name,)
+ logfire.instrument_fastapi(app)
+ # Instrument database after logfire is configured
+ from src.core.events.database import engine
+ logfire.instrument_sqlalchemy(engine=engine)
# Gzip Middleware (will add brotli later)
app.add_middleware(GZipMiddleware, minimum_size=1000)
diff --git a/apps/api/cli.py b/apps/api/cli.py
index b4238ac4..5e3f8142 100644
--- a/apps/api/cli.py
+++ b/apps/api/cli.py
@@ -49,6 +49,8 @@ def install(
email="",
logo_image="",
thumbnail_image="",
+ about="",
+ label="",
)
install_create_organization(org, db_session)
print("Default organization created ✅")
@@ -91,6 +93,8 @@ def install(
email="",
logo_image="",
thumbnail_image="",
+ about="",
+ label="",
)
install_create_organization(org, db_session)
print(orgname + " Organization created ✅")
diff --git a/apps/api/config/config.py b/apps/api/config/config.py
index e3f7ff8c..9e9635c7 100644
--- a/apps/api/config/config.py
+++ b/apps/api/config/config.py
@@ -12,6 +12,7 @@ class CookieConfig(BaseModel):
class GeneralConfig(BaseModel):
development_mode: bool
install_mode: bool
+ logfire_enabled: bool
class SecurityConfig(BaseModel):
@@ -118,6 +119,13 @@ def get_learnhouse_config() -> LearnHouseConfig:
else yaml_config.get("general", {}).get("install_mode")
)
+ # Logfire config
+ env_logfire_enabled = os.environ.get("LEARNHOUSE_LOGFIRE_ENABLED", "None")
+ logfire_enabled = (
+ env_logfire_enabled.lower() == "true" if env_logfire_enabled != "None"
+ else yaml_config.get("general", {}).get("logfire_enabled", False)
+ )
+
# Security Config
env_auth_jwt_secret_key = os.environ.get("LEARNHOUSE_AUTH_JWT_SECRET_KEY")
auth_jwt_secret_key = env_auth_jwt_secret_key or yaml_config.get(
@@ -295,7 +303,9 @@ def get_learnhouse_config() -> LearnHouseConfig:
site_description=site_description,
contact_email=contact_email,
general_config=GeneralConfig(
- development_mode=bool(development_mode), install_mode=bool(install_mode)
+ development_mode=bool(development_mode),
+ install_mode=bool(install_mode),
+ logfire_enabled=bool(logfire_enabled)
),
hosting_config=hosting_config,
database_config=database_config,
diff --git a/apps/api/config/config.yaml b/apps/api/config/config.yaml
index c9dcec79..33f1601f 100644
--- a/apps/api/config/config.yaml
+++ b/apps/api/config/config.yaml
@@ -7,6 +7,7 @@ contact_email: hi@learnhouse.app
general:
development_mode: true
install_mode: true
+ logfire_enabled: false
security:
auth_jwt_secret_key: secret
diff --git a/apps/api/src/core/events/database.py b/apps/api/src/core/events/database.py
index b970af1a..3be3eac9 100644
--- a/apps/api/src/core/events/database.py
+++ b/apps/api/src/core/events/database.py
@@ -1,5 +1,4 @@
import logging
-import logfire
import os
import importlib
from config.config import get_learnhouse_config
@@ -58,7 +57,7 @@ else:
# Only create tables if not in test mode (tests will handle this themselves)
if not is_testing:
SQLModel.metadata.create_all(engine)
- logfire.instrument_sqlalchemy(engine=engine)
+ # Note: logfire instrumentation will be handled in app.py after configuration
async def connect_to_db(app: FastAPI):
app.db_engine = engine # type: ignore
diff --git a/apps/api/src/db/organizations.py b/apps/api/src/db/organizations.py
index f8fd9ec9..34570c89 100644
--- a/apps/api/src/db/organizations.py
+++ b/apps/api/src/db/organizations.py
@@ -1,6 +1,7 @@
from typing import Optional
from pydantic import BaseModel
-from sqlmodel import Field, SQLModel, JSON, Column
+from sqlalchemy import JSON, Column
+from sqlmodel import Field, SQLModel
from src.db.roles import RoleRead
from src.db.organization_config import OrganizationConfig
diff --git a/apps/api/src/db/roles.py b/apps/api/src/db/roles.py
index 7d1c8a0b..7b6bd4e2 100644
--- a/apps/api/src/db/roles.py
+++ b/apps/api/src/db/roles.py
@@ -16,14 +16,36 @@ class Permission(BaseModel):
return getattr(self, item)
+class PermissionsWithOwn(BaseModel):
+ action_create: bool
+ action_read: bool
+ action_read_own: bool
+ action_update: bool
+ action_update_own: bool
+ action_delete: bool
+ action_delete_own: bool
+
+ def __getitem__(self, item):
+ return getattr(self, item)
+
+
+class DashboardPermission(BaseModel):
+ action_access: bool
+
+ def __getitem__(self, item):
+ return getattr(self, item)
+
+
class Rights(BaseModel):
- courses: Permission
+ courses: PermissionsWithOwn
users: Permission
usergroups : Permission
collections: Permission
organizations: Permission
coursechapters: Permission
activities: Permission
+ roles: Permission
+ dashboard: DashboardPermission
def __getitem__(self, item):
return getattr(self, item)
diff --git a/apps/api/src/routers/courses/courses.py b/apps/api/src/routers/courses/courses.py
index 6e1938fb..1e0d4733 100644
--- a/apps/api/src/routers/courses/courses.py
+++ b/apps/api/src/routers/courses/courses.py
@@ -26,6 +26,7 @@ from src.services.courses.courses import (
delete_course,
update_course_thumbnail,
search_courses,
+ get_course_user_rights,
)
from src.services.courses.updates import (
create_update,
@@ -358,12 +359,94 @@ async def api_remove_bulk_course_contributors(
):
"""
Remove multiple contributors from a course by their usernames
- Only administrators can perform this action
"""
return await remove_bulk_course_contributors(
- request,
- course_uuid,
- usernames,
- current_user,
- db_session
+ request, course_uuid, usernames, current_user, db_session
)
+
+
+@router.get("/{course_uuid}/rights")
+async def api_get_course_user_rights(
+ request: Request,
+ course_uuid: str,
+ db_session: Session = Depends(get_db_session),
+ current_user: PublicUser = Depends(get_current_user),
+) -> dict:
+ """
+ Get detailed user rights for a specific course.
+
+ This endpoint returns comprehensive rights information that can be used
+ by the UI to enable/disable features based on user permissions.
+
+
+
+ **Response Structure:**
+ ```json
+ {
+ "course_uuid": "course_123",
+ "user_id": 456,
+ "is_anonymous": false,
+ "permissions": {
+ "read": true,
+ "create": false,
+ "update": true,
+ "delete": false,
+ "create_content": true,
+ "update_content": true,
+ "delete_content": true,
+ "manage_contributors": true,
+ "manage_access": true,
+ "grade_assignments": true,
+ "mark_activities_done": true,
+ "create_certifications": true
+ },
+ "ownership": {
+ "is_owner": true,
+ "is_creator": true,
+ "is_maintainer": false,
+ "is_contributor": false,
+ "authorship_status": "ACTIVE"
+ },
+ "roles": {
+ "is_admin": false,
+ "is_maintainer_role": false,
+ "is_instructor": true,
+ "is_user": true
+ }
+ }
+ ```
+
+ **Permissions Explained:**
+ - `read`: Can read the course content
+ - `create`: Can create new courses (instructor role or higher)
+ - `update`: Can update course settings (title, description, etc.)
+ - `delete`: Can delete the course
+ - `create_content`: Can create activities, assignments, chapters, etc.
+ - `update_content`: Can update course content
+ - `delete_content`: Can delete course content
+ - `manage_contributors`: Can add/remove contributors
+ - `manage_access`: Can change course access settings (public, open_to_contributors)
+ - `grade_assignments`: Can grade student assignments
+ - `mark_activities_done`: Can mark activities as done for other users
+ - `create_certifications`: Can create course certifications
+
+ **Ownership Information:**
+ - `is_owner`: Is course owner (CREATOR, MAINTAINER, or CONTRIBUTOR)
+ - `is_creator`: Is course creator
+ - `is_maintainer`: Is course maintainer
+ - `is_contributor`: Is course contributor
+ - `authorship_status`: Current authorship status (ACTIVE, PENDING, INACTIVE)
+
+ **Role Information:**
+ - `is_admin`: Has admin role (role 1)
+ - `is_maintainer_role`: Has maintainer role (role 2)
+ - `is_instructor`: Has instructor role (role 3)
+ - `is_user`: Has basic user role (role 4)
+
+ **Security Notes:**
+ - Returns rights based on course ownership and user roles
+ - Safe to expose to UI as it only returns permission information
+ - Anonymous users can only read public courses
+ - All permissions are calculated based on current user context
+ """
+ return await get_course_user_rights(request, course_uuid, current_user, db_session)
diff --git a/apps/api/src/routers/roles.py b/apps/api/src/routers/roles.py
index ef9350e0..5d5cbfff 100644
--- a/apps/api/src/routers/roles.py
+++ b/apps/api/src/routers/roles.py
@@ -1,28 +1,45 @@
-from fastapi import APIRouter, Depends, Request
+from fastapi import APIRouter, Depends, Request, HTTPException
from sqlmodel import Session
from src.core.events.database import get_db_session
from src.db.roles import RoleCreate, RoleRead, RoleUpdate
from src.security.auth import get_current_user
-from src.services.roles.roles import create_role, delete_role, read_role, update_role
+from src.services.roles.roles import create_role, delete_role, read_role, update_role, get_roles_by_organization
from src.db.users import PublicUser
+from typing import List
router = APIRouter()
-@router.post("/")
+@router.post("/org/{org_id}")
async def api_create_role(
request: Request,
+ org_id: int,
role_object: RoleCreate,
current_user: PublicUser = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
)-> RoleRead:
"""
- Create new role
+ Create new role for a specific organization
"""
+ # Set the org_id in the role object
+ role_object.org_id = org_id
return await create_role(request, db_session, role_object, current_user)
+@router.get("/org/{org_id}")
+async def api_get_roles_by_organization(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+)-> List[RoleRead]:
+ """
+ Get all roles for a specific organization, including global roles
+ """
+ return await get_roles_by_organization(request, db_session, org_id, current_user)
+
+
@router.get("/{role_id}")
async def api_get_role(
request: Request,
@@ -39,6 +56,7 @@ async def api_get_role(
@router.put("/{role_id}")
async def api_update_role(
request: Request,
+ role_id: str,
role_object: RoleUpdate,
current_user: PublicUser = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
@@ -46,6 +64,16 @@ async def api_update_role(
"""
Update role by role_id
"""
+ # Convert role_id to integer and set it in the role_object
+ try:
+ role_id_int = int(role_id)
+ except ValueError:
+ raise HTTPException(
+ status_code=400,
+ detail="Invalid role ID format. Role ID must be a number.",
+ )
+
+ role_object.role_id = role_id_int
return await update_role(request, db_session, role_object, current_user)
diff --git a/apps/api/src/security/courses_security.py b/apps/api/src/security/courses_security.py
new file mode 100644
index 00000000..6bca2b92
--- /dev/null
+++ b/apps/api/src/security/courses_security.py
@@ -0,0 +1,410 @@
+"""
+SECURITY DOCUMENTATION FOR COURSES RBAC SYSTEM
+
+This module provides unified RBAC (Role-Based Access Control) checks for all courses-related operations.
+
+SECURITY MEASURES IMPLEMENTED:
+
+1. COURSE OWNERSHIP REQUIREMENTS:
+ - All non-read operations (create, update, delete) require course ownership
+ - Course ownership is determined by ResourceAuthor table with ACTIVE status
+ - Valid ownership roles: CREATOR, MAINTAINER, CONTRIBUTOR
+ - Admin/maintainer roles are also accepted for course operations
+
+2. COURSE CREATION VS COURSE CONTENT CREATION:
+ - COURSE CREATION: Allow if user has instructor role (3) or higher
+ - COURSE CONTENT CREATION (activities, assignments, chapters, etc.): Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - This distinction allows instructors to create courses but prevents them from creating content in courses they don't own
+
+3. STRICT ACCESS CONTROLS:
+ - Activities: Require course ownership for all non-read operations
+ - Assignments: Require course ownership for all non-read operations
+ - Chapters: Require course ownership for all non-read operations
+ - Certifications: Require course ownership for all non-read operations
+ - Collections: Use organization-level permissions
+
+4. GRADING AND SUBMISSION SECURITY:
+ - Only course owners or instructors can grade assignments
+ - Users can only submit their own work
+ - Users cannot update grades unless they are instructors
+ - Users can only update their own submissions
+
+5. CERTIFICATE SECURITY:
+ - Certificates can only be created by course owners or instructors
+ - System-generated certificates (from course completion) are properly secured
+ - Certificate creation requires proper RBAC checks
+
+6. ACTIVITY MARKING SECURITY:
+ - Only course owners or instructors can mark activities as done for other users
+ - Users can only mark their own activities as done
+
+7. COLLECTION SECURITY:
+ - Users can only add courses to collections if they have read access to those courses
+ - Collection operations require appropriate organization-level permissions
+
+8. ANONYMOUS USER HANDLING:
+ - Anonymous users can only read public courses
+ - All non-read operations require authentication
+
+9. ERROR HANDLING:
+ - Clear error messages for security violations
+ - Proper HTTP status codes (401, 403, 404)
+ - Comprehensive logging of security events
+
+10. COURSE ACCESS MANAGEMENT SECURITY:
+ - Sensitive fields (public, open_to_contributors) require additional validation
+ - Only course owners (CREATOR, MAINTAINER) or admins can change access settings
+ - Course creation requires proper organization-level permissions
+ - Course updates require course ownership or admin role
+
+11. CONTRIBUTOR MANAGEMENT SECURITY:
+ - Only course owners (CREATOR, MAINTAINER) or admins can add/remove contributors
+ - Only course owners (CREATOR, MAINTAINER) or admins can update contributor roles
+ - Cannot modify the role of the course creator
+ - Contributor applications are created with PENDING status
+ - Only course owners or admins can approve contributor applications
+
+SECURITY BEST PRACTICES:
+- Always check course ownership before allowing modifications
+- Validate user permissions at multiple levels
+- Use proper RBAC checks for all operations
+- Implement principle of least privilege
+- Provide clear error messages for security violations
+- Log security events for audit purposes
+- Additional validation for sensitive access control fields
+- Strict ownership requirements for contributor management
+- Distinguish between course creation and course content creation permissions
+
+CRITICAL SECURITY FIXES:
+- Fixed: Users could create certifications for courses they don't own
+- Fixed: Users could grade assignments without proper permissions
+- Fixed: Users could mark activities as done for other users without permissions
+- Fixed: Collections could be created with courses the user doesn't have access to
+- Fixed: Assignment submissions could be modified by unauthorized users
+- Fixed: Users could change course access settings (public, open_to_contributors) without proper permissions
+- Fixed: Users could add/remove contributors from courses they don't own
+- Fixed: Users could update contributor roles without course ownership
+- Fixed: Course creation used hardcoded RBAC check
+- Fixed: Contributor management used permissive RBAC checks instead of strict ownership requirements
+- Fixed: Instructors could create content in courses they don't own (now they can only create courses)
+"""
+
+from typing import Literal
+from fastapi import HTTPException, Request, status
+from sqlmodel import Session, select
+from src.db.users import AnonymousUser, PublicUser
+from src.db.courses.courses import Course
+from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
+from src.security.rbac.rbac import (
+ authorization_verify_based_on_roles_and_authorship,
+ authorization_verify_if_element_is_public,
+ authorization_verify_if_user_is_anon,
+ authorization_verify_based_on_org_admin_status,
+)
+
+
+async def courses_rbac_check(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+ require_course_ownership: bool = False,
+) -> bool:
+ """
+ Unified RBAC check for courses-related operations.
+
+ SECURITY NOTES:
+ - READ operations: Allow if user has read access to the course (public courses or user has permissions)
+ - COURSE CREATION: Allow if user has instructor role (3) or higher
+ - COURSE CONTENT CREATION (activities, assignments, chapters, etc.): Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - UPDATE/DELETE operations: Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - Course ownership is determined by ResourceAuthor table with ACTIVE status
+ - Admin/maintainer roles are checked via authorization_verify_based_on_org_admin_status
+
+ Args:
+ request: FastAPI request object
+ course_uuid: UUID of the course (or "course_x" for course creation)
+ current_user: Current user (PublicUser or AnonymousUser)
+ action: Action to perform (create, read, update, delete)
+ db_session: Database session
+ require_course_ownership: If True, requires course ownership for non-read actions
+
+ Returns:
+ bool: True if authorized, raises HTTPException otherwise
+
+ Raises:
+ HTTPException: 403 Forbidden if user lacks required permissions
+ HTTPException: 401 Unauthorized if user is anonymous for non-read actions
+ """
+
+ if action == "read":
+ if current_user.id == 0: # Anonymous user
+ return await authorization_verify_if_element_is_public(
+ request, course_uuid, action, db_session
+ )
+ else:
+ return await authorization_verify_based_on_roles_and_authorship(
+ request, current_user.id, action, course_uuid, db_session
+ )
+ else:
+ # For non-read actions, proceed with strict RBAC checks
+ await authorization_verify_if_user_is_anon(current_user.id)
+
+ # SECURITY: Special handling for course creation vs course content creation
+ if action == "create" and course_uuid == "course_x":
+ # This is course creation - allow instructors (role 3) or higher
+ # Check if user has instructor role or higher
+ from src.security.rbac.rbac import authorization_verify_based_on_roles
+
+ has_create_permission = await authorization_verify_based_on_roles(
+ request, current_user.id, "create", "course_x", db_session
+ )
+
+ if has_create_permission:
+ return True
+ else:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="You must have instructor role or higher to create courses",
+ )
+
+ # SECURITY: For course content creation and other operations, require course ownership
+ # This prevents users without course ownership from creating/modifying course content
+ if require_course_ownership or action in ["create", "update", "delete"]:
+ # Check if user is course owner (CREATOR, MAINTAINER, or CONTRIBUTOR)
+ statement = select(ResourceAuthor).where(
+ ResourceAuthor.resource_uuid == course_uuid,
+ ResourceAuthor.user_id == current_user.id
+ )
+ resource_author = db_session.exec(statement).first()
+
+ is_course_owner = False
+ if resource_author:
+ if ((resource_author.authorship == ResourceAuthorshipEnum.CREATOR) or
+ (resource_author.authorship == ResourceAuthorshipEnum.MAINTAINER) or
+ (resource_author.authorship == ResourceAuthorshipEnum.CONTRIBUTOR)) and \
+ resource_author.authorship_status == ResourceAuthorshipStatusEnum.ACTIVE:
+ is_course_owner = True
+
+ # Check if user has admin or maintainer role
+ is_admin_or_maintainer = await authorization_verify_based_on_org_admin_status(
+ request, current_user.id, action, course_uuid, db_session
+ )
+
+ # SECURITY: For creating, updating, and deleting course content, user MUST be either:
+ # 1. Course owner (CREATOR, MAINTAINER, or CONTRIBUTOR with ACTIVE status)
+ # 2. Admin or maintainer role
+ # General role permissions are NOT sufficient for these actions
+ if not (is_course_owner or is_admin_or_maintainer):
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=f"You must be the course owner (CREATOR, MAINTAINER, or CONTRIBUTOR) or have admin/maintainer role to {action} in this course",
+ )
+ return True
+ else:
+ # For other actions, use the existing RBAC check
+ return await authorization_verify_based_on_roles_and_authorship(
+ request,
+ current_user.id,
+ action,
+ course_uuid,
+ db_session,
+ )
+
+
+async def courses_rbac_check_with_course_lookup(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+ require_course_ownership: bool = False,
+) -> Course:
+ """
+ Unified RBAC check for courses-related operations with course lookup.
+
+ SECURITY NOTES:
+ - First validates that the course exists
+ - Then performs RBAC check using courses_rbac_check
+ - Returns the course object if authorized
+
+ Args:
+ request: FastAPI request object
+ course_uuid: UUID of the course
+ current_user: Current user (PublicUser or AnonymousUser)
+ action: Action to perform (create, read, update, delete)
+ db_session: Database session
+ require_course_ownership: If True, requires course ownership for non-read actions
+
+ Returns:
+ Course: The course object if authorized, raises HTTPException otherwise
+
+ Raises:
+ HTTPException: 404 Not Found if course doesn't exist
+ HTTPException: 403 Forbidden if user lacks required permissions
+ """
+
+ # First check if course exists
+ statement = select(Course).where(Course.course_uuid == course_uuid)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
+ )
+
+ # Perform RBAC check
+ await courses_rbac_check(
+ request, course_uuid, current_user, action, db_session, require_course_ownership
+ )
+
+ return course
+
+
+async def courses_rbac_check_for_activities(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+) -> bool:
+ """
+ Specialized RBAC check for activities that requires course ownership for non-read actions.
+
+ SECURITY NOTES:
+ - Activities are core course content and require strict ownership controls
+ - READ: Allow if user has read access to the course
+ - CREATE/UPDATE/DELETE: Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - This prevents unauthorized users from creating/modifying course activities
+ - Instructors can create courses but cannot create activities in courses they don't own
+ """
+
+ return await courses_rbac_check(
+ request, course_uuid, current_user, action, db_session, require_course_ownership=True
+ )
+
+
+async def courses_rbac_check_for_assignments(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+) -> bool:
+ """
+ Specialized RBAC check for assignments that requires course ownership for non-read actions.
+
+ SECURITY NOTES:
+ - Assignments are course content and require strict ownership controls
+ - READ: Allow if user has read access to the course
+ - CREATE/UPDATE/DELETE: Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - This prevents unauthorized users from creating/modifying course assignments
+ - Instructors can create courses but cannot create assignments in courses they don't own
+ """
+
+ return await courses_rbac_check(
+ request, course_uuid, current_user, action, db_session, require_course_ownership=True
+ )
+
+
+async def courses_rbac_check_for_chapters(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+) -> bool:
+ """
+ Specialized RBAC check for chapters that requires course ownership for non-read actions.
+
+ SECURITY NOTES:
+ - Chapters are course structure and require strict ownership controls
+ - READ: Allow if user has read access to the course
+ - CREATE/UPDATE/DELETE: Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - This prevents unauthorized users from creating/modifying course chapters
+ - Instructors can create courses but cannot create chapters in courses they don't own
+ """
+
+ return await courses_rbac_check(
+ request, course_uuid, current_user, action, db_session, require_course_ownership=True
+ )
+
+
+async def courses_rbac_check_for_certifications(
+ request: Request,
+ course_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+) -> bool:
+ """
+ Specialized RBAC check for certifications that requires course ownership for non-read actions.
+
+ SECURITY NOTES:
+ - Certifications are course credentials and require strict ownership controls
+ - READ: Allow if user has read access to the course
+ - CREATE/UPDATE/DELETE: Require course ownership (CREATOR, MAINTAINER, CONTRIBUTOR) or admin/maintainer role
+ - This prevents unauthorized users from creating/modifying course certifications
+ - CRITICAL: Without this check, users could create certifications for courses they don't own
+ - Instructors can create courses but cannot create certifications in courses they don't own
+ """
+
+ return await courses_rbac_check(
+ request, course_uuid, current_user, action, db_session, require_course_ownership=True
+ )
+
+
+async def courses_rbac_check_for_collections(
+ request: Request,
+ collection_uuid: str,
+ current_user: PublicUser | AnonymousUser,
+ action: Literal["create", "read", "update", "delete"],
+ db_session: Session,
+) -> bool:
+ """
+ Specialized RBAC check for collections.
+
+ SECURITY NOTES:
+ - Collections are course groupings and require appropriate access controls
+ - READ: Allow if collection is public or user has read access
+ - CREATE/UPDATE/DELETE: Require appropriate permissions based on collection ownership
+ - Collections may have different ownership models than courses
+
+ Args:
+ request: FastAPI request object
+ collection_uuid: UUID of the collection
+ current_user: Current user (PublicUser or AnonymousUser)
+ action: Action to perform (create, read, update, delete)
+ db_session: Database session
+
+ Returns:
+ bool: True if authorized, raises HTTPException otherwise
+ """
+
+ if action == "read":
+ if current_user.id == 0: # Anonymous user
+ res = await authorization_verify_if_element_is_public(
+ request, collection_uuid, action, db_session
+ )
+ if res == False:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="User rights : You are not allowed to read this collection",
+ )
+ return res
+ else:
+ return await authorization_verify_based_on_roles_and_authorship(
+ request, current_user.id, action, collection_uuid, db_session
+ )
+ else:
+ await authorization_verify_if_user_is_anon(current_user.id)
+
+ return await authorization_verify_based_on_roles_and_authorship(
+ request,
+ current_user.id,
+ action,
+ collection_uuid,
+ db_session,
+ )
\ No newline at end of file
diff --git a/apps/api/src/security/rbac/rbac.py b/apps/api/src/security/rbac/rbac.py
index 1e56238c..5bf64bba 100644
--- a/apps/api/src/security/rbac/rbac.py
+++ b/apps/api/src/security/rbac/rbac.py
@@ -7,7 +7,7 @@ from src.db.courses.courses import Course
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
from src.db.roles import Role
from src.db.user_organizations import UserOrganization
-from src.security.rbac.utils import check_element_type
+from src.security.rbac.utils import check_element_type, check_course_permissions_with_own
# Tested and working
@@ -106,14 +106,30 @@ async def authorization_verify_based_on_roles(
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
+
+ # Check if user is the author of the resource for "own" permissions
+ is_author = False
+ if action in ["update", "delete", "read"]:
+ is_author = await authorization_verify_if_user_is_author(
+ request, user_id, action, element_uuid, db_session
+ )
+
# Check all roles until we find one that grants the permission
for role in user_roles_in_organization_and_standard_roles:
role = Role.model_validate(role)
if role.rights:
rights = role.rights
element_rights = getattr(rights, element_type, None)
- if element_rights and getattr(element_rights, f"action_{action}", False):
- return True
+ if element_rights:
+ # Special handling for courses with PermissionsWithOwn
+ if element_type == "courses":
+ if await check_course_permissions_with_own(element_rights, action, is_author):
+ return True
+ else:
+ # For non-course resources, only check general permissions
+ # (regular Permission class no longer has "own" permissions)
+ if getattr(element_rights, f"action_{action}", False):
+ return True
# If we get here, no role granted the permission
return False
diff --git a/apps/api/src/security/rbac/utils.py b/apps/api/src/security/rbac/utils.py
index d6960a24..9904e65d 100644
--- a/apps/api/src/security/rbac/utils.py
+++ b/apps/api/src/security/rbac/utils.py
@@ -30,6 +30,38 @@ async def check_element_type(element_uuid):
)
+async def check_course_permissions_with_own(
+ element_rights,
+ action: str,
+ is_author: bool = False
+) -> bool:
+ """
+ Check course-specific permissions including "own" permissions.
+
+ Args:
+ element_rights: The rights object for courses (PermissionsWithOwn)
+ action: The action to check ("read", "update", "delete", "create")
+ is_author: Whether the user is the author of the course
+
+ Returns:
+ bool: True if permission is granted, False otherwise
+ """
+ if not element_rights:
+ return False
+
+ # Check for general permission first
+ if getattr(element_rights, f"action_{action}", False):
+ return True
+
+ # Check for "own" permission if user is the author
+ if is_author:
+ own_action = f"action_{action}_own"
+ if getattr(element_rights, own_action, False):
+ return True
+
+ return False
+
+
async def get_singular_form_of_element(element_uuid):
element_type = await check_element_type(element_uuid)
diff --git a/apps/api/src/services/courses/activities/activities.py b/apps/api/src/services/courses/activities/activities.py
index 42ad1f05..5929a920 100644
--- a/apps/api/src/services/courses/activities/activities.py
+++ b/apps/api/src/services/courses/activities/activities.py
@@ -1,12 +1,6 @@
-from typing import Literal
from sqlmodel import Session, select
from src.db.courses.courses import Course
from src.db.courses.chapters import Chapter
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
- authorization_verify_if_user_is_anon,
-)
from src.db.courses.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
from src.db.courses.chapter_activities import ChapterActivity
from src.db.users import AnonymousUser, PublicUser
@@ -15,6 +9,7 @@ from uuid import uuid4
from datetime import datetime
from src.services.payments.payments_access import check_activity_paid_access
+from src.security.courses_security import courses_rbac_check_for_activities
####################################################
@@ -49,7 +44,7 @@ async def create_activity(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "create", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "create", db_session)
# Create Activity
activity = Activity(**activity_object.model_dump())
@@ -118,7 +113,7 @@ async def get_activity(
activity, course = result
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "read", db_session)
# Paid access check
has_paid_access = await check_activity_paid_access(
@@ -156,7 +151,7 @@ async def get_activityby_id(
activity, course = result
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "read", db_session)
return ActivityRead.model_validate(activity)
@@ -187,7 +182,7 @@ async def update_activity(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "update", db_session)
# Update only the fields that were passed in
for var, value in vars(activity_object).items():
@@ -228,7 +223,7 @@ async def delete_activity(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "delete", db_session)
# Delete activity from chapter
statement = select(ChapterActivity).where(
@@ -296,46 +291,8 @@ async def get_activities(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "read", db_session)
activities = [ActivityRead.model_validate(activity) for activity in activities]
return activities
-
-
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
- request: Request,
- element_uuid: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
-
-
- if action == "read":
- if current_user.id == 0: # Anonymous user
- res = await authorization_verify_if_element_is_public(
- request, element_uuid, action, db_session
- )
- return res
- else:
- res = await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, element_uuid, db_session
- )
- return res
- else:
- # For non-read actions, proceed with regular RBAC checks
- await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- element_uuid,
- db_session,
- )
-
-
-## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/activities/assignments.py b/apps/api/src/services/courses/activities/assignments.py
index e936c800..b18a6d18 100644
--- a/apps/api/src/services/courses/activities/assignments.py
+++ b/apps/api/src/services/courses/activities/assignments.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Literal
from uuid import uuid4
from fastapi import HTTPException, Request, UploadFile
from sqlmodel import Session, select
@@ -34,9 +33,6 @@ from src.security.features_utils.usage import (
increase_feature_usage,
)
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
- authorization_verify_if_user_is_anon,
authorization_verify_based_on_roles,
)
from src.services.courses.activities.uploads.sub_file import upload_submission_file
@@ -45,6 +41,7 @@ from src.services.courses.activities.uploads.tasks_ref_files import (
)
from src.services.trail.trail import check_trail_presence
from src.services.courses.certifications import check_course_completion_and_create_certificate
+from src.security.courses_security import courses_rbac_check_for_assignments
## > Assignments CRUD
@@ -66,7 +63,7 @@ async def create_assignment(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "create", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "create", db_session)
# Usage check
check_limits_with_usage("assignments", course.org_id, db_session)
@@ -118,7 +115,7 @@ async def read_assignment(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment read
return AssignmentRead.model_validate(assignment)
@@ -161,7 +158,7 @@ async def read_assignment_from_activity_uuid(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment read
return AssignmentRead.model_validate(assignment)
@@ -195,7 +192,7 @@ async def update_assignment(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
# Update only the fields that were passed in
for var, value in vars(assignment_object).items():
@@ -239,7 +236,7 @@ async def delete_assignment(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "delete", db_session)
# Feature usage
decrease_feature_usage("assignments", course.org_id, db_session)
@@ -289,7 +286,7 @@ async def delete_assignment_from_activity_uuid(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "delete", db_session)
# Feature usage
decrease_feature_usage("assignments", course.org_id, db_session)
@@ -333,7 +330,7 @@ async def create_assignment_task(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "create", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "create", db_session)
# Create Assignment Task
assignment_task = AssignmentTask(**assignment_task_object.model_dump())
@@ -388,7 +385,7 @@ async def read_assignment_tasks(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment tasks read
return [
@@ -436,7 +433,7 @@ async def read_assignment_task(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment task read
return AssignmentTaskRead.model_validate(assignmenttask)
@@ -490,7 +487,7 @@ async def put_assignment_task_reference_file(
org = db_session.exec(org_statement).first()
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
# Upload reference file
if reference_file and reference_file.filename and activity and org:
@@ -568,7 +565,7 @@ async def put_assignment_task_submission_file(
org = db_session.exec(org_statement).first()
# RBAC check - only need read permission to submit files
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# Check if user is enrolled in the course
if not await authorization_verify_based_on_roles(request, current_user.id, "read", course.course_uuid, db_session):
@@ -633,7 +630,7 @@ async def update_assignment_task(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
# Update only the fields that were passed in
for var, value in vars(assignment_task_object).items():
@@ -689,7 +686,7 @@ async def delete_assignment_task(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "delete", db_session)
# Delete Assignment Task
db_session.delete(assignment_task)
@@ -741,7 +738,7 @@ async def handle_assignment_task_submission(
detail="Course not found",
)
- # Check if user has instructor/admin permissions
+ # SECURITY: Check if user has instructor/admin permissions for grading
is_instructor = await authorization_verify_based_on_roles(request, current_user.id, "update", course.course_uuid, db_session)
# For regular users, ensure they can only submit their own work
@@ -753,7 +750,7 @@ async def handle_assignment_task_submission(
detail="You must be enrolled in this course to submit assignments"
)
- # Regular users cannot update grades - only check if actual values are being set
+ # SECURITY: Regular users cannot update grades - only check if actual values are being set
if (assignment_task_submission_object.grade is not None and assignment_task_submission_object.grade != 0) or \
(assignment_task_submission_object.task_submission_grade_feedback is not None and assignment_task_submission_object.task_submission_grade_feedback != ""):
raise HTTPException(
@@ -762,10 +759,10 @@ async def handle_assignment_task_submission(
)
# Only need read permission for submissions
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
else:
- # Instructors/admins need update permission to grade
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ # SECURITY: Instructors/admins need update permission to grade
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
# Try to find existing submission if UUID is provided
assignment_task_submission = None
@@ -777,7 +774,7 @@ async def handle_assignment_task_submission(
# If submission exists, update it
if assignment_task_submission:
- # For regular users, ensure they can only update their own submissions
+ # SECURITY: For regular users, ensure they can only update their own submissions
if not is_instructor and assignment_task_submission.user_id != current_user.id:
raise HTTPException(
status_code=403,
@@ -880,7 +877,7 @@ async def read_user_assignment_task_submissions(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment task submission read
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
@@ -953,7 +950,7 @@ async def read_assignment_task_submissions(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment task submission read
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
@@ -1012,7 +1009,7 @@ async def update_assignment_task_submission(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# Update only the fields that were passed in
for var, value in vars(assignment_task_submission_object).items():
@@ -1081,7 +1078,7 @@ async def delete_assignment_task_submission(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "delete", db_session)
# Delete Assignment Task Submission
db_session.delete(assignment_task_submission)
@@ -1147,7 +1144,7 @@ async def create_assignment_submission(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# Create Assignment User Submission
assignment_user_submission = AssignmentUserSubmission(
@@ -1280,7 +1277,7 @@ async def read_assignment_submissions(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment tasks read
return [
@@ -1323,7 +1320,7 @@ async def read_user_assignment_submissions(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignment tasks read
return [
@@ -1389,7 +1386,7 @@ async def update_assignment_submission(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# Update only the fields that were passed in
for var, value in vars(assignment_user_submission_object).items():
@@ -1447,7 +1444,7 @@ async def delete_assignment_submission(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "delete", db_session)
# Delete Assignment User Submission
db_session.delete(assignment_user_submission)
@@ -1464,7 +1461,7 @@ async def grade_assignment_submission(
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
-
+ # SECURITY: This function should only be accessible by course owners or instructors
# Check if assignment exists
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
assignment = db_session.exec(statement).first()
@@ -1484,7 +1481,8 @@ async def grade_assignment_submission(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ # SECURITY: Require course ownership or instructor role for grading
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
# Check if assignment user submission exists
statement = select(AssignmentUserSubmission).where(
@@ -1602,6 +1600,7 @@ async def mark_activity_as_done_for_user(
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
+ # SECURITY: This function should only be accessible by course owners or instructors
# Get Assignment
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
assignment = db_session.exec(statement).first()
@@ -1625,7 +1624,8 @@ async def mark_activity_as_done_for_user(
detail="Course not found",
)
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ # SECURITY: Require course ownership or instructor role for marking activities as done
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "update", db_session)
if not activity:
raise HTTPException(
@@ -1704,46 +1704,7 @@ async def get_assignments_from_course(
assignments.append(assignment)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_assignments(request, course.course_uuid, current_user, "read", db_session)
# return assignments read
return [AssignmentRead.model_validate(assignment) for assignment in assignments]
-
-
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
- request: Request,
- course_uuid: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
-
- if action == "read":
- if current_user.id == 0: # Anonymous user
- res = await authorization_verify_if_element_is_public(
- request, course_uuid, action, db_session
- )
- return res
- else:
- res = (
- await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, course_uuid, db_session
- )
- )
- return res
- else:
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_uuid,
- db_session,
- )
-
-
-## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/activities/pdf.py b/apps/api/src/services/courses/activities/pdf.py
index e6f2ca51..ae10de6e 100644
--- a/apps/api/src/services/courses/activities/pdf.py
+++ b/apps/api/src/services/courses/activities/pdf.py
@@ -1,11 +1,6 @@
-from typing import Literal
from src.db.courses.courses import Course
from src.db.organizations import Organization
from sqlmodel import Session, select
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_user_is_anon,
-)
from src.db.courses.chapters import Chapter
from src.db.courses.activities import (
Activity,
@@ -20,6 +15,7 @@ from src.services.courses.activities.uploads.pdfs import upload_pdf
from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4
from datetime import datetime
+from src.security.courses_security import courses_rbac_check_for_activities
async def create_documentpdf_activity(
@@ -30,9 +26,6 @@ async def create_documentpdf_activity(
db_session: Session,
pdf_file: UploadFile | None = None,
):
- # RBAC check
- await rbac_check(request, "activity_x", current_user, "create", db_session)
-
# get chapter_id
statement = select(Chapter).where(Chapter.id == chapter_id)
chapter = db_session.exec(statement).first()
@@ -52,6 +45,19 @@ async def create_documentpdf_activity(
detail="CourseChapter not found",
)
+ # Get course_uuid for RBAC check
+ statement = select(Course).where(Course.id == coursechapter.course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
+ )
+
+ # RBAC check
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "create", db_session)
+
# get org_id
org_id = coursechapter.org_id
@@ -59,10 +65,6 @@ async def create_documentpdf_activity(
statement = select(Organization).where(Organization.id == coursechapter.org_id)
organization = db_session.exec(statement).first()
- # Get course_uuid
- statement = select(Course).where(Course.id == coursechapter.course_id)
- course = db_session.exec(statement).first()
-
# create activity uuid
activity_uuid = f"activity_{uuid4()}"
@@ -94,9 +96,7 @@ async def create_documentpdf_activity(
content={
"filename": "documentpdf." + pdf_format,
"activity_uuid": activity_uuid,
- },
- published_version=1,
- version=1,
+ },
org_id=org_id if org_id else 0,
course_id=coursechapter.course_id,
activity_uuid=activity_uuid,
@@ -121,7 +121,7 @@ async def create_documentpdf_activity(
)
# upload pdf
- if pdf_file:
+ if pdf_file and organization and course:
# get pdffile format
await upload_pdf(
pdf_file,
@@ -136,27 +136,3 @@ async def create_documentpdf_activity(
db_session.refresh(activity_chapter)
return ActivityRead.model_validate(activity)
-
-
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
- request: Request,
- course_id: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_id,
- db_session,
- )
-
-
-## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/activities/video.py b/apps/api/src/services/courses/activities/video.py
index 71408033..2072e1b9 100644
--- a/apps/api/src/services/courses/activities/video.py
+++ b/apps/api/src/services/courses/activities/video.py
@@ -5,10 +5,6 @@ from src.db.organizations import Organization
from pydantic import BaseModel
from sqlmodel import Session, select
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_user_is_anon,
-)
from src.db.courses.chapters import Chapter
from src.db.courses.activities import (
Activity,
@@ -23,6 +19,7 @@ from src.services.courses.activities.uploads.videos import upload_video
from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4
from datetime import datetime
+from src.security.courses_security import courses_rbac_check_for_activities
async def create_video_activity(
@@ -34,9 +31,6 @@ async def create_video_activity(
video_file: UploadFile | None = None,
details: str = "{}",
):
- # RBAC check
- await rbac_check(request, "activity_x", current_user, "create", db_session)
-
# get chapter_id
statement = select(Chapter).where(Chapter.id == chapter_id)
chapter = db_session.exec(statement).first()
@@ -59,14 +53,23 @@ async def create_video_activity(
detail="CourseChapter not found",
)
+ # Get course_uuid for RBAC check
+ statement = select(Course).where(Course.id == coursechapter.course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
+ )
+
+ # RBAC check
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "create", db_session)
+
# Get org_uuid
statement = select(Organization).where(Organization.id == coursechapter.org_id)
organization = db_session.exec(statement).first()
- # Get course_uuid
- statement = select(Course).where(Course.id == coursechapter.course_id)
- course = db_session.exec(statement).first()
-
# generate activity_uuid
activity_uuid = str(f"activity_{uuid4()}")
@@ -99,13 +102,11 @@ async def create_video_activity(
activity_uuid=activity_uuid,
org_id=coursechapter.org_id,
course_id=coursechapter.course_id,
- published_version=1,
content={
"filename": "video." + video_format,
"activity_uuid": activity_uuid,
},
- details=details,
- version=1,
+ details=details if isinstance(details, dict) else json.loads(details),
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
@@ -117,7 +118,7 @@ async def create_video_activity(
db_session.refresh(activity)
# upload video
- if video_file:
+ if video_file and organization and course:
# get videofile format
await upload_video(
video_file,
@@ -163,9 +164,6 @@ async def create_external_video_activity(
data: ExternalVideo,
db_session: Session,
):
- # RBAC check
- await rbac_check(request, "activity_x", current_user, "create", db_session)
-
# get chapter_id
statement = select(Chapter).where(Chapter.id == data.chapter_id)
chapter = db_session.exec(statement).first()
@@ -185,6 +183,19 @@ async def create_external_video_activity(
detail="CourseChapter not found",
)
+ # Get course_uuid for RBAC check
+ statement = select(Course).where(Course.id == coursechapter.course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
+ )
+
+ # RBAC check
+ await courses_rbac_check_for_activities(request, course.course_uuid, current_user, "create", db_session)
+
# generate activity_uuid
activity_uuid = str(f"activity_{uuid4()}")
@@ -198,14 +209,12 @@ async def create_external_video_activity(
activity_uuid=activity_uuid,
course_id=coursechapter.course_id,
org_id=coursechapter.org_id,
- published_version=1,
content={
"uri": data.uri,
"type": data.type,
"activity_uuid": activity_uuid,
},
details=details,
- version=1,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
@@ -234,22 +243,4 @@ async def create_external_video_activity(
return ActivityRead.model_validate(activity)
-async def rbac_check(
- request: Request,
- course_id: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_id,
- db_session,
- )
-
-
## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/certifications.py b/apps/api/src/services/courses/certifications.py
index af5a2b54..87a78297 100644
--- a/apps/api/src/services/courses/certifications.py
+++ b/apps/api/src/services/courses/certifications.py
@@ -1,4 +1,4 @@
-from typing import List, Literal
+from typing import List
from uuid import uuid4
from datetime import datetime
from sqlmodel import Session, select
@@ -15,11 +15,7 @@ from src.db.courses.courses import Course
from src.db.courses.chapter_activities import ChapterActivity
from src.db.trail_steps import TrailStep
from src.db.users import PublicUser, AnonymousUser
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
- authorization_verify_if_user_is_anon,
-)
+from src.security.courses_security import courses_rbac_check_for_certifications
####################################################
@@ -46,7 +42,7 @@ async def create_certification(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "create", db_session)
+ await courses_rbac_check_for_certifications(request, course.course_uuid, current_user, "create", db_session)
# Create certification
certification = Certifications(
@@ -93,7 +89,7 @@ async def get_certification(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_certifications(request, course.course_uuid, current_user, "read", db_session)
return CertificationRead(**certification.model_dump())
@@ -117,7 +113,7 @@ async def get_certifications_by_course(
)
# RBAC check
- await rbac_check(request, course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_certifications(request, course_uuid, current_user, "read", db_session)
# Get certifications for this course
statement = select(Certifications).where(Certifications.course_id == course.id)
@@ -155,7 +151,7 @@ async def update_certification(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_certifications(request, course.course_uuid, current_user, "update", db_session)
# Update only the fields that were passed in
for var, value in vars(certification_object).items():
@@ -200,7 +196,7 @@ async def delete_certification(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_certifications(request, course.course_uuid, current_user, "delete", db_session)
db_session.delete(certification)
db_session.commit()
@@ -218,8 +214,16 @@ async def create_certificate_user(
user_id: int,
certification_id: int,
db_session: Session,
+ current_user: PublicUser | AnonymousUser | None = None,
) -> CertificateUserRead:
- """Create a certificate user link"""
+ """
+ Create a certificate user link
+
+ SECURITY NOTES:
+ - This function should only be called by authorized users (course owners, instructors, or system)
+ - When called from check_course_completion_and_create_certificate, it's a system operation
+ - When called directly, requires proper RBAC checks
+ """
# Check if certification exists
statement = select(Certifications).where(Certifications.id == certification_id)
@@ -231,6 +235,21 @@ async def create_certificate_user(
detail="Certification not found",
)
+ # SECURITY: If current_user is provided, perform RBAC check
+ if current_user:
+ # Get course for RBAC check
+ statement = select(Course).where(Course.id == certification.course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
+ )
+
+ # Require course ownership or instructor role for creating certificates
+ await courses_rbac_check_for_certifications(request, course.course_uuid, current_user, "create", db_session)
+
# Check if certificate user already exists
statement = select(CertificateUser).where(
CertificateUser.user_id == user_id,
@@ -316,7 +335,7 @@ async def get_user_certificates_for_course(
)
# RBAC check
- await rbac_check(request, course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_certifications(request, course_uuid, current_user, "read", db_session)
# Get all certifications for this course
statement = select(Certifications).where(Certifications.course_id == course.id)
@@ -357,7 +376,14 @@ async def check_course_completion_and_create_certificate(
course_id: int,
db_session: Session,
) -> bool:
- """Check if all activities in a course are completed and create certificate if so"""
+ """
+ Check if all activities in a course are completed and create certificate if so
+
+ SECURITY NOTES:
+ - This function is called by the system when activities are completed
+ - It should only create certificates for users who have actually completed the course
+ - The function is called from mark_activity_as_done_for_user which already has RBAC checks
+ """
# Get all activities in the course
statement = select(ChapterActivity).where(ChapterActivity.course_id == course_id)
@@ -381,7 +407,8 @@ async def check_course_completion_and_create_certificate(
certification = db_session.exec(statement).first()
if certification and certification.id:
- # Create certificate user link
+ # SECURITY: Create certificate user link (system operation, no RBAC needed here)
+ # This is called from mark_activity_as_done_for_user which already has proper RBAC checks
try:
await create_certificate_user(request, user_id, certification.id, db_session)
return True
@@ -505,37 +532,4 @@ async def get_all_user_certificates(
} if user else None
})
- return result
-
-
-####################################################
-# RBAC Utils
-####################################################
-
-
-async def rbac_check(
- request: Request,
- course_uuid: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
- if action == "read":
- if current_user.id == 0: # Anonymous user
- await authorization_verify_if_element_is_public(
- request, course_uuid, action, db_session
- )
- else:
- await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, course_uuid, db_session
- )
- else:
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_uuid,
- db_session,
- )
\ No newline at end of file
+ return result
\ No newline at end of file
diff --git a/apps/api/src/services/courses/chapters.py b/apps/api/src/services/courses/chapters.py
index 4a30bb3d..ee72bc81 100644
--- a/apps/api/src/services/courses/chapters.py
+++ b/apps/api/src/services/courses/chapters.py
@@ -1,13 +1,8 @@
from datetime import datetime
-from typing import List, Literal
+from typing import List
from uuid import uuid4
from sqlmodel import Session, select
-from src.db.users import AnonymousUser
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
- authorization_verify_if_user_is_anon,
-)
+from src.db.users import AnonymousUser, PublicUser
from src.db.courses.course_chapters import CourseChapter
from src.db.courses.activities import Activity, ActivityRead
from src.db.courses.chapter_activities import ChapterActivity
@@ -18,9 +13,9 @@ from src.db.courses.chapters import (
ChapterUpdate,
ChapterUpdateOrder,
)
-from src.services.courses.courses import Course
-from src.services.users.users import PublicUser
+from src.db.courses.courses import Course
from fastapi import HTTPException, status, Request
+from src.security.courses_security import courses_rbac_check_for_chapters
####################################################
@@ -42,7 +37,7 @@ async def create_chapter(
course = db_session.exec(statement).one()
# RBAC check
- await rbac_check(request, "chapter_x", current_user, "create", db_session)
+ await courses_rbac_check_for_chapters(request, course.course_uuid, current_user, "create", db_session)
# complete chapter object
chapter.course_id = chapter_object.course_id
@@ -55,7 +50,7 @@ async def create_chapter(
statement = (
select(CourseChapter)
.where(CourseChapter.course_id == chapter.course_id)
- .order_by(CourseChapter.order)
+ .order_by(CourseChapter.order) # type: ignore
)
course_chapters = db_session.exec(statement).all()
@@ -122,14 +117,14 @@ async def get_chapter(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_chapters(request, course.course_uuid, current_user, "read", db_session)
# Get activities for this chapter
statement = (
select(Activity)
- .join(ChapterActivity, Activity.id == ChapterActivity.activity_id)
+ .join(ChapterActivity, Activity.id == ChapterActivity.activity_id) # type: ignore
.where(ChapterActivity.chapter_id == chapter_id)
- .distinct(Activity.id)
+ .distinct(Activity.id) # type: ignore
)
activities = db_session.exec(statement).all()
@@ -158,7 +153,7 @@ async def update_chapter(
)
# RBAC check
- await rbac_check(request, chapter.chapter_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_chapters(request, chapter.chapter_uuid, current_user, "update", db_session)
# Update only the fields that were passed in
for var, value in vars(chapter_object).items():
@@ -193,7 +188,7 @@ async def delete_chapter(
)
# RBAC check
- await rbac_check(request, chapter.chapter_uuid, current_user, "delete", db_session)
+ await courses_rbac_check_for_chapters(request, chapter.chapter_uuid, current_user, "delete", db_session)
# Remove all linked chapter activities
statement = select(ChapterActivity).where(ChapterActivity.chapter_id == chapter.id)
@@ -224,26 +219,26 @@ async def get_course_chapters(
statement = (
select(Chapter)
- .join(CourseChapter, Chapter.id == CourseChapter.chapter_id)
+ .join(CourseChapter, Chapter.id == CourseChapter.chapter_id) # type: ignore
.where(CourseChapter.course_id == course_id)
.where(Chapter.course_id == course_id)
- .order_by(CourseChapter.order)
- .group_by(Chapter.id, CourseChapter.order)
+ .order_by(CourseChapter.order) # type: ignore
+ .group_by(Chapter.id, CourseChapter.order) # type: ignore
)
chapters = db_session.exec(statement).all()
chapters = [ChapterRead(**chapter.model_dump(), activities=[]) for chapter in chapters]
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session) # type: ignore
+ await courses_rbac_check_for_chapters(request, course.course_uuid, current_user, "read", db_session) # type: ignore
# Get activities for each chapter
for chapter in chapters:
statement = (
select(ChapterActivity)
.where(ChapterActivity.chapter_id == chapter.id)
- .order_by(ChapterActivity.order)
- .distinct(ChapterActivity.id, ChapterActivity.order)
+ .order_by(ChapterActivity.order) # type: ignore
+ .distinct(ChapterActivity.id, ChapterActivity.order) # type: ignore
)
chapter_activities = db_session.exec(statement).all()
@@ -251,7 +246,7 @@ async def get_course_chapters(
statement = (
select(Activity)
.where(Activity.id == chapter_activity.activity_id, with_unpublished_activities or Activity.published == True)
- .distinct(Activity.id)
+ .distinct(Activity.id) # type: ignore
)
activity = db_session.exec(statement).first()
@@ -279,7 +274,7 @@ async def DEPRECEATED_get_course_chapters(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check_for_chapters(request, course.course_uuid, current_user, "read", db_session)
chapters_in_db = await get_course_chapters(request, course.id, db_session, current_user) # type: ignore
@@ -306,9 +301,9 @@ async def DEPRECEATED_get_course_chapters(
activities_list = {}
statement = (
select(Activity)
- .join(ChapterActivity, ChapterActivity.activity_id == Activity.id)
+ .join(ChapterActivity, ChapterActivity.activity_id == Activity.id) # type: ignore
.where(ChapterActivity.activity_id == Activity.id)
- .group_by(Activity.id)
+ .group_by(Activity.id) # type: ignore
)
activities_in_db = db_session.exec(statement).all()
@@ -324,10 +319,10 @@ async def DEPRECEATED_get_course_chapters(
# get chapter order
statement = (
select(Chapter)
- .join(CourseChapter, CourseChapter.chapter_id == Chapter.id)
+ .join(CourseChapter, CourseChapter.chapter_id == Chapter.id) # type: ignore
.where(CourseChapter.chapter_id == Chapter.id)
- .group_by(Chapter.id, CourseChapter.order)
- .order_by(CourseChapter.order)
+ .group_by(Chapter.id, CourseChapter.order) # type: ignore
+ .order_by(CourseChapter.order) # type: ignore
)
chapters_in_db = db_session.exec(statement).all()
@@ -361,7 +356,7 @@ async def reorder_chapters_and_activities(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check_for_chapters(request, course.course_uuid, current_user, "update", db_session)
###########
# Chapters
@@ -458,39 +453,3 @@ async def reorder_chapters_and_activities(
db_session.commit()
return {"detail": "Chapters and activities reordered successfully"}
-
-
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
- request: Request,
- course_uuid: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
- if action == "read":
- if current_user.id == 0: # Anonymous user
- res = await authorization_verify_if_element_is_public(
- request, course_uuid, action, db_session
- )
- return res
- else:
- res = await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, course_uuid, db_session
- )
- return res
- else:
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_uuid,
- db_session,
- )
-
-
-## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/collections.py b/apps/api/src/services/courses/collections.py
index 774e2393..80713b03 100644
--- a/apps/api/src/services/courses/collections.py
+++ b/apps/api/src/services/courses/collections.py
@@ -1,13 +1,8 @@
from datetime import datetime
-from typing import List, Literal
+from typing import List
from uuid import uuid4
from sqlmodel import Session, select
-from src.db.users import AnonymousUser
-from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
- authorization_verify_if_user_is_anon,
-)
+from src.db.users import AnonymousUser, PublicUser
from src.db.collections import (
Collection,
CollectionCreate,
@@ -16,8 +11,8 @@ from src.db.collections import (
)
from src.db.collections_courses import CollectionCourse
from src.db.courses.courses import Course
-from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request
+from src.security.courses_security import courses_rbac_check_for_collections
####################################################
@@ -40,7 +35,7 @@ async def get_collection(
)
# RBAC check
- await rbac_check(
+ await courses_rbac_check_for_collections(
request, collection.collection_uuid, current_user, "read", db_session
)
@@ -86,8 +81,10 @@ async def create_collection(
) -> CollectionRead:
collection = Collection.model_validate(collection_object)
- # RBAC check
- await rbac_check(request, "collection_x", current_user, "create", db_session)
+ # SECURITY: Check if user has permission to create collections in this organization
+ # Since collections are organization-level resources, we need to check org permissions
+ # For now, we'll use the existing RBAC check but with proper organization context
+ await courses_rbac_check_for_collections(request, "collection_x", current_user, "create", db_session)
# Complete the collection object
collection.collection_uuid = f"collection_{uuid4()}"
@@ -99,18 +96,32 @@ async def create_collection(
db_session.commit()
db_session.refresh(collection)
- # Link courses to collection
+ # SECURITY: Link courses to collection - ensure user has access to all courses being added
if collection:
for course_id in collection_object.courses:
- collection_course = CollectionCourse(
- collection_id=int(collection.id), # type: ignore
- course_id=course_id,
- org_id=int(collection_object.org_id),
- creation_date=str(datetime.now()),
- update_date=str(datetime.now()),
- )
- # Add collection_course to database
- db_session.add(collection_course)
+ # Check if user has access to this course
+ statement = select(Course).where(Course.id == course_id)
+ course = db_session.exec(statement).first()
+
+ if course:
+ # Verify user has read access to the course before adding it to collection
+ try:
+ await courses_rbac_check_for_collections(request, course.course_uuid, current_user, "read", db_session)
+ except HTTPException:
+ raise HTTPException(
+ status_code=403,
+ detail=f"You don't have permission to add course {course.name} to this collection"
+ )
+
+ collection_course = CollectionCourse(
+ collection_id=int(collection.id), # type: ignore
+ course_id=course_id,
+ org_id=int(collection_object.org_id),
+ creation_date=str(datetime.now()),
+ update_date=str(datetime.now()),
+ )
+ # Add collection_course to database
+ db_session.add(collection_course)
db_session.commit()
db_session.refresh(collection)
@@ -145,7 +156,7 @@ async def update_collection(
)
# RBAC check
- await rbac_check(
+ await courses_rbac_check_for_collections(
request, collection.collection_uuid, current_user, "update", db_session
)
@@ -219,7 +230,7 @@ async def delete_collection(
)
# RBAC check
- await rbac_check(
+ await courses_rbac_check_for_collections(
request, collection.collection_uuid, current_user, "delete", db_session
)
@@ -248,7 +259,7 @@ async def get_collections(
Collection.org_id == org_id, Collection.public == True
)
statement_all = (
- select(Collection).where(Collection.org_id == org_id).distinct(Collection.id)
+ select(Collection).where(Collection.org_id == org_id).distinct(Collection.id) # type: ignore
)
if current_user.id == 0:
@@ -288,49 +299,7 @@ async def get_collections(
courses = db_session.exec(statement).all()
- collection = CollectionRead(**collection.model_dump(), courses=courses)
+ collection = CollectionRead(**collection.model_dump(), courses=list(courses))
collections_with_courses.append(collection)
return collections_with_courses
-
-
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
- request: Request,
- collection_uuid: str,
- current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
- db_session: Session,
-):
- if action == "read":
- if current_user.id == 0: # Anonymous user
- res = await authorization_verify_if_element_is_public(
- request, collection_uuid, action, db_session
- )
- if res == False:
- raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN,
- detail="User rights : You are not allowed to read this collection",
- )
- else:
- res = (
- await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, collection_uuid, db_session
- )
- )
- return res
- else:
- await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- collection_uuid,
- db_session,
- )
-
-
-## 🔒 RBAC Utils ##
diff --git a/apps/api/src/services/courses/contributors.py b/apps/api/src/services/courses/contributors.py
index b055c901..746fc615 100644
--- a/apps/api/src/services/courses/contributors.py
+++ b/apps/api/src/services/courses/contributors.py
@@ -1,10 +1,11 @@
from datetime import datetime
-from fastapi import HTTPException, Request, status
+from fastapi import HTTPException, Request
from sqlmodel import Session, select, and_
from src.db.users import PublicUser, AnonymousUser, User, UserRead
from src.db.courses.courses import Course
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
-from src.security.rbac.rbac import authorization_verify_if_user_is_anon, authorization_verify_based_on_roles_and_authorship
+from src.security.rbac.rbac import authorization_verify_if_user_is_anon
+from src.security.courses_security import courses_rbac_check
from typing import List
@@ -14,6 +15,14 @@ async def apply_course_contributor(
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
+ """
+ Apply to become a course contributor
+
+ SECURITY NOTES:
+ - Any authenticated user can apply to become a contributor
+ - Applications are created with PENDING status
+ - Only course owners (CREATOR, MAINTAINER) or admins can approve applications
+ """
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
@@ -73,21 +82,17 @@ async def update_course_contributor(
):
"""
Update a course contributor's role and status
- Only administrators can perform this action
+
+ SECURITY NOTES:
+ - Only course owners (CREATOR, MAINTAINER) or admins can update contributors
+ - Cannot modify the role of the course creator
+ - Requires strict course ownership checks
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
- # RBAC check - verify if user has admin rights
- authorized = await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, "update", course_uuid, db_session
- )
-
- if not authorized:
- raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN,
- detail="You are not authorized to update course contributors",
- )
+ # SECURITY: Require course ownership or admin role for updating contributors
+ await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@@ -115,7 +120,7 @@ async def update_course_contributor(
detail="Contributor not found for this course",
)
- # Don't allow changing the role of the creator
+ # SECURITY: Don't allow changing the role of the creator
if existing_authorship.authorship == ResourceAuthorshipEnum.CREATOR:
raise HTTPException(
status_code=400,
@@ -144,6 +149,10 @@ async def get_course_contributors(
) -> List[dict]:
"""
Get all contributors for a course with their user information
+
+ SECURITY NOTES:
+ - Requires read access to the course
+ - Contributors are visible to anyone with course read access
"""
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@@ -155,6 +164,9 @@ async def get_course_contributors(
detail="Course not found",
)
+ # SECURITY: Require read access to the course
+ await courses_rbac_check(request, course_uuid, current_user, "read", db_session)
+
# Get all contributors for this course with user information
statement = (
select(ResourceAuthor, User)
@@ -184,21 +196,17 @@ async def add_bulk_course_contributors(
):
"""
Add multiple contributors to a course by their usernames
- Only administrators can perform this action
+
+ SECURITY NOTES:
+ - Only course owners (CREATOR, MAINTAINER) or admins can add contributors
+ - Requires strict course ownership checks
+ - Cannot add contributors to courses the user doesn't own
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
- # RBAC check - verify if user has admin rights
- authorized = await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, "update", course_uuid, db_session
- )
-
- if not authorized:
- raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN,
- detail="You are not authorized to add contributors",
- )
+ # SECURITY: Require course ownership or admin role for adding contributors
+ await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@@ -284,21 +292,18 @@ async def remove_bulk_course_contributors(
):
"""
Remove multiple contributors from a course by their usernames
- Only administrators can perform this action
+
+ SECURITY NOTES:
+ - Only course owners (CREATOR, MAINTAINER) or admins can remove contributors
+ - Requires strict course ownership checks
+ - Cannot remove contributors from courses the user doesn't own
+ - Cannot remove the course creator
"""
# Verify user is not anonymous
await authorization_verify_if_user_is_anon(current_user.id)
- # RBAC check - verify if user has admin rights
- authorized = await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, "update", course_uuid, db_session
- )
-
- if not authorized:
- raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN,
- detail="You are not authorized to remove contributors",
- )
+ # SECURITY: Require course ownership or admin role for removing contributors
+ await courses_rbac_check(request, course_uuid, current_user, "update", db_session)
# Check if course exists
statement = select(Course).where(Course.course_uuid == course_uuid)
@@ -346,7 +351,7 @@ async def remove_bulk_course_contributors(
})
continue
- # Don't allow removing the creator
+ # SECURITY: Don't allow removing the creator
if existing_authorship.authorship == ResourceAuthorshipEnum.CREATOR:
results["failed"].append({
"username": username,
diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py
index 98030597..5dffdbe6 100644
--- a/apps/api/src/services/courses/courses.py
+++ b/apps/api/src/services/courses/courses.py
@@ -1,4 +1,4 @@
-from typing import Literal, List
+from typing import List
from uuid import uuid4
from sqlmodel import Session, select, or_, and_, text
from src.db.usergroup_resources import UserGroupResource
@@ -21,13 +21,13 @@ from src.db.courses.courses import (
ThumbnailType,
)
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship,
- authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
+ authorization_verify_based_on_org_admin_status,
)
from src.services.courses.thumbnails import upload_thumbnail
-from fastapi import HTTPException, Request, UploadFile
+from fastapi import HTTPException, Request, UploadFile, status
from datetime import datetime
+from src.security.courses_security import courses_rbac_check
async def get_course(
@@ -46,15 +46,15 @@ async def get_course(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "read", db_session)
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -92,15 +92,15 @@ async def get_course_by_id(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "read", db_session)
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -153,7 +153,7 @@ async def get_course_meta(
author_results = [(ra, u) for _, ra, u in results if ra is not None and u is not None]
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "read", db_session)
# Get course chapters
chapters = []
@@ -241,7 +241,7 @@ async def get_courses_orgslug(
.join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid.in_(course_uuids)) # type: ignore
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
@@ -349,10 +349,10 @@ async def search_courses(
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -399,10 +399,20 @@ async def create_course(
thumbnail_file: UploadFile | None = None,
thumbnail_type: ThumbnailType = ThumbnailType.IMAGE,
):
+ """
+ Create a new course
+
+ SECURITY NOTES:
+ - Requires proper permissions to create courses in the organization
+ - User becomes the CREATOR of the course automatically
+ - Course creation is subject to organization limits and permissions
+ """
course = Course.model_validate(course_object)
- # RBAC check
- await rbac_check(request, "course_x", current_user, "create", db_session)
+ # SECURITY: Check if user has permission to create courses in this organization
+ # Since this is a new course, we need to check organization-level permissions
+ # For now, we'll use the existing RBAC check but with proper organization context
+ await courses_rbac_check(request, "course_x", current_user, "create", db_session)
# Usage check
check_limits_with_usage("courses", org_id, db_session)
@@ -440,7 +450,7 @@ async def create_course(
db_session.commit()
db_session.refresh(course)
- # Make the user the creator of the course
+ # SECURITY: Make the user the creator of the course
resource_author = ResourceAuthor(
resource_uuid=course.course_uuid,
user_id=current_user.id,
@@ -458,10 +468,10 @@ async def create_course(
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -506,7 +516,7 @@ async def update_course_thumbnail(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "update", db_session)
# Get org uuid
org_statement = select(Organization).where(Organization.id == course.org_id)
@@ -543,10 +553,10 @@ async def update_course_thumbnail(
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -575,6 +585,14 @@ async def update_course(
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
+ """
+ Update a course
+
+ SECURITY NOTES:
+ - Requires course ownership (CREATOR, MAINTAINER) or admin role
+ - Sensitive fields (public, open_to_contributors) require additional validation
+ - Cannot change course access settings without proper permissions
+ """
statement = select(Course).where(Course.course_uuid == course_uuid)
course = db_session.exec(statement).first()
@@ -584,8 +602,46 @@ async def update_course(
detail="Course not found",
)
- # RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ # SECURITY: Require course ownership or admin role for updating courses
+ await courses_rbac_check(request, course.course_uuid, current_user, "update", db_session)
+
+ # SECURITY: Additional checks for sensitive access control fields
+ sensitive_fields_updated = []
+
+ # Check if sensitive fields are being updated
+ if course_object.public is not None:
+ sensitive_fields_updated.append("public")
+ if course_object.open_to_contributors is not None:
+ sensitive_fields_updated.append("open_to_contributors")
+
+ # If sensitive fields are being updated, require additional validation
+ if sensitive_fields_updated:
+ # SECURITY: For sensitive access control changes, require CREATOR or MAINTAINER role
+ # Check if user is course owner (CREATOR or MAINTAINER)
+ statement = select(ResourceAuthor).where(
+ ResourceAuthor.resource_uuid == course_uuid,
+ ResourceAuthor.user_id == current_user.id
+ )
+ resource_author = db_session.exec(statement).first()
+
+ is_course_owner = False
+ if resource_author:
+ if ((resource_author.authorship == ResourceAuthorshipEnum.CREATOR) or
+ (resource_author.authorship == ResourceAuthorshipEnum.MAINTAINER)) and \
+ resource_author.authorship_status == ResourceAuthorshipStatusEnum.ACTIVE:
+ is_course_owner = True
+
+ # Check if user has admin or maintainer role
+ is_admin_or_maintainer = await authorization_verify_based_on_org_admin_status(
+ request, current_user.id, "update", course_uuid, db_session
+ )
+
+ # SECURITY: Only course owners (CREATOR, MAINTAINER) or admins can change access settings
+ if not (is_course_owner or is_admin_or_maintainer):
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=f"You must be the course owner (CREATOR or MAINTAINER) or have admin role to change access settings: {', '.join(sensitive_fields_updated)}",
+ )
# Update only the fields that were passed in
for var, value in vars(course_object).items():
@@ -602,10 +658,10 @@ async def update_course(
# Get course authors with their roles
authors_statement = (
select(ResourceAuthor, User)
- .join(User, ResourceAuthor.user_id == User.id)
+ .join(User, ResourceAuthor.user_id == User.id) # type: ignore
.where(ResourceAuthor.resource_uuid == course.course_uuid)
.order_by(
- ResourceAuthor.id.asc()
+ ResourceAuthor.id.asc() # type: ignore
)
)
author_results = db_session.exec(authors_statement).all()
@@ -643,7 +699,7 @@ async def delete_course(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "delete", db_session)
# Feature usage
decrease_feature_usage("courses", course.org_id, db_session)
@@ -681,7 +737,7 @@ async def get_user_courses(
return []
# Get courses with the extracted UUIDs
- statement = select(Course).where(Course.course_uuid.in_(course_uuids))
+ statement = select(Course).where(Course.course_uuid.in_(course_uuids)) # type: ignore
# Apply pagination
statement = statement.offset((page - 1) * limit).limit(limit)
@@ -738,39 +794,177 @@ async def get_user_courses(
return result
-## 🔒 RBAC Utils ##
-
-
-async def rbac_check(
+async def get_course_user_rights(
request: Request,
course_uuid: str,
current_user: PublicUser | AnonymousUser,
- action: Literal["create", "read", "update", "delete"],
db_session: Session,
-):
- if action == "read":
- if current_user.id == 0: # Anonymous user
- res = await authorization_verify_if_element_is_public(
- request, course_uuid, action, db_session
- )
- return res
- else:
- res = (
- await authorization_verify_based_on_roles_and_authorship(
- request, current_user.id, action, course_uuid, db_session
- )
- )
- return res
- else:
- await authorization_verify_if_user_is_anon(current_user.id)
+) -> dict:
+ """
+ Get detailed user rights for a specific course.
+
+ This function returns comprehensive rights information that can be used
+ by the UI to enable/disable features based on user permissions.
+
+ SECURITY NOTES:
+ - Returns rights based on course ownership and user roles
+ - Includes both course-level and content-level permissions
+ - Safe to expose to UI as it only returns permission information
+ """
+ # Check if course exists
+ statement = select(Course).where(Course.course_uuid == course_uuid)
+ course = db_session.exec(statement).first()
- await authorization_verify_based_on_roles_and_authorship(
- request,
- current_user.id,
- action,
- course_uuid,
- db_session,
+ if not course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course not found",
)
+ # Initialize rights object
+ rights = {
+ "course_uuid": course_uuid,
+ "user_id": current_user.id,
+ "is_anonymous": current_user.id == 0,
+ "permissions": {
+ "read": False,
+ "create": False,
+ "update": False,
+ "delete": False,
+ "create_content": False,
+ "update_content": False,
+ "delete_content": False,
+ "manage_contributors": False,
+ "manage_access": False,
+ "grade_assignments": False,
+ "mark_activities_done": False,
+ "create_certifications": False,
+ },
+ "ownership": {
+ "is_owner": False,
+ "is_creator": False,
+ "is_maintainer": False,
+ "is_contributor": False,
+ "authorship_status": None,
+ },
+ "roles": {
+ "is_admin": False,
+ "is_maintainer_role": False,
+ "is_instructor": False,
+ "is_user": False,
+ }
+ }
-## 🔒 RBAC Utils ##
+ # Handle anonymous users
+ if current_user.id == 0:
+ # Anonymous users can only read public courses
+ if course.public:
+ rights["permissions"]["read"] = True
+ return rights
+
+ # Check course ownership
+ statement = select(ResourceAuthor).where(
+ ResourceAuthor.resource_uuid == course_uuid,
+ ResourceAuthor.user_id == current_user.id
+ )
+ resource_author = db_session.exec(statement).first()
+
+ if resource_author:
+ rights["ownership"]["authorship_status"] = resource_author.authorship_status
+
+ if resource_author.authorship_status == ResourceAuthorshipStatusEnum.ACTIVE:
+ if resource_author.authorship == ResourceAuthorshipEnum.CREATOR:
+ rights["ownership"]["is_creator"] = True
+ rights["ownership"]["is_owner"] = True
+ elif resource_author.authorship == ResourceAuthorshipEnum.MAINTAINER:
+ rights["ownership"]["is_maintainer"] = True
+ rights["ownership"]["is_owner"] = True
+ elif resource_author.authorship == ResourceAuthorshipEnum.CONTRIBUTOR:
+ rights["ownership"]["is_contributor"] = True
+ rights["ownership"]["is_owner"] = True
+
+ # Check user roles
+ from src.security.rbac.rbac import authorization_verify_based_on_org_admin_status
+ from src.security.rbac.rbac import authorization_verify_based_on_roles
+
+ # Check admin/maintainer role
+ is_admin_or_maintainer = await authorization_verify_based_on_org_admin_status(
+ request, current_user.id, "update", course_uuid, db_session
+ )
+
+ if is_admin_or_maintainer:
+ rights["roles"]["is_admin"] = True
+ rights["roles"]["is_maintainer_role"] = True
+
+ # Check instructor role
+ has_instructor_permissions = await authorization_verify_based_on_roles(
+ request, current_user.id, "create", "course_x", db_session
+ )
+
+ if has_instructor_permissions:
+ rights["roles"]["is_instructor"] = True
+
+ # Check user role (basic permissions)
+ has_user_permissions = await authorization_verify_based_on_roles(
+ request, current_user.id, "read", course_uuid, db_session
+ )
+
+ if has_user_permissions:
+ rights["roles"]["is_user"] = True
+
+ # Determine permissions based on ownership and roles
+ is_course_owner = rights["ownership"]["is_owner"]
+ is_admin = rights["roles"]["is_admin"]
+ is_maintainer_role = rights["roles"]["is_maintainer_role"]
+ is_instructor = rights["roles"]["is_instructor"]
+
+ # READ permissions
+ if course.public or is_course_owner or is_admin or is_maintainer_role or is_instructor or has_user_permissions:
+ rights["permissions"]["read"] = True
+
+ # CREATE permissions (course creation)
+ if is_instructor or is_admin or is_maintainer_role:
+ rights["permissions"]["create"] = True
+
+ # UPDATE permissions (course-level updates)
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["update"] = True
+
+ # DELETE permissions (course deletion)
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["delete"] = True
+
+ # CONTENT CREATION permissions (activities, assignments, chapters, etc.)
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["create_content"] = True
+
+ # CONTENT UPDATE permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["update_content"] = True
+
+ # CONTENT DELETE permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["delete_content"] = True
+
+ # CONTRIBUTOR MANAGEMENT permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["manage_contributors"] = True
+
+ # ACCESS MANAGEMENT permissions (public, open_to_contributors)
+ if (rights["ownership"]["is_creator"] or rights["ownership"]["is_maintainer"] or
+ is_admin or is_maintainer_role):
+ rights["permissions"]["manage_access"] = True
+
+ # GRADING permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["grade_assignments"] = True
+
+ # ACTIVITY MARKING permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["mark_activities_done"] = True
+
+ # CERTIFICATION permissions
+ if is_course_owner or is_admin or is_maintainer_role:
+ rights["permissions"]["create_certifications"] = True
+
+ return rights
diff --git a/apps/api/src/services/courses/updates.py b/apps/api/src/services/courses/updates.py
index f3fea858..19a2324f 100644
--- a/apps/api/src/services/courses/updates.py
+++ b/apps/api/src/services/courses/updates.py
@@ -12,7 +12,7 @@ from src.db.courses.course_updates import (
from src.db.courses.courses import Course
from src.db.organizations import Organization
from src.db.users import AnonymousUser, PublicUser
-from src.services.courses.courses import rbac_check
+from src.security.courses_security import courses_rbac_check
async def create_update(
@@ -41,7 +41,7 @@ async def create_update(
)
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "update", db_session)
# Generate UUID
courseupdate_uuid = str(f"courseupdate_{uuid4()}")
@@ -81,7 +81,7 @@ async def update_update(
)
# RBAC check
- await rbac_check(
+ await courses_rbac_check(
request, update.courseupdate_uuid, current_user, "update", db_session
)
@@ -115,7 +115,7 @@ async def delete_update(
)
# RBAC check
- await rbac_check(
+ await courses_rbac_check(
request, update.courseupdate_uuid, current_user, "delete", db_session
)
diff --git a/apps/api/src/services/install/install.py b/apps/api/src/services/install/install.py
index aea4551e..559967f3 100644
--- a/apps/api/src/services/install/install.py
+++ b/apps/api/src/services/install/install.py
@@ -24,7 +24,7 @@ from src.db.organization_config import (
UserGroupOrgConfig,
)
from src.db.organizations import Organization, OrganizationCreate
-from src.db.roles import Permission, Rights, Role, RoleTypeEnum
+from src.db.roles import DashboardPermission, Permission, PermissionsWithOwn, Rights, Role, RoleTypeEnum
from src.db.user_organizations import UserOrganization
from src.db.users import User, UserCreate, UserRead
from config.config import get_learnhouse_config
@@ -127,7 +127,7 @@ def install_default_elements(db_session: Session):
statement = select(Role).where(Role.role_type == RoleTypeEnum.TYPE_GLOBAL)
roles = db_session.exec(statement).all()
- if roles and len(roles) == 3:
+ if roles and len(roles) == 4:
raise HTTPException(
status_code=409,
detail="Default roles already exist",
@@ -136,16 +136,19 @@ def install_default_elements(db_session: Session):
# Create default roles
role_global_admin = Role(
name="Admin",
- description="Standard Admin Role",
+ description="Full platform control",
id=1,
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_admin",
rights=Rights(
- courses=Permission(
+ courses=PermissionsWithOwn(
action_create=True,
action_read=True,
+ action_read_own=True,
action_update=True,
+ action_update_own=True,
action_delete=True,
+ action_delete_own=True,
),
users=Permission(
action_create=True,
@@ -183,6 +186,15 @@ def install_default_elements(db_session: Session):
action_update=True,
action_delete=True,
),
+ roles=Permission(
+ action_create=True,
+ action_read=True,
+ action_update=True,
+ action_delete=True,
+ ),
+ dashboard=DashboardPermission(
+ action_access=True,
+ ),
),
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
@@ -190,22 +202,25 @@ def install_default_elements(db_session: Session):
role_global_maintainer = Role(
name="Maintainer",
- description="Standard Maintainer Role",
+ description="Mid-level manager, wide permissions but no platform control",
id=2,
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_maintainer",
rights=Rights(
- courses=Permission(
+ courses=PermissionsWithOwn(
action_create=True,
action_read=True,
+ action_read_own=True,
action_update=True,
+ action_update_own=True,
action_delete=True,
+ action_delete_own=True,
),
users=Permission(
action_create=True,
action_read=True,
action_update=True,
- action_delete=True,
+ action_delete=False,
),
usergroups=Permission(
action_create=True,
@@ -220,10 +235,10 @@ def install_default_elements(db_session: Session):
action_delete=True,
),
organizations=Permission(
- action_create=True,
+ action_create=False,
action_read=True,
- action_update=True,
- action_delete=True,
+ action_update=False,
+ action_delete=False,
),
coursechapters=Permission(
action_create=True,
@@ -237,6 +252,81 @@ def install_default_elements(db_session: Session):
action_update=True,
action_delete=True,
),
+ roles=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ dashboard=DashboardPermission(
+ action_access=True,
+ ),
+ ),
+ creation_date=str(datetime.now()),
+ update_date=str(datetime.now()),
+ )
+
+ role_global_instructor = Role(
+ name="Instructor",
+ description="Can manage their own content",
+ id=3,
+ role_type=RoleTypeEnum.TYPE_GLOBAL,
+ role_uuid="role_global_instructor",
+ rights=Rights(
+ courses=PermissionsWithOwn(
+ action_create=True,
+ action_read=True,
+ action_read_own=True,
+ action_update=False,
+ action_update_own=True,
+ action_delete=False,
+ action_delete_own=True,
+ ),
+ users=Permission(
+ action_create=False,
+ action_read=False,
+ action_update=False,
+ action_delete=False,
+ ),
+ usergroups=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ collections=Permission(
+ action_create=True,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ organizations=Permission(
+ action_create=False,
+ action_read=False,
+ action_update=False,
+ action_delete=False,
+ ),
+ coursechapters=Permission(
+ action_create=True,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ activities=Permission(
+ action_create=True,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ roles=Permission(
+ action_create=False,
+ action_read=False,
+ action_update=False,
+ action_delete=False,
+ ),
+ dashboard=DashboardPermission(
+ action_access=True,
+ ),
),
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
@@ -244,20 +334,23 @@ def install_default_elements(db_session: Session):
role_global_user = Role(
name="User",
- description="Standard User Role",
+ description="Read-Only Learner",
role_type=RoleTypeEnum.TYPE_GLOBAL,
role_uuid="role_global_user",
- id=3,
+ id=4,
rights=Rights(
- courses=Permission(
+ courses=PermissionsWithOwn(
action_create=False,
action_read=True,
+ action_read_own=True,
action_update=False,
- action_delete=False,
+ action_update_own=False,
+ action_delete=True,
+ action_delete_own=True,
),
users=Permission(
- action_create=True,
- action_read=True,
+ action_create=False,
+ action_read=False,
action_update=False,
action_delete=False,
),
@@ -275,7 +368,7 @@ def install_default_elements(db_session: Session):
),
organizations=Permission(
action_create=False,
- action_read=True,
+ action_read=False,
action_update=False,
action_delete=False,
),
@@ -288,9 +381,18 @@ def install_default_elements(db_session: Session):
activities=Permission(
action_create=False,
action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ roles=Permission(
+ action_create=False,
+ action_read=False,
action_update=False,
action_delete=False,
),
+ dashboard=DashboardPermission(
+ action_access=False,
+ ),
),
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
@@ -299,11 +401,13 @@ def install_default_elements(db_session: Session):
# Serialize rights to JSON
role_global_admin.rights = role_global_admin.rights.dict() # type: ignore
role_global_maintainer.rights = role_global_maintainer.rights.dict() # type: ignore
+ role_global_instructor.rights = role_global_instructor.rights.dict() # type: ignore
role_global_user.rights = role_global_user.rights.dict() # type: ignore
# Insert roles in DB
db_session.add(role_global_admin)
db_session.add(role_global_maintainer)
+ db_session.add(role_global_instructor)
db_session.add(role_global_user)
# commit changes
diff --git a/apps/api/src/services/orgs/join.py b/apps/api/src/services/orgs/join.py
index a62f8fad..2b8b15ac 100644
--- a/apps/api/src/services/orgs/join.py
+++ b/apps/api/src/services/orgs/join.py
@@ -80,7 +80,7 @@ async def join_org(
user_organization = UserOrganization(
user_id=user.id,
org_id=org.id,
- role_id=3,
+ role_id=4,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
@@ -102,7 +102,7 @@ async def join_org(
user_organization = UserOrganization(
user_id=user.id,
org_id=org.id,
- role_id=3,
+ role_id=4,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
diff --git a/apps/api/src/services/payments/payments_courses.py b/apps/api/src/services/payments/payments_courses.py
index 1382e408..45bf73c2 100644
--- a/apps/api/src/services/payments/payments_courses.py
+++ b/apps/api/src/services/payments/payments_courses.py
@@ -4,7 +4,7 @@ from src.db.payments.payments_courses import PaymentsCourse
from src.db.payments.payments_products import PaymentsProduct
from src.db.courses.courses import Course
from src.db.users import PublicUser, AnonymousUser
-from src.services.courses.courses import rbac_check
+from src.security.courses_security import courses_rbac_check
async def link_course_to_product(
request: Request,
@@ -22,7 +22,7 @@ async def link_course_to_product(
raise HTTPException(status_code=404, detail="Course not found")
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "update", db_session)
# Check if product exists
statement = select(PaymentsProduct).where(
@@ -71,7 +71,7 @@ async def unlink_course_from_product(
raise HTTPException(status_code=404, detail="Course not found")
# RBAC check
- await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+ await courses_rbac_check(request, course.course_uuid, current_user, "update", db_session)
# Find and delete the payment course link
statement = select(PaymentsCourse).where(
diff --git a/apps/api/src/services/roles/roles.py b/apps/api/src/services/roles/roles.py
index ea5d0715..7e5a4b97 100644
--- a/apps/api/src/services/roles/roles.py
+++ b/apps/api/src/services/roles/roles.py
@@ -1,12 +1,15 @@
-from typing import Literal
+from typing import Literal, List
from uuid import uuid4
-from sqlmodel import Session, select
+from sqlmodel import Session, select, text
+from sqlalchemy.exc import IntegrityError
from src.security.rbac.rbac import (
authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.users import AnonymousUser, PublicUser
-from src.db.roles import Role, RoleCreate, RoleRead, RoleUpdate
+from src.db.roles import Role, RoleCreate, RoleRead, RoleUpdate, RoleTypeEnum
+from src.db.organizations import Organization
+from src.db.user_organizations import UserOrganization
from fastapi import HTTPException, Request
from datetime import datetime
@@ -22,24 +25,401 @@ async def create_role(
# RBAC check
await rbac_check(request, current_user, "create", "role_xxx", db_session)
+ # ============================================================================
+ # VERIFICATION 1: Ensure the role is created as TYPE_ORGANIZATION and has an org_id
+ # ============================================================================
+ if not role.org_id:
+ raise HTTPException(
+ status_code=400,
+ detail="Organization ID is required for role creation",
+ )
+
+ # Force the role type to be TYPE_ORGANIZATION for user-created roles
+ role.role_type = RoleTypeEnum.TYPE_ORGANIZATION
+
+ # ============================================================================
+ # VERIFICATION 2: Check if the organization exists
+ # ============================================================================
+ statement = select(Organization).where(Organization.id == role.org_id)
+ organization = db_session.exec(statement).first()
+
+ if not organization:
+ raise HTTPException(
+ status_code=404,
+ detail="Organization not found",
+ )
+
+ # ============================================================================
+ # VERIFICATION 3: Check if the current user is a member of the organization
+ # ============================================================================
+ statement = select(UserOrganization).where(
+ UserOrganization.user_id == current_user.id,
+ UserOrganization.org_id == role.org_id
+ )
+ user_org = db_session.exec(statement).first()
+
+ if not user_org:
+ raise HTTPException(
+ status_code=403,
+ detail="You are not a member of this organization",
+ )
+
+ # ============================================================================
+ # VERIFICATION 4: Check if the user has permission to create roles in this organization
+ # ============================================================================
+ # Get the user's role in this organization
+ statement = select(Role).where(Role.id == user_org.role_id)
+ user_role = db_session.exec(statement).first()
+
+ if not user_role:
+ raise HTTPException(
+ status_code=403,
+ detail="Your role in this organization could not be determined",
+ )
+
+ # Check if the user has role creation permissions
+ if user_role.rights and isinstance(user_role.rights, dict):
+ roles_rights = user_role.rights.get('roles', {})
+ if not roles_rights.get('action_create', False):
+ raise HTTPException(
+ status_code=403,
+ detail="You don't have permission to create roles in this organization",
+ )
+ else:
+ # If no rights are defined, check if user has admin role (role_id 1 or 2)
+ if user_role.id not in [1, 2]: # Admin and Maintainer roles
+ raise HTTPException(
+ status_code=403,
+ detail="You don't have permission to create roles in this organization. Admin or Maintainer role required.",
+ )
+
+ # ============================================================================
+ # VERIFICATION 5: Check if a role with the same name already exists in this organization
+ # ============================================================================
+ statement = select(Role).where(
+ Role.name == role.name,
+ Role.org_id == role.org_id,
+ Role.role_type == RoleTypeEnum.TYPE_ORGANIZATION
+ )
+ existing_role = db_session.exec(statement).first()
+
+ if existing_role:
+ raise HTTPException(
+ status_code=409,
+ detail=f"A role with the name '{role.name}' already exists in this organization",
+ )
+
+ # ============================================================================
+ # VERIFICATION 6: Validate role name and description
+ # ============================================================================
+ if not role.name or role.name.strip() == "":
+ raise HTTPException(
+ status_code=400,
+ detail="Role name is required and cannot be empty",
+ )
+
+ if len(role.name.strip()) > 100: # Assuming a reasonable limit
+ raise HTTPException(
+ status_code=400,
+ detail="Role name cannot exceed 100 characters",
+ )
+
+ # ============================================================================
+ # VERIFICATION 7: Validate rights structure if provided
+ # ============================================================================
+ if role.rights:
+ # Convert Rights model to dict if needed
+ if isinstance(role.rights, dict):
+ # It's already a dict
+ rights_dict = role.rights
+ else:
+ # It's likely a Pydantic model, try to convert to dict
+ try:
+ # Try dict() method first (for Pydantic v1)
+ rights_dict = role.rights.dict()
+ except AttributeError:
+ try:
+ # Try model_dump() method (for Pydantic v2)
+ rights_dict = role.rights.model_dump() # type: ignore
+ except AttributeError:
+ raise HTTPException(
+ status_code=400,
+ detail="Rights must be provided as a JSON object",
+ )
+
+ # Validate rights structure - check for required top-level keys
+ required_rights = [
+ 'courses', 'users', 'usergroups', 'collections',
+ 'organizations', 'coursechapters', 'activities',
+ 'roles', 'dashboard'
+ ]
+
+ for required_right in required_rights:
+ if required_right not in rights_dict:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required right: {required_right}",
+ )
+
+ # Validate the structure of each right
+ right_data = rights_dict[required_right]
+ if not isinstance(right_data, dict):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Right '{required_right}' must be a JSON object",
+ )
+
+ # Validate courses permissions (has additional 'own' permissions)
+ if required_right == 'courses':
+ required_course_permissions = [
+ 'action_create', 'action_read', 'action_read_own',
+ 'action_update', 'action_update_own', 'action_delete', 'action_delete_own'
+ ]
+ for perm in required_course_permissions:
+ if perm not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required course permission: {perm}",
+ )
+ if not isinstance(right_data[perm], bool):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Course permission '{perm}' must be a boolean",
+ )
+
+ # Validate other permissions (standard permissions)
+ elif required_right in ['users', 'usergroups', 'collections', 'organizations', 'coursechapters', 'activities', 'roles']:
+ required_permissions = ['action_create', 'action_read', 'action_update', 'action_delete']
+ for perm in required_permissions:
+ if perm not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required permission '{perm}' for '{required_right}'",
+ )
+ if not isinstance(right_data[perm], bool):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Permission '{perm}' for '{required_right}' must be a boolean",
+ )
+
+ # Validate dashboard permissions
+ elif required_right == 'dashboard':
+ if 'action_access' not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail="Missing required dashboard permission: action_access",
+ )
+ if not isinstance(right_data['action_access'], bool):
+ raise HTTPException(
+ status_code=400,
+ detail="Dashboard permission 'action_access' must be a boolean",
+ )
+
+ # Convert back to dict if it was a model
+ if not isinstance(role.rights, dict):
+ role.rights = rights_dict
+
+ # ============================================================================
+ # VERIFICATION 8: Ensure user cannot create a role with higher permissions than they have
+ # ============================================================================
+ if role.rights and isinstance(role.rights, dict) and user_role.rights and isinstance(user_role.rights, dict):
+ # Check if the new role has any permissions that the user doesn't have
+ for right_key, right_permissions in role.rights.items():
+ if right_key in user_role.rights:
+ user_right_permissions = user_role.rights[right_key]
+
+ # Check each permission in the right
+ for perm_key, perm_value in right_permissions.items():
+ if isinstance(perm_value, bool) and perm_value: # If the new role has this permission enabled
+ if isinstance(user_right_permissions, dict) and perm_key in user_right_permissions:
+ user_has_perm = user_right_permissions[perm_key]
+ if not user_has_perm:
+ raise HTTPException(
+ status_code=403,
+ detail=f"You cannot create a role with '{perm_key}' permission for '{right_key}' as you don't have this permission yourself",
+ )
+ else:
+ raise HTTPException(
+ status_code=403,
+ detail=f"You cannot create a role with '{perm_key}' permission for '{right_key}' as you don't have this permission yourself",
+ )
+
# Complete the role object
role.role_uuid = f"role_{uuid4()}"
role.creation_date = str(datetime.now())
role.update_date = str(datetime.now())
- db_session.add(role)
- db_session.commit()
- db_session.refresh(role)
+ # ============================================================================
+ # VERIFICATION 9: Handle ID sequence issue (existing logic)
+ # ============================================================================
+ try:
+ db_session.add(role)
+ db_session.commit()
+ db_session.refresh(role)
+ except IntegrityError as e:
+ if "duplicate key value violates unique constraint" in str(e) and "role_pkey" in str(e):
+ # Handle the sequence issue by finding the next available ID
+ db_session.rollback()
+
+ # Get the maximum ID from the role table using raw SQL
+ result = db_session.execute(text("SELECT COALESCE(MAX(id), 0) as max_id FROM role"))
+ max_id_result = result.scalar()
+ max_id = max_id_result if max_id_result is not None else 0
+
+ # Set the next available ID
+ role.id = max_id + 1
+
+ # Try to insert again
+ db_session.add(role)
+ db_session.commit()
+ db_session.refresh(role)
+
+ # Update the sequence to the correct value for future inserts
+ try:
+ # Use raw SQL to update the sequence
+ db_session.execute(text(f"SELECT setval('role_id_seq', {max_id + 1}, true)"))
+ db_session.commit()
+ except Exception:
+ # If sequence doesn't exist or can't be updated, that's okay
+ # The manual ID assignment above will handle it
+ pass
+ else:
+ # Re-raise the original exception if it's not the sequence issue
+ raise e
- role = RoleRead(**role.model_dump())
+ # Create RoleRead object with all required fields
+ role_data = role.model_dump()
+ # Ensure org_id is properly handled
+ if role_data.get('org_id') is None:
+ role_data['org_id'] = 0
+ role = RoleRead(**role_data)
return role
+async def get_roles_by_organization(
+ request: Request,
+ db_session: Session,
+ org_id: int,
+ current_user: PublicUser,
+) -> List[RoleRead]:
+ """
+ Get all roles for a specific organization, including global roles.
+
+ Args:
+ request: FastAPI request object
+ db_session: Database session
+ org_id: Organization ID
+ current_user: Current authenticated user
+
+ Returns:
+ List[RoleRead]: List of roles for the organization (including global roles)
+
+ Raises:
+ HTTPException: If organization not found or user lacks permissions
+ """
+ # ============================================================================
+ # VERIFICATION 1: Check if the organization exists
+ # ============================================================================
+ statement = select(Organization).where(Organization.id == org_id)
+ organization = db_session.exec(statement).first()
+
+ if not organization:
+ raise HTTPException(
+ status_code=404,
+ detail="Organization not found",
+ )
+
+ # ============================================================================
+ # VERIFICATION 2: Check if the current user is a member of the organization
+ # ============================================================================
+ statement = select(UserOrganization).where(
+ UserOrganization.user_id == current_user.id,
+ UserOrganization.org_id == org_id
+ )
+ user_org = db_session.exec(statement).first()
+
+ if not user_org:
+ raise HTTPException(
+ status_code=403,
+ detail="You are not a member of this organization",
+ )
+
+ # ============================================================================
+ # VERIFICATION 3: Check if the user has permission to read roles in this organization
+ # ============================================================================
+ # Get the user's role in this organization
+ statement = select(Role).where(Role.id == user_org.role_id)
+ user_role = db_session.exec(statement).first()
+
+ if not user_role:
+ raise HTTPException(
+ status_code=403,
+ detail="Your role in this organization could not be determined",
+ )
+
+ # Check if the user has role reading permissions
+ if user_role.rights and isinstance(user_role.rights, dict):
+ roles_rights = user_role.rights.get('roles', {})
+ if not roles_rights.get('action_read', False):
+ raise HTTPException(
+ status_code=403,
+ detail="You don't have permission to read roles in this organization",
+ )
+ else:
+ # If no rights are defined, check if user has admin role (role_id 1 or 2)
+ if user_role.id not in [1, 2]: # Admin and Maintainer roles
+ raise HTTPException(
+ status_code=403,
+ detail="You don't have permission to read roles in this organization. Admin or Maintainer role required.",
+ )
+
+ # ============================================================================
+ # GET ROLES: Fetch all roles for the organization AND global roles
+ # ============================================================================
+ # Get global roles first
+ global_roles_statement = select(Role).where(
+ Role.role_type == RoleTypeEnum.TYPE_GLOBAL
+ ).order_by(Role.id) # type: ignore
+
+ global_roles = list(db_session.exec(global_roles_statement).all())
+
+ # Get organization-specific roles
+ org_roles_statement = select(Role).where(
+ Role.org_id == org_id,
+ Role.role_type == RoleTypeEnum.TYPE_ORGANIZATION
+ ).order_by(Role.id) # type: ignore
+
+ org_roles = list(db_session.exec(org_roles_statement).all())
+
+ # Combine lists with global roles first, then organization roles
+ all_roles = global_roles + org_roles
+
+ # Convert to RoleRead objects
+ role_reads = []
+ for role in all_roles:
+ role_data = role.model_dump()
+ # Ensure org_id is properly handled
+ if role_data.get('org_id') is None:
+ role_data['org_id'] = 0
+ role_reads.append(RoleRead(**role_data))
+
+ return role_reads
+
+
async def read_role(
request: Request, db_session: Session, role_id: str, current_user: PublicUser
):
- statement = select(Role).where(Role.id == role_id)
+ # Convert role_id to integer
+ try:
+ role_id_int = int(role_id)
+ except ValueError:
+ raise HTTPException(
+ status_code=400,
+ detail="Invalid role ID format. Role ID must be a number.",
+ )
+
+ statement = select(Role).where(Role.id == role_id_int)
result = db_session.exec(statement)
role = result.first()
@@ -75,6 +455,15 @@ async def update_role(
detail="Role not found",
)
+ # ============================================================================
+ # VERIFICATION: Prevent updating TYPE_GLOBAL roles
+ # ============================================================================
+ if role.role_type == RoleTypeEnum.TYPE_GLOBAL:
+ raise HTTPException(
+ status_code=403,
+ detail="Global roles cannot be updated. These are system-defined roles that must remain unchanged.",
+ )
+
# RBAC check
await rbac_check(request, current_user, "update", role.role_uuid, db_session)
@@ -85,9 +474,116 @@ async def update_role(
del role_object.role_id
# Update only the fields that were passed in
- for var, value in vars(role_object).items():
+ # Use model_dump() to get the data as a dictionary
+ try:
+ update_data = role_object.model_dump(exclude_unset=True)
+ except AttributeError:
+ # Fallback to dict() method for older Pydantic versions
+ try:
+ update_data = role_object.dict(exclude_unset=True)
+ except AttributeError:
+ # Fallback to vars() for SQLModel
+ update_data = {k: v for k, v in vars(role_object).items() if v is not None}
+
+ # Update the role with the new data
+ for key, value in update_data.items():
if value is not None:
- setattr(role, var, value)
+ setattr(role, key, value)
+
+ # ============================================================================
+ # VALIDATE RIGHTS STRUCTURE if rights are being updated
+ # ============================================================================
+ if role.rights:
+ # Convert Rights model to dict if needed
+ if isinstance(role.rights, dict):
+ # It's already a dict
+ rights_dict = role.rights
+ else:
+ # It's likely a Pydantic model, try to convert to dict
+ try:
+ # Try dict() method first (for Pydantic v1)
+ rights_dict = role.rights.dict()
+ except AttributeError:
+ try:
+ # Try model_dump() method (for Pydantic v2)
+ rights_dict = role.rights.model_dump() # type: ignore
+ except AttributeError:
+ raise HTTPException(
+ status_code=400,
+ detail="Rights must be provided as a JSON object",
+ )
+
+ # Validate rights structure - check for required top-level keys
+ required_rights = [
+ 'courses', 'users', 'usergroups', 'collections',
+ 'organizations', 'coursechapters', 'activities',
+ 'roles', 'dashboard'
+ ]
+
+ for required_right in required_rights:
+ if required_right not in rights_dict:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required right: {required_right}",
+ )
+
+ # Validate the structure of each right
+ right_data = rights_dict[required_right]
+ if not isinstance(right_data, dict):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Right '{required_right}' must be a JSON object",
+ )
+
+ # Validate courses permissions (has additional 'own' permissions)
+ if required_right == 'courses':
+ required_course_permissions = [
+ 'action_create', 'action_read', 'action_read_own',
+ 'action_update', 'action_update_own', 'action_delete', 'action_delete_own'
+ ]
+ for perm in required_course_permissions:
+ if perm not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required course permission: {perm}",
+ )
+ if not isinstance(right_data[perm], bool):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Course permission '{perm}' must be a boolean",
+ )
+
+ # Validate other permissions (standard permissions)
+ elif required_right in ['users', 'usergroups', 'collections', 'organizations', 'coursechapters', 'activities', 'roles']:
+ required_permissions = ['action_create', 'action_read', 'action_update', 'action_delete']
+ for perm in required_permissions:
+ if perm not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail=f"Missing required permission '{perm}' for '{required_right}'",
+ )
+ if not isinstance(right_data[perm], bool):
+ raise HTTPException(
+ status_code=400,
+ detail=f"Permission '{perm}' for '{required_right}' must be a boolean",
+ )
+
+ # Validate dashboard permissions
+ elif required_right == 'dashboard':
+ if 'action_access' not in right_data:
+ raise HTTPException(
+ status_code=400,
+ detail="Missing required dashboard permission: action_access",
+ )
+ if not isinstance(right_data['action_access'], bool):
+ raise HTTPException(
+ status_code=400,
+ detail="Dashboard permission 'action_access' must be a boolean",
+ )
+
+ # Convert back to dict if it was a model
+ if not isinstance(role.rights, dict):
+ role.rights = rights_dict
db_session.add(role)
db_session.commit()
@@ -101,10 +597,17 @@ async def update_role(
async def delete_role(
request: Request, db_session: Session, role_id: str, current_user: PublicUser
):
- # RBAC check
- await rbac_check(request, current_user, "delete", role_id, db_session)
-
- statement = select(Role).where(Role.id == role_id)
+ # Convert role_id to integer
+ try:
+ role_id_int = int(role_id)
+ except ValueError:
+ raise HTTPException(
+ status_code=400,
+ detail="Invalid role ID format. Role ID must be a number.",
+ )
+
+ # First, get the role to check if it exists and get its UUID
+ statement = select(Role).where(Role.id == role_id_int)
result = db_session.exec(statement)
role = result.first()
@@ -115,6 +618,18 @@ async def delete_role(
detail="Role not found",
)
+ # ============================================================================
+ # VERIFICATION: Prevent deleting TYPE_GLOBAL roles
+ # ============================================================================
+ if role.role_type == RoleTypeEnum.TYPE_GLOBAL:
+ raise HTTPException(
+ status_code=403,
+ detail="Global roles cannot be deleted. These are system-defined roles that must remain unchanged.",
+ )
+
+ # RBAC check using the role's UUID
+ await rbac_check(request, current_user, "delete", role.role_uuid, db_session)
+
db_session.delete(role)
db_session.commit()
diff --git a/apps/api/src/services/users/users.py b/apps/api/src/services/users/users.py
index 18669ef6..913daf47 100644
--- a/apps/api/src/services/users/users.py
+++ b/apps/api/src/services/users/users.py
@@ -103,7 +103,7 @@ async def create_user(
user_organization = UserOrganization(
user_id=user.id if user.id else 0,
org_id=int(org_id),
- role_id=3,
+ role_id=4,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
)
diff --git a/apps/api/src/tests/security/test_rbac.py b/apps/api/src/tests/security/test_rbac.py
index 450b510c..dcee3914 100644
--- a/apps/api/src/tests/security/test_rbac.py
+++ b/apps/api/src/tests/security/test_rbac.py
@@ -57,57 +57,69 @@ class TestRBAC:
@pytest.fixture
def mock_role(self):
"""Create a mock role object"""
- from src.db.roles import RoleTypeEnum
+ from src.db.roles import RoleTypeEnum, Rights, PermissionsWithOwn, Permission, DashboardPermission
role = Mock(spec=Role)
role.id = 1
role.org_id = 1
role.name = "Test Role"
role.description = "A test role."
- # Rights should be a dictionary for validation
- role.rights = {
- "courses": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "users": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "usergroups": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "collections": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "organizations": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "coursechapters": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- },
- "activities": {
- "action_create": False,
- "action_read": True,
- "action_update": False,
- "action_delete": False,
- }
- }
+ # Rights should be a Rights object with proper Permission objects
+ role.rights = Rights(
+ courses=PermissionsWithOwn(
+ action_create=False,
+ action_read=True,
+ action_read_own=False,
+ action_update=False,
+ action_update_own=False,
+ action_delete=False,
+ action_delete_own=False,
+ ),
+ users=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ usergroups=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ collections=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ organizations=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ coursechapters=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ activities=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ roles=Permission(
+ action_create=False,
+ action_read=True,
+ action_update=False,
+ action_delete=False,
+ ),
+ dashboard=DashboardPermission(
+ action_access=True,
+ )
+ )
role.role_type = RoleTypeEnum.TYPE_GLOBAL
role.role_uuid = "role_test"
role.creation_date = "2024-01-01T00:00:00"
@@ -277,7 +289,7 @@ class TestRBAC:
mock_check_type.return_value = "courses"
# Mock role without permission
- mock_role.rights["courses"]["action_read"] = False
+ mock_role.rights.courses.action_read = False
# Mock database query
mock_db_session.exec.return_value.all.return_value = [mock_role]
diff --git a/apps/web/app/auth/options.ts b/apps/web/app/auth/options.ts
index 90a6df54..50ee9f05 100644
--- a/apps/web/app/auth/options.ts
+++ b/apps/web/app/auth/options.ts
@@ -91,12 +91,12 @@ export const nextAuthOptions = {
token.user = userFromOAuth.data;
}
- // Refresh token only if it's close to expiring (5 minutes before expiry)
+ // Refresh token only if it's close to expiring (1 minute before expiry)
if (token?.user?.tokens) {
const tokenExpiry = token.user.tokens.expiry || 0;
- const fiveMinutes = 5 * 60 * 1000;
+ const oneMinute = 1 * 60 * 1000;
- if (Date.now() + fiveMinutes >= tokenExpiry) {
+ if (Date.now() + oneMinute >= tokenExpiry) {
const RefreshedToken = await getNewAccessTokenUsingRefreshTokenServer(
token?.user?.tokens?.refresh_token
);
@@ -118,11 +118,11 @@ export const nextAuthOptions = {
async session({ session, token }: any) {
// Include user information in the session
if (token.user) {
- // Cache the session for 5 minutes to avoid frequent API calls
+ // Cache the session for 1 minute to refresh every minute
const cacheKey = `user_session_${token.user.tokens.access_token}`;
let cachedSession = global.sessionCache?.[cacheKey];
- if (cachedSession && Date.now() - cachedSession.timestamp < 5 * 60 * 1000) {
+ if (cachedSession && Date.now() - cachedSession.timestamp < 1 * 60 * 1000) {
return cachedSession.data;
}
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index 1b770546..6bb780de 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -22,7 +22,7 @@ export default function RootLayout({
{isDevEnv ? '' : }
-
+
{children}
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
index 766c978b..cb5ab797 100644
--- a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
@@ -8,6 +8,10 @@ import Modal from '@components/Objects/StyledElements/Modal/Modal'
import { useSearchParams } from 'next/navigation'
import React from 'react'
import useAdminStatus from '@components/Hooks/useAdminStatus'
+import { getUriWithOrg } from '@services/config/config'
+import { useOrg } from '@components/Contexts/OrgContext'
+import { BookOpen } from 'lucide-react'
+import Link from 'next/link'
type CourseProps = {
orgslug: string
@@ -22,6 +26,7 @@ function CoursesHome(params: CourseProps) {
const orgslug = params.orgslug
const courses = params.courses
const isUserAdmin = useAdminStatus() as any
+ const org = useOrg() as any
async function closeNewCourseModal() {
setNewCourseModal(false)
@@ -32,7 +37,16 @@ function CoursesHome(params: CourseProps) {
-
Courses
+
+
Courses
+
+
+ Rights Guide
+
+
}) {
const params = use(props.params);
+ const router = useRouter();
+
function getEntireCourseUUID(courseuuid: string) {
// add course_ to uuid
return `course_${courseuuid}`
}
+ const courseuuid = getEntireCourseUUID(params.courseuuid)
+ const { hasPermission, isLoading: rightsLoading } = useCourseRights(courseuuid)
+
+ // Define tab configurations with their required permissions
+ const tabs = [
+ {
+ key: 'general',
+ label: 'General',
+ icon: Info,
+ href: `/dash/courses/course/${params.courseuuid}/general`,
+ requiredPermission: 'update' as const
+ },
+ {
+ key: 'content',
+ label: 'Content',
+ icon: GalleryVerticalEnd,
+ href: `/dash/courses/course/${params.courseuuid}/content`,
+ requiredPermission: 'update_content' as const
+ },
+ {
+ key: 'access',
+ label: 'Access',
+ icon: Globe,
+ href: `/dash/courses/course/${params.courseuuid}/access`,
+ requiredPermission: 'manage_access' as const
+ },
+ {
+ key: 'contributors',
+ label: 'Contributors',
+ icon: UserPen,
+ href: `/dash/courses/course/${params.courseuuid}/contributors`,
+ requiredPermission: 'manage_contributors' as const
+ },
+ {
+ key: 'certification',
+ label: 'Certification',
+ icon: Award,
+ href: `/dash/courses/course/${params.courseuuid}/certification`,
+ requiredPermission: 'create_certifications' as const
+ }
+ ]
+
+ // Filter tabs based on permissions
+ const visibleTabs = tabs.filter(tab => hasPermission(tab.requiredPermission))
+
+ // Check if current subpage is accessible
+ const currentTab = tabs.find(tab => tab.key === params.subpage)
+ const hasAccessToCurrentPage = currentTab ? hasPermission(currentTab.requiredPermission) : false
+
+ // Redirect to first available tab if current page is not accessible
+ useEffect(() => {
+ if (!rightsLoading && !hasAccessToCurrentPage && visibleTabs.length > 0) {
+ const firstAvailableTab = visibleTabs[0]
+ router.replace(getUriWithOrg(params.orgslug, '') + firstAvailableTab.href)
+ }
+ }, [rightsLoading, hasAccessToCurrentPage, visibleTabs, router, params.orgslug])
+
+ // Show loading state while rights are being fetched
+ if (rightsLoading) {
+ return (
+
+ )
+ }
+
+ // Show access denied if no tabs are available
+ if (!rightsLoading && visibleTabs.length === 0) {
+ return (
+
+
+
+
Access Denied
+
You don't have permission to access this course.
+
+
+ )
+ }
+
return (
-
+
-
{
+ const IconComponent = tab.icon
+ const isActive = params.subpage.toString() === tab.key
+ const hasAccess = hasPermission(tab.requiredPermission)
+
+ if (!hasAccess) {
+ // Show disabled tab with subtle visual cues and tooltip
+ return (
+
+ Access Restricted
+
+ You don't have permission to access {tab.label}
+
+
+ }
+ >
+
+
+ )
}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ return (
+
+
+
+ )
+ })}
-
}) {
className="h-full overflow-y-auto relative"
>
- {params.subpage == 'content' ? () : ('')}
- {params.subpage == 'general' ? () : ('')}
- {params.subpage == 'access' ? () : ('')}
- {params.subpage == 'contributors' ? () : ('')}
- {params.subpage == 'certification' ? () : ('')}
-
+ {params.subpage == 'content' && hasPermission('update_content') ? (
+
+ ) : null}
+ {params.subpage == 'general' && hasPermission('update') ? (
+
+ ) : null}
+ {params.subpage == 'access' && hasPermission('manage_access') ? (
+
+ ) : null}
+ {params.subpage == 'contributors' && hasPermission('manage_contributors') ? (
+
+ ) : null}
+ {params.subpage == 'certification' && hasPermission('create_certifications') ? (
+
+ ) : null}
diff --git a/apps/web/app/orgs/[orgslug]/dash/documentation/layout.tsx b/apps/web/app/orgs/[orgslug]/dash/documentation/layout.tsx
new file mode 100644
index 00000000..63585436
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/documentation/layout.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+export default function DocumentationLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return <>{children}>
+}
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/documentation/rights/page.tsx b/apps/web/app/orgs/[orgslug]/dash/documentation/rights/page.tsx
new file mode 100644
index 00000000..ee8dc5cf
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/documentation/rights/page.tsx
@@ -0,0 +1,217 @@
+'use client'
+import React from 'react'
+import { getUriWithOrg } from '@services/config/config'
+import { useOrg } from '@components/Contexts/OrgContext'
+import {
+ Shield,
+ Users,
+ BookOpen,
+ UserCheck,
+ Lock,
+ Globe,
+ Award,
+ FileText,
+ Settings,
+ Crown,
+ User,
+ UserCog,
+ GraduationCap,
+ Eye,
+ Edit,
+ Trash2,
+ Plus,
+ CheckCircle,
+ XCircle,
+ AlertCircle,
+ Info,
+ ArrowLeft,
+ AlertTriangle,
+ Key,
+ UserCheck as UserCheckIcon
+} from 'lucide-react'
+import Link from 'next/link'
+import { motion } from 'framer-motion'
+
+interface RightsDocumentationProps {
+ params: Promise<{ orgslug: string }>
+}
+
+const RightsDocumentation = ({ params }: RightsDocumentationProps) => {
+ const org = useOrg() as any
+
+ const roleHierarchy = [
+ {
+ name: 'Admin',
+ icon: ,
+ color: 'bg-purple-50 border-purple-200',
+ description: 'Full platform control with all permissions',
+ permissions: ['All permissions', 'Manage organization', 'Manage users', 'Manage courses', 'Manage roles'],
+ level: 4
+ },
+ {
+ name: 'Maintainer',
+ icon: ,
+ color: 'bg-blue-50 border-blue-200',
+ description: 'Mid-level manager with wide permissions',
+ permissions: ['Manage courses', 'Manage users', 'Manage assignments', ],
+ level: 3
+ },
+ {
+ name: 'Instructor',
+ icon: ,
+ color: 'bg-green-50 border-green-200',
+ description: 'Can create courses but need ownership for content creation',
+ permissions: ['Create courses', 'Manage own courses', 'Create assignments', 'Grade assignments'],
+ level: 2
+ },
+ {
+ name: 'User',
+ icon: ,
+ color: 'bg-gray-50 border-gray-200',
+ description: 'Read-Only Learner',
+ permissions: ['View courses', 'Submit assignments', 'Take assessments'],
+ level: 1
+ }
+ ]
+
+ const courseOwnershipTypes = [
+ {
+ name: 'Creator',
+ icon: ,
+ color: 'bg-yellow-50 border-yellow-200',
+ description: 'Original course creator with full control',
+ permissions: ['Full course control', 'Manage contributors', 'Change access settings', 'Delete course']
+ },
+ {
+ name: 'Maintainer',
+ icon: ,
+ color: 'bg-blue-50 border-blue-200',
+ description: 'Course maintainer with extensive permissions',
+ permissions: ['Manage course content', 'Manage contributors', 'Change access settings', 'Cannot delete course']
+ },
+ {
+ name: 'Contributor',
+ icon: ,
+ color: 'bg-green-50 border-green-200',
+ description: 'Course contributor with limited permissions',
+ permissions: ['Edit course content', 'Create activities', 'Cannot manage contributors', 'Cannot change access']
+ }
+ ]
+
+ return (
+
+
+ {/* Top Icon */}
+
+
+
+
+
+
+ {/* Header */}
+
+
+
+ Back to Dashboard
+
+
+
Authorizations & Rights Guide
+
+
+ Understanding LearnHouse permissions, roles, and access controls based on RBAC system
+
+
+
+ {/* Role Hierarchy Section */}
+
+
+
+ Role Hierarchy
+
+
+ {roleHierarchy.map((role, index) => (
+
+
+ {role.icon}
+
{role.name}
+
+ {role.description}
+
+ {role.permissions.map((permission, permIndex) => (
+ -
+
+ {permission}
+
+ ))}
+
+
+ ))}
+
+
+
+ {/* Course Ownership Types */}
+
+
+
+ Course Ownership Types
+
+
+ {courseOwnershipTypes.map((type, index) => (
+
+
+ {type.icon}
+
{type.name}
+
+ {type.description}
+
+ {type.permissions.map((permission, permIndex) => (
+ -
+
+ {permission}
+
+ ))}
+
+
+ ))}
+
+
+
+
+ )
+}
+
+export default RightsDocumentation
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
index 71131cf8..6930f42d 100644
--- a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
@@ -4,7 +4,7 @@ import { motion } from 'framer-motion'
import Link from 'next/link'
import { useMediaQuery } from 'usehooks-ts'
import { getUriWithOrg } from '@services/config/config'
-import { Monitor, ScanEye, SquareUserRound, UserPlus, Users } from 'lucide-react'
+import { Monitor, ScanEye, SquareUserRound, UserPlus, Users, Shield } from 'lucide-react'
import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
@@ -12,6 +12,7 @@ import OrgUsers from '@components/Dashboard/Pages/Users/OrgUsers/OrgUsers'
import OrgAccess from '@components/Dashboard/Pages/Users/OrgAccess/OrgAccess'
import OrgUsersAdd from '@components/Dashboard/Pages/Users/OrgUsersAdd/OrgUsersAdd'
import OrgUserGroups from '@components/Dashboard/Pages/Users/OrgUserGroups/OrgUserGroups'
+import OrgRoles from '@components/Dashboard/Pages/Users/OrgRoles/OrgRoles'
export type SettingsParams = {
subpage: string
@@ -43,6 +44,10 @@ function UsersSettingsPage(props: { params: Promise }) {
setH1Label('UserGroups')
setH2Label('Create and manage user groups')
}
+ if (params.subpage == 'roles') {
+ setH1Label('Roles')
+ setH2Label('Create and manage roles with specific permissions')
+ }
}
useEffect(() => {
@@ -112,6 +117,23 @@ function UsersSettingsPage(props: { params: Promise }) {
+
+
+
}) {
{params.subpage == 'signups' ? : ''}
{params.subpage == 'add' ? : ''}
{params.subpage == 'usergroups' ? : ''}
+ {params.subpage == 'roles' ? : ''}
)
diff --git a/apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx b/apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx
index 4fafdc3a..e20fd1ec 100644
--- a/apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx
+++ b/apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx
@@ -9,6 +9,7 @@ import { getCourseThumbnailMediaDirectory } from '@services/media/media'
import Link from 'next/link'
import Image from 'next/image'
import EmptyThumbnailImage from '../../../public/empty_thumbnail.png'
+import { BookOpen } from 'lucide-react'
export function CourseOverviewTop({
params,
@@ -57,7 +58,14 @@ export function CourseOverviewTop({
-
diff --git a/apps/web/components/Dashboard/Pages/Users/OrgRoles/OrgRoles.tsx b/apps/web/components/Dashboard/Pages/Users/OrgRoles/OrgRoles.tsx
new file mode 100644
index 00000000..fc13b174
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Users/OrgRoles/OrgRoles.tsx
@@ -0,0 +1,295 @@
+'use client'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useOrg } from '@components/Contexts/OrgContext'
+import AddRole from '@components/Objects/Modals/Dash/OrgRoles/AddRole'
+import EditRole from '@components/Objects/Modals/Dash/OrgRoles/EditRole'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
+import { getAPIUrl } from '@services/config/config'
+import { deleteRole } from '@services/roles/roles'
+import { swrFetcher } from '@services/utils/ts/requests'
+import { Pencil, Shield, Users, X, Globe } from 'lucide-react'
+import React from 'react'
+import toast from 'react-hot-toast'
+import useSWR, { mutate } from 'swr'
+
+function OrgRoles() {
+ const org = useOrg() as any
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token;
+ const [createRoleModal, setCreateRoleModal] = React.useState(false)
+ const [editRoleModal, setEditRoleModal] = React.useState(false)
+ const [selectedRole, setSelectedRole] = React.useState(null) as any
+
+ const { data: roles } = useSWR(
+ org ? `${getAPIUrl()}roles/org/${org.id}` : null,
+ (url) => swrFetcher(url, access_token)
+ )
+
+ const deleteRoleUI = async (role_id: any) => {
+ const toastId = toast.loading("Deleting...");
+ const res = await deleteRole(role_id, org.id, access_token)
+ if (res.status === 200) {
+ mutate(`${getAPIUrl()}roles/org/${org.id}`)
+ toast.success("Deleted role", {id:toastId})
+ }
+ else {
+ toast.error('Error deleting role', {id:toastId})
+ }
+ }
+
+ const handleEditRoleModal = (role: any) => {
+ setSelectedRole(role)
+ setEditRoleModal(!editRoleModal)
+ }
+
+ const getRightsSummary = (rights: any) => {
+ if (!rights) return 'No permissions'
+
+ const totalPermissions = Object.keys(rights).reduce((acc, key) => {
+ if (typeof rights[key] === 'object') {
+ return acc + Object.keys(rights[key]).filter(k => rights[key][k] === true).length
+ }
+ return acc
+ }, 0)
+
+ return `${totalPermissions} permissions`
+ }
+
+ // Check if a role is system-wide (TYPE_GLOBAL or role_uuid starts with role_global_)
+ const isSystemRole = (role: any) => {
+ // Check for role_type field first
+ if (role.role_type === 'TYPE_GLOBAL') {
+ return true
+ }
+
+ // Check for role_uuid starting with role_global_
+ if (role.role_uuid && role.role_uuid.startsWith('role_global_')) {
+ return true
+ }
+
+ // Check for common system role IDs (1-4 are typically system roles)
+ if (role.id && [1, 2, 3, 4].includes(role.id)) {
+ return true
+ }
+
+ // Check if the role name indicates it's a system role
+ if (role.name && ['Admin', 'Maintainer', 'Instructor', 'User'].includes(role.name)) {
+ return true
+ }
+
+ return false
+ }
+
+ return (
+ <>
+
+
+
+
Manage Roles & Permissions
+
+ {' '}
+ Roles define what users can do within your organization. Create custom roles with specific permissions for different user types.{' '}
+
+
+
+ {/* Mobile view - Cards */}
+
+ {roles?.map((role: any) => {
+ const isSystem = isSystemRole(role)
+ return (
+
+
+
+
+ {role.name}
+ {isSystem && (
+
+
+ System-wide
+
+ )}
+
+
+ {getRightsSummary(role.rights)}
+
+
+
{role.description || 'No description'}
+
+ {!isSystem ? (
+ <>
+
+ handleEditRoleModal(role)
+ }
+ minHeight="lg"
+ minWidth='xl'
+ customWidth="max-w-7xl"
+ dialogContent={
+
+ }
+ dialogTitle="Edit Role"
+ dialogDescription={
+ 'Edit the role permissions and details'
+ }
+ dialogTrigger={
+
+ }
+ />
+
+
+ Delete
+
+ }
+ functionToExecute={() => {
+ deleteRoleUI(role.id)
+ }}
+ status="warning"
+ />
+ >
+ ) : null}
+
+
+ )
+ })}
+
+
+ {/* Desktop view - Table */}
+
+
+
+
+ | Role Name |
+ Description |
+ Permissions |
+ Actions |
+
+
+ <>
+
+ {roles?.map((role: any) => {
+ const isSystem = isSystemRole(role)
+ return (
+
+ |
+
+
+ {role.name}
+ {isSystem && (
+
+
+ System-wide
+
+ )}
+
+ |
+ {role.description || 'No description'} |
+
+
+ {getRightsSummary(role.rights)}
+
+ |
+
+
+ {!isSystem ? (
+ <>
+
+ handleEditRoleModal(role)
+ }
+ minHeight="lg"
+ minWidth='xl'
+ customWidth="max-w-7xl"
+ dialogContent={
+
+ }
+ dialogTitle="Edit Role"
+ dialogDescription={
+ 'Edit the role permissions and details'
+ }
+ dialogTrigger={
+
+ }
+ />
+
+
+ Delete
+
+ }
+ functionToExecute={() => {
+ deleteRoleUI(role.id)
+ }}
+ status="warning"
+ />
+ >
+ ) : null}
+
+ |
+
+ )
+ })}
+
+ >
+
+
+
+
+
setCreateRoleModal(!createRoleModal)}
+ minHeight="no-min"
+ minWidth='xl'
+ customWidth="max-w-7xl"
+ dialogContent={
+
+ }
+ dialogTitle="Create a Role"
+ dialogDescription={
+ 'Create a new role with specific permissions'
+ }
+ dialogTrigger={
+
+ }
+ />
+
+
+ >
+ )
+}
+
+export default OrgRoles
\ No newline at end of file
diff --git a/apps/web/components/Hooks/useAdminStatus.tsx b/apps/web/components/Hooks/useAdminStatus.tsx
index 93ae93d0..1284dd0a 100644
--- a/apps/web/components/Hooks/useAdminStatus.tsx
+++ b/apps/web/components/Hooks/useAdminStatus.tsx
@@ -3,40 +3,193 @@ import { useLHSession } from '@components/Contexts/LHSessionContext';
import { useEffect, useState, useMemo } from 'react';
interface Role {
- org: { id: number };
- role: { id: number; role_uuid: string };
+ org: { id: number; org_uuid: string };
+ role: {
+ id: number;
+ role_uuid: string;
+ rights?: {
+ [key: string]: {
+ [key: string]: boolean;
+ };
+ };
+ };
}
-function useAdminStatus() {
+interface Rights {
+ courses: {
+ action_create: boolean;
+ action_read: boolean;
+ action_read_own: boolean;
+ action_update: boolean;
+ action_update_own: boolean;
+ action_delete: boolean;
+ action_delete_own: boolean;
+ };
+ users: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ usergroups: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ collections: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ organizations: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ coursechapters: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ activities: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ roles: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ dashboard: {
+ action_access: boolean;
+ };
+}
+
+interface UseAdminStatusReturn {
+ isAdmin: boolean | null;
+ loading: boolean;
+ userRoles: Role[];
+ rights: Rights | null;
+}
+
+function useAdminStatus(): UseAdminStatusReturn {
const session = useLHSession() as any;
const org = useOrg() as any;
const [isAdmin, setIsAdmin] = useState(null);
const [loading, setLoading] = useState(true);
+ const [rights, setRights] = useState(null);
const userRoles = useMemo(() => session?.data?.roles || [], [session?.data?.roles]);
useEffect(() => {
if (session.status === 'authenticated' && org?.id) {
- const isAdminVar = userRoles.some((role: Role) => {
- return (
- role.org.id === org.id &&
- (
- role.role.id === 1 ||
- role.role.id === 2 ||
- role.role.role_uuid === 'role_global_admin' ||
- role.role.role_uuid === 'role_global_maintainer'
- )
- );
- });
+ // Extract rights from the backend session data
+ const extractRightsFromRoles = (): Rights | null => {
+ if (!userRoles || userRoles.length === 0) return null;
+
+ // Find roles for the current organization
+ const orgRoles = userRoles.filter((role: Role) => role.org.id === org.id);
+ if (orgRoles.length === 0) return null;
+
+ // Merge rights from all roles for this organization
+ const mergedRights: Rights = {
+ courses: {
+ action_create: false,
+ action_read: false,
+ action_read_own: false,
+ action_update: false,
+ action_update_own: false,
+ action_delete: false,
+ action_delete_own: false
+ },
+ users: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ usergroups: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ collections: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ organizations: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ coursechapters: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ activities: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ roles: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ dashboard: {
+ action_access: false
+ }
+ };
+
+ // Merge rights from all roles
+ orgRoles.forEach((role: Role) => {
+ if (role.role.rights) {
+ Object.keys(role.role.rights).forEach((resourceType) => {
+ if (mergedRights[resourceType as keyof Rights]) {
+ Object.keys(role.role.rights![resourceType]).forEach((action) => {
+ if (role.role.rights![resourceType][action] === true) {
+ (mergedRights[resourceType as keyof Rights] as any)[action] = true;
+ }
+ });
+ }
+ });
+ }
+ });
+
+ return mergedRights;
+ };
+
+ const extractedRights = extractRightsFromRoles();
+ setRights(extractedRights);
+
+ // User is admin only if they have dashboard access
+ const isAdminVar = extractedRights?.dashboard?.action_access === true;
setIsAdmin(isAdminVar);
- setLoading(false); // Set loading to false once the status is determined
+
+ setLoading(false);
} else {
setIsAdmin(false);
- setLoading(false); // Set loading to false if not authenticated or org not found
+ setRights(null);
+ setLoading(false);
}
}, [session.status, userRoles, org.id]);
- return { isAdmin, loading };
+ return { isAdmin, loading, userRoles, rights };
}
export default useAdminStatus;
diff --git a/apps/web/components/Hooks/useCourseRights.tsx b/apps/web/components/Hooks/useCourseRights.tsx
new file mode 100644
index 00000000..6c143bef
--- /dev/null
+++ b/apps/web/components/Hooks/useCourseRights.tsx
@@ -0,0 +1,64 @@
+'use client'
+import { getAPIUrl } from '@services/config/config'
+import { swrFetcher } from '@services/utils/ts/requests'
+import useSWR from 'swr'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+
+export interface CourseRights {
+ course_uuid: string
+ user_id: number
+ is_anonymous: boolean
+ permissions: {
+ read: boolean
+ create: boolean
+ update: boolean
+ delete: boolean
+ create_content: boolean
+ update_content: boolean
+ delete_content: boolean
+ manage_contributors: boolean
+ manage_access: boolean
+ grade_assignments: boolean
+ mark_activities_done: boolean
+ create_certifications: boolean
+ }
+ ownership: {
+ is_owner: boolean
+ is_creator: boolean
+ is_maintainer: boolean
+ is_contributor: boolean
+ authorship_status: string
+ }
+ roles: {
+ is_admin: boolean
+ is_maintainer_role: boolean
+ is_instructor: boolean
+ is_user: boolean
+ }
+}
+
+export function useCourseRights(courseuuid: string) {
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token
+
+ const { data: rights, error, isLoading } = useSWR(
+ courseuuid ? `${getAPIUrl()}courses/${courseuuid}/rights` : null,
+ (url: string) => swrFetcher(url, access_token)
+ )
+
+ return {
+ rights,
+ error,
+ isLoading,
+ hasPermission: (permission: keyof CourseRights['permissions']) => {
+ return rights?.permissions?.[permission] ?? false
+ },
+ hasRole: (role: keyof CourseRights['roles']) => {
+ return rights?.roles?.[role] ?? false
+ },
+ isOwner: rights?.ownership?.is_owner ?? false,
+ isCreator: rights?.ownership?.is_creator ?? false,
+ isMaintainer: rights?.ownership?.is_maintainer ?? false,
+ isContributor: rights?.ownership?.is_contributor ?? false
+ }
+}
\ No newline at end of file
diff --git a/apps/web/components/Objects/Modals/Dash/OrgRoles/AddRole.tsx b/apps/web/components/Objects/Modals/Dash/OrgRoles/AddRole.tsx
new file mode 100644
index 00000000..bb1ebac1
--- /dev/null
+++ b/apps/web/components/Objects/Modals/Dash/OrgRoles/AddRole.tsx
@@ -0,0 +1,599 @@
+'use client'
+import FormLayout, {
+ FormField,
+ FormLabelAndMessage,
+ Input,
+ Textarea,
+} from '@components/Objects/StyledElements/Form/Form'
+import * as Form from '@radix-ui/react-form'
+import { useOrg } from '@components/Contexts/OrgContext'
+import React from 'react'
+import { createRole } from '@services/roles/roles'
+import { mutate } from 'swr'
+import { getAPIUrl } from '@services/config/config'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useFormik } from 'formik'
+import toast from 'react-hot-toast'
+import { Shield, BookOpen, Users, UserCheck, FolderOpen, Building, FileText, Activity, Settings, Monitor, CheckSquare, Square } from 'lucide-react'
+
+type AddRoleProps = {
+ setCreateRoleModal: any
+}
+
+interface Rights {
+ courses: {
+ action_create: boolean;
+ action_read: boolean;
+ action_read_own: boolean;
+ action_update: boolean;
+ action_update_own: boolean;
+ action_delete: boolean;
+ action_delete_own: boolean;
+ };
+ users: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ usergroups: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ collections: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ organizations: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ coursechapters: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ activities: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ roles: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ dashboard: {
+ action_access: boolean;
+ };
+}
+
+const validate = (values: any) => {
+ const errors: any = {}
+
+ if (!values.name) {
+ errors.name = 'Required'
+ } else if (values.name.length < 2) {
+ errors.name = 'Name must be at least 2 characters'
+ }
+
+ if (!values.description) {
+ errors.description = 'Required'
+ } else if (values.description.length < 10) {
+ errors.description = 'Description must be at least 10 characters'
+ }
+
+ return errors
+}
+
+const defaultRights: Rights = {
+ courses: {
+ action_create: false,
+ action_read: false,
+ action_read_own: false,
+ action_update: false,
+ action_update_own: false,
+ action_delete: false,
+ action_delete_own: false
+ },
+ users: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ usergroups: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ collections: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ organizations: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ coursechapters: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ activities: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ roles: {
+ action_create: false,
+ action_read: false,
+ action_update: false,
+ action_delete: false
+ },
+ dashboard: {
+ action_access: false
+ }
+}
+
+const predefinedRoles = {
+ 'Admin': {
+ name: 'Admin',
+ description: 'Full platform control with all permissions',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: true, action_delete_own: true },
+ users: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ usergroups: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ organizations: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ roles: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Course Manager': {
+ name: 'Course Manager',
+ description: 'Can manage courses, chapters, and activities',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: false, action_delete_own: true },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Instructor': {
+ name: 'Instructor',
+ description: 'Can create and manage their own courses',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: false, action_update_own: true, action_delete: false, action_delete_own: true },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Viewer': {
+ name: 'Viewer',
+ description: 'Read-only access to courses and content',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Content Creator': {
+ name: 'Content Creator',
+ description: 'Can create and edit content but not manage users',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'User Manager': {
+ name: 'User Manager',
+ description: 'Can manage users and user groups',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ usergroups: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Moderator': {
+ name: 'Moderator',
+ description: 'Can moderate content and manage activities',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Analyst': {
+ name: 'Analyst',
+ description: 'Read-only access with analytics capabilities',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Guest': {
+ name: 'Guest',
+ description: 'Limited access for external users',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: false, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: false }
+ }
+ }
+}
+
+function AddRole(props: AddRoleProps) {
+ const org = useOrg() as any;
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token;
+ const [isSubmitting, setIsSubmitting] = React.useState(false)
+ const [rights, setRights] = React.useState(defaultRights)
+
+ const formik = useFormik({
+ initialValues: {
+ name: '',
+ description: '',
+ org_id: org.id,
+ rights: defaultRights
+ },
+ validate,
+ onSubmit: async (values) => {
+ const toastID = toast.loading("Creating...")
+ setIsSubmitting(true)
+
+ // Ensure rights object is properly structured
+ const formattedRights = {
+ courses: {
+ action_create: rights.courses?.action_create || false,
+ action_read: rights.courses?.action_read || false,
+ action_read_own: rights.courses?.action_read_own || false,
+ action_update: rights.courses?.action_update || false,
+ action_update_own: rights.courses?.action_update_own || false,
+ action_delete: rights.courses?.action_delete || false,
+ action_delete_own: rights.courses?.action_delete_own || false
+ },
+ users: {
+ action_create: rights.users?.action_create || false,
+ action_read: rights.users?.action_read || false,
+ action_update: rights.users?.action_update || false,
+ action_delete: rights.users?.action_delete || false
+ },
+ usergroups: {
+ action_create: rights.usergroups?.action_create || false,
+ action_read: rights.usergroups?.action_read || false,
+ action_update: rights.usergroups?.action_update || false,
+ action_delete: rights.usergroups?.action_delete || false
+ },
+ collections: {
+ action_create: rights.collections?.action_create || false,
+ action_read: rights.collections?.action_read || false,
+ action_update: rights.collections?.action_update || false,
+ action_delete: rights.collections?.action_delete || false
+ },
+ organizations: {
+ action_create: rights.organizations?.action_create || false,
+ action_read: rights.organizations?.action_read || false,
+ action_update: rights.organizations?.action_update || false,
+ action_delete: rights.organizations?.action_delete || false
+ },
+ coursechapters: {
+ action_create: rights.coursechapters?.action_create || false,
+ action_read: rights.coursechapters?.action_read || false,
+ action_update: rights.coursechapters?.action_update || false,
+ action_delete: rights.coursechapters?.action_delete || false
+ },
+ activities: {
+ action_create: rights.activities?.action_create || false,
+ action_read: rights.activities?.action_read || false,
+ action_update: rights.activities?.action_update || false,
+ action_delete: rights.activities?.action_delete || false
+ },
+ roles: {
+ action_create: rights.roles?.action_create || false,
+ action_read: rights.roles?.action_read || false,
+ action_update: rights.roles?.action_update || false,
+ action_delete: rights.roles?.action_delete || false
+ },
+ dashboard: {
+ action_access: rights.dashboard?.action_access || false
+ }
+ }
+
+ const res = await createRole({
+ name: values.name,
+ description: values.description,
+ org_id: values.org_id,
+ rights: formattedRights
+ }, access_token)
+ if (res.status === 200 || res.status === 201) {
+ setIsSubmitting(false)
+ mutate(`${getAPIUrl()}roles/org/${org.id}`)
+ props.setCreateRoleModal(false)
+ toast.success("Created new role", {id:toastID})
+ } else {
+ setIsSubmitting(false)
+ toast.error("Couldn't create new role", {id:toastID})
+ }
+ },
+ })
+
+ const handleRightChange = (section: keyof Rights, action: string, value: boolean) => {
+ setRights(prev => ({
+ ...prev,
+ [section]: {
+ ...prev[section],
+ [action]: value
+ } as any
+ }))
+ }
+
+ const handleSelectAll = (section: keyof Rights, value: boolean) => {
+ setRights(prev => ({
+ ...prev,
+ [section]: Object.keys(prev[section]).reduce((acc, key) => ({
+ ...acc,
+ [key]: value
+ }), {} as any)
+ }))
+ }
+
+ const handlePredefinedRole = (roleKey: string) => {
+ const role = predefinedRoles[roleKey as keyof typeof predefinedRoles]
+ if (role) {
+ formik.setFieldValue('name', role.name)
+ formik.setFieldValue('description', role.description)
+ setRights(role.rights as Rights)
+ }
+ }
+
+ const PermissionSection = ({ title, icon: Icon, section, permissions }: { title: string, icon: any, section: keyof Rights, permissions: string[] }) => {
+ const sectionRights = rights[section] as any
+ const allSelected = permissions.every(perm => sectionRights[perm])
+ const someSelected = permissions.some(perm => sectionRights[perm]) && !allSelected
+
+ return (
+
+
+
+
+
{title}
+
+
+
+
+ {permissions.map((permission) => (
+
+ ))}
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Predefined Rights
+
+ {Object.keys(predefinedRoles).map((roleKey) => (
+
+ ))}
+
+
+
+
+
+
Permissions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default AddRole
\ No newline at end of file
diff --git a/apps/web/components/Objects/Modals/Dash/OrgRoles/EditRole.tsx b/apps/web/components/Objects/Modals/Dash/OrgRoles/EditRole.tsx
new file mode 100644
index 00000000..2ade7d1c
--- /dev/null
+++ b/apps/web/components/Objects/Modals/Dash/OrgRoles/EditRole.tsx
@@ -0,0 +1,548 @@
+'use client'
+import FormLayout, {
+ FormField,
+ FormLabelAndMessage,
+ Input,
+ Textarea,
+} from '@components/Objects/StyledElements/Form/Form'
+import * as Form from '@radix-ui/react-form'
+import { useOrg } from '@components/Contexts/OrgContext'
+import React from 'react'
+import { updateRole } from '@services/roles/roles'
+import { mutate } from 'swr'
+import { getAPIUrl } from '@services/config/config'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useFormik } from 'formik'
+import toast from 'react-hot-toast'
+import { Shield, BookOpen, Users, UserCheck, FolderOpen, Building, FileText, Activity, Settings, Monitor, CheckSquare, Square } from 'lucide-react'
+
+type EditRoleProps = {
+ role: {
+ id: number,
+ name: string,
+ description: string,
+ rights: any
+ }
+ setEditRoleModal: any
+}
+
+interface Rights {
+ courses: {
+ action_create: boolean;
+ action_read: boolean;
+ action_read_own: boolean;
+ action_update: boolean;
+ action_update_own: boolean;
+ action_delete: boolean;
+ action_delete_own: boolean;
+ };
+ users: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ usergroups: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ collections: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ organizations: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ coursechapters: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ activities: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ roles: {
+ action_create: boolean;
+ action_read: boolean;
+ action_update: boolean;
+ action_delete: boolean;
+ };
+ dashboard: {
+ action_access: boolean;
+ };
+}
+
+const validate = (values: any) => {
+ const errors: any = {}
+
+ if (!values.name) {
+ errors.name = 'Required'
+ } else if (values.name.length < 2) {
+ errors.name = 'Name must be at least 2 characters'
+ }
+
+ if (!values.description) {
+ errors.description = 'Required'
+ } else if (values.description.length < 10) {
+ errors.description = 'Description must be at least 10 characters'
+ }
+
+ return errors
+}
+
+const predefinedRoles = {
+ 'Admin': {
+ name: 'Admin',
+ description: 'Full platform control with all permissions',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: true, action_delete_own: true },
+ users: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ usergroups: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ organizations: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ roles: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Course Manager': {
+ name: 'Course Manager',
+ description: 'Can manage courses, chapters, and activities',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: false, action_delete_own: true },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Instructor': {
+ name: 'Instructor',
+ description: 'Can create and manage their own courses',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: false, action_update_own: true, action_delete: false, action_delete_own: true },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Viewer': {
+ name: 'Viewer',
+ description: 'Read-only access to courses and content',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Content Creator': {
+ name: 'Content Creator',
+ description: 'Can create and edit content but not manage users',
+ rights: {
+ courses: { action_create: true, action_read: true, action_read_own: true, action_update: true, action_update_own: true, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: true, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'User Manager': {
+ name: 'User Manager',
+ description: 'Can manage users and user groups',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ usergroups: { action_create: true, action_read: true, action_update: true, action_delete: true },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Moderator': {
+ name: 'Moderator',
+ description: 'Can moderate content and manage activities',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: true, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Analyst': {
+ name: 'Analyst',
+ description: 'Read-only access with analytics capabilities',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: true, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ dashboard: { action_access: true }
+ }
+ },
+ 'Guest': {
+ name: 'Guest',
+ description: 'Limited access for external users',
+ rights: {
+ courses: { action_create: false, action_read: true, action_read_own: false, action_update: false, action_update_own: false, action_delete: false, action_delete_own: false },
+ users: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ usergroups: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ collections: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ organizations: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ coursechapters: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ activities: { action_create: false, action_read: true, action_update: false, action_delete: false },
+ roles: { action_create: false, action_read: false, action_update: false, action_delete: false },
+ dashboard: { action_access: false }
+ }
+ }
+}
+
+function EditRole(props: EditRoleProps) {
+ const org = useOrg() as any;
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token;
+ const [isSubmitting, setIsSubmitting] = React.useState(false)
+ const [rights, setRights] = React.useState(props.role.rights || {})
+
+ const formik = useFormik({
+ initialValues: {
+ name: props.role.name,
+ description: props.role.description,
+ org_id: org.id,
+ rights: props.role.rights || {}
+ },
+ validate,
+ onSubmit: async (values) => {
+ const toastID = toast.loading("Updating...")
+ setIsSubmitting(true)
+
+ // Ensure rights object is properly structured
+ const formattedRights = {
+ courses: {
+ action_create: rights.courses?.action_create || false,
+ action_read: rights.courses?.action_read || false,
+ action_read_own: rights.courses?.action_read_own || false,
+ action_update: rights.courses?.action_update || false,
+ action_update_own: rights.courses?.action_update_own || false,
+ action_delete: rights.courses?.action_delete || false,
+ action_delete_own: rights.courses?.action_delete_own || false
+ },
+ users: {
+ action_create: rights.users?.action_create || false,
+ action_read: rights.users?.action_read || false,
+ action_update: rights.users?.action_update || false,
+ action_delete: rights.users?.action_delete || false
+ },
+ usergroups: {
+ action_create: rights.usergroups?.action_create || false,
+ action_read: rights.usergroups?.action_read || false,
+ action_update: rights.usergroups?.action_update || false,
+ action_delete: rights.usergroups?.action_delete || false
+ },
+ collections: {
+ action_create: rights.collections?.action_create || false,
+ action_read: rights.collections?.action_read || false,
+ action_update: rights.collections?.action_update || false,
+ action_delete: rights.collections?.action_delete || false
+ },
+ organizations: {
+ action_create: rights.organizations?.action_create || false,
+ action_read: rights.organizations?.action_read || false,
+ action_update: rights.organizations?.action_update || false,
+ action_delete: rights.organizations?.action_delete || false
+ },
+ coursechapters: {
+ action_create: rights.coursechapters?.action_create || false,
+ action_read: rights.coursechapters?.action_read || false,
+ action_update: rights.coursechapters?.action_update || false,
+ action_delete: rights.coursechapters?.action_delete || false
+ },
+ activities: {
+ action_create: rights.activities?.action_create || false,
+ action_read: rights.activities?.action_read || false,
+ action_update: rights.activities?.action_update || false,
+ action_delete: rights.activities?.action_delete || false
+ },
+ roles: {
+ action_create: rights.roles?.action_create || false,
+ action_read: rights.roles?.action_read || false,
+ action_update: rights.roles?.action_update || false,
+ action_delete: rights.roles?.action_delete || false
+ },
+ dashboard: {
+ action_access: rights.dashboard?.action_access || false
+ }
+ }
+
+ const res = await updateRole(props.role.id, {
+ name: values.name,
+ description: values.description,
+ org_id: values.org_id,
+ rights: formattedRights
+ }, access_token)
+ if (res.status === 200) {
+ setIsSubmitting(false)
+ mutate(`${getAPIUrl()}roles/org/${org.id}`)
+ props.setEditRoleModal(false)
+ toast.success("Updated role", {id:toastID})
+ } else {
+ setIsSubmitting(false)
+ toast.error("Couldn't update role", {id:toastID})
+ }
+ },
+ })
+
+ const handleRightChange = (section: keyof Rights, action: string, value: boolean) => {
+ setRights(prev => ({
+ ...prev,
+ [section]: {
+ ...prev[section],
+ [action]: value
+ } as any
+ }))
+ }
+
+ const handleSelectAll = (section: keyof Rights, value: boolean) => {
+ setRights(prev => ({
+ ...prev,
+ [section]: Object.keys(prev[section]).reduce((acc, key) => ({
+ ...acc,
+ [key]: value
+ }), {} as any)
+ }))
+ }
+
+ const handlePredefinedRole = (roleKey: string) => {
+ const role = predefinedRoles[roleKey as keyof typeof predefinedRoles]
+ if (role) {
+ formik.setFieldValue('name', role.name)
+ formik.setFieldValue('description', role.description)
+ setRights(role.rights as Rights)
+ }
+ }
+
+ const PermissionSection = ({ title, icon: Icon, section, permissions }: { title: string, icon: any, section: keyof Rights, permissions: string[] }) => {
+ const sectionRights = rights[section] as any
+ const allSelected = permissions.every(perm => sectionRights[perm])
+ const someSelected = permissions.some(perm => sectionRights[perm]) && !allSelected
+
+ return (
+
+
+
+
+
{title}
+
+
+
+
+ {permissions.map((permission) => (
+
+ ))}
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Predefined Rights
+
+ {Object.keys(predefinedRoles).map((roleKey) => (
+
+ ))}
+
+
+
+
+
+
Permissions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default EditRole
\ No newline at end of file
diff --git a/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx b/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
index 0c14a500..9df82de4 100644
--- a/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
+++ b/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
@@ -11,10 +11,12 @@ import * as Form from '@radix-ui/react-form'
import { FormMessage } from '@radix-ui/react-form'
import { getAPIUrl } from '@services/config/config'
import { updateUserRole } from '@services/organizations/orgs'
+import { swrFetcher } from '@services/utils/ts/requests'
import React, { useEffect } from 'react'
import toast from 'react-hot-toast'
import { BarLoader } from 'react-spinners'
import { mutate } from 'swr'
+import useSWR from 'swr'
interface Props {
user: any
@@ -25,13 +27,19 @@ interface Props {
function RolesUpdate(props: Props) {
const org = useOrg() as any
const session = useLHSession() as any
- const access_token = session?.data?.tokens?.access_token;
+ const access_token = session?.data?.tokens?.access_token;
const [isSubmitting, setIsSubmitting] = React.useState(false)
const [assignedRole, setAssignedRole] = React.useState(
props.alreadyAssignedRole
)
const [error, setError] = React.useState(null) as any
+ // Fetch available roles for the organization
+ const { data: roles, error: rolesError } = useSWR(
+ org ? `${getAPIUrl()}roles/org/${org.id}` : null,
+ (url) => swrFetcher(url, access_token)
+ )
+
const handleAssignedRole = (event: React.ChangeEvent) => {
setError(null)
setAssignedRole(event.target.value)
@@ -80,10 +88,20 @@ function RolesUpdate(props: Props) {
defaultValue={assignedRole}
className="border border-gray-300 rounded-md p-2"
required
+ disabled={!roles || rolesError}
>
-
-
-
+ {!roles || rolesError ? (
+
+ ) : (
+ <>
+
+ {roles.map((role: any) => (
+
+ ))}
+ >
+ )}
diff --git a/apps/web/components/Objects/StyledElements/Modal/Modal.tsx b/apps/web/components/Objects/StyledElements/Modal/Modal.tsx
index c618435f..2b164213 100644
--- a/apps/web/components/Objects/StyledElements/Modal/Modal.tsx
+++ b/apps/web/components/Objects/StyledElements/Modal/Modal.tsx
@@ -47,12 +47,14 @@ const Modal = (params: ModalParams) => {
{params.dialogTrigger}
)}
{
)}>
{params.dialogTitle && params.dialogDescription && (
- {params.dialogTitle}
- {params.dialogDescription}
+ {params.dialogTitle}
+ {params.dialogDescription}
)}
-
- {params.dialogContent}
+
+
+ {params.dialogContent}
+
{(params.dialogClose || params.addDefCloseButton) && (
-
+
{params.dialogClose}
{params.addDefCloseButton && (
diff --git a/apps/web/components/Security/HeaderProfileBox.tsx b/apps/web/components/Security/HeaderProfileBox.tsx
index 6eaa7dde..1c8b1fbf 100644
--- a/apps/web/components/Security/HeaderProfileBox.tsx
+++ b/apps/web/components/Security/HeaderProfileBox.tsx
@@ -1,23 +1,138 @@
'use client'
-import React, { useEffect } from 'react'
+import React, { useEffect, useMemo } from 'react'
import styled from 'styled-components'
import Link from 'next/link'
-import { Package2, Settings } from 'lucide-react'
+import { Package2, Settings, Crown, Shield, User, Users, Building, LogOut, User as UserIcon, Home, ChevronDown } from 'lucide-react'
import UserAvatar from '@components/Objects/UserAvatar'
import useAdminStatus from '@components/Hooks/useAdminStatus'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import { getUriWithoutOrg } from '@services/config/config'
import Tooltip from '@components/Objects/StyledElements/Tooltip/Tooltip'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@components/ui/dropdown-menu"
+import { signOut } from 'next-auth/react'
+
+interface RoleInfo {
+ name: string;
+ icon: React.ReactNode;
+ bgColor: string;
+ textColor: string;
+ description: string;
+}
+
+interface CustomRoleInfo {
+ name: string;
+ description?: string;
+}
export const HeaderProfileBox = () => {
const session = useLHSession() as any
- const isUserAdmin = useAdminStatus()
+ const { isAdmin, loading, userRoles, rights } = useAdminStatus()
const org = useOrg() as any
useEffect(() => { }
, [session])
+ const userRoleInfo = useMemo((): RoleInfo | null => {
+ if (!userRoles || userRoles.length === 0) return null;
+
+ // Find the highest priority role for the current organization
+ const orgRoles = userRoles.filter((role: any) => role.org.id === org?.id);
+
+ if (orgRoles.length === 0) return null;
+
+ // Sort by role priority (admin > maintainer > instructor > user)
+ const sortedRoles = orgRoles.sort((a: any, b: any) => {
+ const getRolePriority = (role: any) => {
+ if (role.role.role_uuid === 'role_global_admin' || role.role.id === 1) return 4;
+ if (role.role.role_uuid === 'role_global_maintainer' || role.role.id === 2) return 3;
+ if (role.role.role_uuid === 'role_global_instructor' || role.role.id === 3) return 2;
+ return 1;
+ };
+ return getRolePriority(b) - getRolePriority(a);
+ });
+
+ const highestRole = sortedRoles[0];
+
+ // Define role configurations based on actual database roles
+ const roleConfigs: { [key: string]: RoleInfo } = {
+ 'role_global_admin': {
+ name: 'ADMIN',
+ icon: ,
+ bgColor: 'bg-purple-600',
+ textColor: 'text-white',
+ description: 'Full platform control with all permissions'
+ },
+ 'role_global_maintainer': {
+ name: 'MAINTAINER',
+ icon: ,
+ bgColor: 'bg-blue-600',
+ textColor: 'text-white',
+ description: 'Mid-level manager with wide permissions'
+ },
+ 'role_global_instructor': {
+ name: 'INSTRUCTOR',
+ icon: ,
+ bgColor: 'bg-green-600',
+ textColor: 'text-white',
+ description: 'Can manage their own content'
+ },
+ 'role_global_user': {
+ name: 'USER',
+ icon: ,
+ bgColor: 'bg-gray-500',
+ textColor: 'text-white',
+ description: 'Read-Only Learner'
+ }
+ };
+
+ // Determine role based on role_uuid or id
+ let roleKey = 'role_global_user'; // default
+ if (highestRole.role.role_uuid) {
+ roleKey = highestRole.role.role_uuid;
+ } else if (highestRole.role.id === 1) {
+ roleKey = 'role_global_admin';
+ } else if (highestRole.role.id === 2) {
+ roleKey = 'role_global_maintainer';
+ } else if (highestRole.role.id === 3) {
+ roleKey = 'role_global_instructor';
+ }
+
+ return roleConfigs[roleKey] || roleConfigs['role_global_user'];
+ }, [userRoles, org?.id]);
+
+ const customRoles = useMemo((): CustomRoleInfo[] => {
+ if (!userRoles || userRoles.length === 0) return [];
+
+ // Find roles for the current organization
+ const orgRoles = userRoles.filter((role: any) => role.org.id === org?.id);
+
+ if (orgRoles.length === 0) return [];
+
+ // Filter for custom roles (not system roles)
+ const customRoles = orgRoles.filter((role: any) => {
+ // Check if it's a system role
+ const isSystemRole =
+ role.role.role_uuid?.startsWith('role_global_') ||
+ [1, 2, 3, 4].includes(role.role.id) ||
+ ['Admin', 'Maintainer', 'Instructor', 'User'].includes(role.role.name);
+
+ return !isSystemRole;
+ });
+
+ return customRoles.map((role: any) => ({
+ name: role.role.name || 'Custom Role',
+ description: role.role.description
+ }));
+ }, [userRoles, org?.id]);
+
return (
{session.status == 'unauthenticated' && (
@@ -35,35 +150,87 @@ export const HeaderProfileBox = () => {
)}
{session.status == 'authenticated' && (
-
-
-
{session.data.user.username}
- {isUserAdmin.isAdmin &&
ADMIN
}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
{session.data.user.username}
+
{session.data.user.email}
+
+
+
+
+ {rights?.dashboard?.action_access && (
+
+
+
+ Dashboard
+
+
+ )}
+
+
+
+ User Settings
+
+
+
+
+
+ My Courses
+
+
+
+ signOut({ callbackUrl: '/' })}
+ className="flex items-center space-x-2 text-red-600 focus:text-red-600"
+ >
+
+ Sign Out
+
+
+
)}
diff --git a/apps/web/package.json b/apps/web/package.json
index 1a4ddecb..3d30f81f 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -16,39 +16,39 @@
"@hello-pangea/dnd": "^18.0.1",
"@icons-pack/react-simple-icons": "^10.2.0",
"@radix-ui/colors": "^0.1.9",
- "@radix-ui/react-aspect-ratio": "^1.1.2",
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
"@radix-ui/react-checkbox": "^1.3.2",
- "@radix-ui/react-dialog": "^1.1.6",
- "@radix-ui/react-dropdown-menu": "^2.1.6",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-form": "^0.0.3",
- "@radix-ui/react-hover-card": "^1.1.6",
+ "@radix-ui/react-hover-card": "^1.1.14",
"@radix-ui/react-icons": "^1.3.2",
- "@radix-ui/react-label": "^2.1.2",
- "@radix-ui/react-select": "^2.1.6",
- "@radix-ui/react-slot": "^1.1.2",
- "@radix-ui/react-switch": "^1.1.3",
- "@radix-ui/react-tabs": "^1.1.3",
- "@radix-ui/react-toggle": "^1.1.2",
- "@radix-ui/react-toggle-group": "^1.1.2",
- "@radix-ui/react-tooltip": "^1.1.8",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "@radix-ui/react-toggle": "^1.1.9",
+ "@radix-ui/react-toggle-group": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.7",
"@stitches/react": "^1.2.8",
- "@tanstack/react-table": "^8.21.2",
- "@tiptap/core": "^2.11.7",
- "@tiptap/extension-bullet-list": "^2.11.7",
- "@tiptap/extension-code-block-lowlight": "^2.11.7",
- "@tiptap/extension-heading": "^2.12.0",
- "@tiptap/extension-link": "^2.11.7",
- "@tiptap/extension-list-item": "^2.11.7",
- "@tiptap/extension-ordered-list": "^2.11.7",
- "@tiptap/extension-table": "^2.11.7",
- "@tiptap/extension-table-cell": "^2.11.7",
- "@tiptap/extension-table-header": "^2.11.7",
- "@tiptap/extension-table-row": "^2.11.7",
- "@tiptap/extension-youtube": "^2.11.7",
- "@tiptap/html": "^2.11.7",
- "@tiptap/pm": "^2.11.7",
- "@tiptap/react": "^2.11.7",
- "@tiptap/starter-kit": "^2.11.7",
+ "@tanstack/react-table": "^8.21.3",
+ "@tiptap/core": "^2.26.1",
+ "@tiptap/extension-bullet-list": "^2.26.1",
+ "@tiptap/extension-code-block-lowlight": "^2.26.1",
+ "@tiptap/extension-heading": "^2.26.1",
+ "@tiptap/extension-link": "^2.26.1",
+ "@tiptap/extension-list-item": "^2.26.1",
+ "@tiptap/extension-ordered-list": "^2.26.1",
+ "@tiptap/extension-table": "^2.26.1",
+ "@tiptap/extension-table-cell": "^2.26.1",
+ "@tiptap/extension-table-header": "^2.26.1",
+ "@tiptap/extension-table-row": "^2.26.1",
+ "@tiptap/extension-youtube": "^2.26.1",
+ "@tiptap/html": "^2.26.1",
+ "@tiptap/pm": "^2.26.1",
+ "@tiptap/react": "^2.26.1",
+ "@tiptap/starter-kit": "^2.26.1",
"@types/dompurify": "^3.2.0",
"@types/html2canvas": "^1.0.0",
"@types/randomcolor": "^0.5.9",
@@ -57,16 +57,16 @@
"clsx": "^2.1.1",
"currency-codes": "^2.2.0",
"dayjs": "^1.11.13",
- "dompurify": "^3.2.5",
+ "dompurify": "^3.2.6",
"emblor": "^1.4.8",
"formik": "^2.4.6",
- "framer-motion": "^12.6.3",
+ "framer-motion": "^12.23.12",
"get-youtube-id": "^1.0.1",
"highlight.js": "^11.11.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"jspdf-html2canvas": "^1.5.2",
- "katex": "^0.16.21",
+ "katex": "^0.16.22",
"lowlight": "^3.3.0",
"lucide-react": "^0.453.0",
"next": "15.3.5",
@@ -81,23 +81,23 @@
"react-confetti": "^6.4.0",
"react-dom": "19.0.0",
"react-hot-toast": "^2.5.2",
- "react-katex": "^3.0.1",
+ "react-katex": "^3.1.0",
"react-plyr": "^2.2.0",
"react-spinners": "^0.13.8",
"react-youtube": "^10.1.0",
"require-in-the-middle": "^7.5.2",
"sharp": "^0.33.5",
- "styled-components": "^6.1.17",
- "swr": "^2.3.3",
+ "styled-components": "^6.1.19",
+ "swr": "^2.3.4",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"unsplash-js": "^7.0.19",
"usehooks-ts": "^3.1.1",
"uuid": "^9.0.1",
- "yup": "^1.6.1"
+ "yup": "^1.7.0"
},
"devDependencies": {
- "@tailwindcss/postcss": "^4.1.3",
+ "@tailwindcss/postcss": "^4.1.11",
"@types/node": "20.12.2",
"@types/qrcode": "^1.5.5",
"@types/react": "19.0.10",
@@ -106,11 +106,11 @@
"@types/react-transition-group": "^4.4.12",
"@types/styled-components": "^5.1.34",
"@types/uuid": "^9.0.8",
- "eslint": "^9.24.0",
+ "eslint": "^9.32.0",
"eslint-config-next": "15.2.1",
"eslint-plugin-unused-imports": "^3.2.0",
- "postcss": "^8.5.3",
- "tailwindcss": "^4.1.3",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.11",
"typescript": "5.4.4"
},
"pnpm": {
diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml
index 62594a84..b8f849be 100644
--- a/apps/web/pnpm-lock.yaml
+++ b/apps/web/pnpm-lock.yaml
@@ -28,104 +28,104 @@ importers:
specifier: ^0.1.9
version: 0.1.9
'@radix-ui/react-aspect-ratio':
- specifier: ^1.1.2
- version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.7
+ version: 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-checkbox':
specifier: ^1.3.2
version: 1.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-dialog':
- specifier: ^1.1.6
- version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.14
+ version: 1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-dropdown-menu':
- specifier: ^2.1.6
- version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^2.1.15
+ version: 2.1.15(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-form':
specifier: ^0.0.3
version: 0.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-hover-card':
- specifier: ^1.1.6
- version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.14
+ version: 1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@19.0.0)
'@radix-ui/react-label':
- specifier: ^2.1.2
- version: 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^2.1.7
+ version: 2.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-select':
- specifier: ^2.1.6
- version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^2.2.5
+ version: 2.2.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot':
- specifier: ^1.1.2
- version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ specifier: ^1.2.3
+ version: 1.2.3(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-switch':
- specifier: ^1.1.3
- version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.2.5
+ version: 1.2.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-tabs':
- specifier: ^1.1.3
- version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.12
+ version: 1.1.12(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-toggle':
- specifier: ^1.1.2
- version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.9
+ version: 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-toggle-group':
- specifier: ^1.1.2
- version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.10
+ version: 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-tooltip':
- specifier: ^1.1.8
- version: 1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.2.7
+ version: 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@stitches/react':
specifier: ^1.2.8
version: 1.2.8(react@19.0.0)
'@tanstack/react-table':
- specifier: ^8.21.2
- version: 8.21.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^8.21.3
+ version: 8.21.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@tiptap/core':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/pm@2.11.7)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/pm@2.26.1)
'@tiptap/extension-bullet-list':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-code-block-lowlight':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/extension-code-block@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)(highlight.js@11.11.1)(lowlight@3.3.0)
'@tiptap/extension-heading':
- specifier: ^2.12.0
- version: 2.12.0(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-link':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
'@tiptap/extension-list-item':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-ordered-list':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-table':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
'@tiptap/extension-table-cell':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-table-header':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-table-row':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/extension-youtube':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
'@tiptap/html':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
'@tiptap/pm':
- specifier: ^2.11.7
- version: 2.11.7
+ specifier: ^2.26.1
+ version: 2.26.1
'@tiptap/react':
- specifier: ^2.11.7
- version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^2.26.1
+ version: 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@tiptap/starter-kit':
- specifier: ^2.11.7
- version: 2.11.7
+ specifier: ^2.26.1
+ version: 2.26.1
'@types/dompurify':
specifier: ^3.2.0
version: 3.2.0
@@ -151,17 +151,17 @@ importers:
specifier: ^1.11.13
version: 1.11.13
dompurify:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.2.6
+ version: 3.2.6
emblor:
specifier: ^1.4.8
version: 1.4.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
formik:
specifier: ^2.4.6
- version: 2.4.6(react@19.0.0)
+ version: 2.4.6(@types/react@19.0.10)(react@19.0.0)
framer-motion:
- specifier: ^12.6.3
- version: 12.6.3(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^12.23.12
+ version: 12.23.12(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
get-youtube-id:
specifier: ^1.0.1
version: 1.0.1
@@ -178,8 +178,8 @@ importers:
specifier: ^1.5.2
version: 1.5.2
katex:
- specifier: ^0.16.21
- version: 0.16.21
+ specifier: ^0.16.22
+ version: 0.16.22
lowlight:
specifier: ^3.3.0
version: 3.3.0
@@ -223,8 +223,8 @@ importers:
specifier: ^2.5.2
version: 2.5.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-katex:
- specifier: ^3.0.1
- version: 3.0.1(prop-types@15.8.1)(react@19.0.0)
+ specifier: ^3.1.0
+ version: 3.1.0(prop-types@15.8.1)(react@19.0.0)
react-plyr:
specifier: ^2.2.0
version: 2.2.0(react@19.0.0)
@@ -241,17 +241,17 @@ importers:
specifier: ^0.33.5
version: 0.33.5
styled-components:
- specifier: ^6.1.17
- version: 6.1.17(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^6.1.19
+ version: 6.1.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
swr:
- specifier: ^2.3.3
- version: 2.3.3(react@19.0.0)
+ specifier: ^2.3.4
+ version: 2.3.4(react@19.0.0)
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
tailwindcss-animate:
specifier: ^1.0.7
- version: 1.0.7(tailwindcss@4.1.3)
+ version: 1.0.7(tailwindcss@4.1.11)
unsplash-js:
specifier: ^7.0.19
version: 7.0.19
@@ -262,12 +262,12 @@ importers:
specifier: ^9.0.1
version: 9.0.1
yup:
- specifier: ^1.6.1
- version: 1.6.1
+ specifier: ^1.7.0
+ version: 1.7.0
devDependencies:
'@tailwindcss/postcss':
- specifier: ^4.1.3
- version: 4.1.3
+ specifier: ^4.1.11
+ version: 4.1.11
'@types/node':
specifier: 20.12.2
version: 20.12.2
@@ -293,20 +293,20 @@ importers:
specifier: ^9.0.8
version: 9.0.8
eslint:
- specifier: ^9.24.0
- version: 9.24.0(jiti@2.4.2)
+ specifier: ^9.32.0
+ version: 9.32.0(jiti@2.5.1)
eslint-config-next:
specifier: 15.2.1
- version: 15.2.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
+ version: 15.2.1(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
eslint-plugin-unused-imports:
specifier: ^3.2.0
- version: 3.2.0(eslint@9.24.0(jiti@2.4.2))
+ version: 3.2.0(eslint@9.32.0(jiti@2.5.1))
postcss:
- specifier: ^8.5.3
- version: 8.5.3
+ specifier: ^8.5.6
+ version: 8.5.6
tailwindcss:
- specifier: ^4.1.3
- version: 4.1.3
+ specifier: ^4.1.11
+ version: 4.1.11
typescript:
specifier: 5.4.4
version: 5.4.4
@@ -317,18 +317,22 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
- '@babel/runtime@7.27.0':
- resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@babel/runtime@7.28.2':
+ resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
engines: {node: '>=6.9.0'}
- '@emnapi/core@1.4.0':
- resolution: {integrity: sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==}
+ '@emnapi/core@1.4.5':
+ resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==}
- '@emnapi/runtime@1.4.0':
- resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==}
+ '@emnapi/runtime@1.4.5':
+ resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==}
- '@emnapi/wasi-threads@1.0.1':
- resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==}
+ '@emnapi/wasi-threads@1.0.4':
+ resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==}
'@emoji-mart/data@1.2.1':
resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==}
@@ -348,8 +352,8 @@ packages:
'@emotion/unitless@0.8.1':
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
- '@eslint-community/eslint-utils@4.5.1':
- resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
+ '@eslint-community/eslint-utils@4.7.0':
+ resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
@@ -358,52 +362,48 @@ packages:
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
- '@eslint/config-array@0.20.0':
- resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==}
+ '@eslint/config-array@0.21.0':
+ resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/config-helpers@0.2.1':
- resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
+ '@eslint/config-helpers@0.3.0':
+ resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/core@0.12.0':
- resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@eslint/core@0.13.0':
- resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
+ '@eslint/core@0.15.1':
+ resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.1':
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.24.0':
- resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==}
+ '@eslint/js@9.32.0':
+ resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.6':
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.2.8':
- resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
+ '@eslint/plugin-kit@0.3.4':
+ resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@floating-ui/core@1.6.9':
- resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
+ '@floating-ui/core@1.7.3':
+ resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
- '@floating-ui/dom@1.6.13':
- resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==}
+ '@floating-ui/dom@1.7.3':
+ resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==}
- '@floating-ui/react-dom@2.1.2':
- resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
+ '@floating-ui/react-dom@2.1.5':
+ resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
- '@floating-ui/utils@0.2.9':
- resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
'@hello-pangea/dnd@18.0.1':
resolution: {integrity: sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==}
@@ -427,8 +427,8 @@ packages:
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
engines: {node: '>=18.18'}
- '@humanwhocodes/retry@0.4.2':
- resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
'@icons-pack/react-simple-icons@10.2.0':
@@ -442,8 +442,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@img/sharp-darwin-arm64@0.34.1':
- resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==}
+ '@img/sharp-darwin-arm64@0.34.3':
+ resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
@@ -454,8 +454,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@img/sharp-darwin-x64@0.34.1':
- resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==}
+ '@img/sharp-darwin-x64@0.34.3':
+ resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
@@ -465,8 +465,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@img/sharp-libvips-darwin-arm64@1.1.0':
- resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
+ resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
cpu: [arm64]
os: [darwin]
@@ -475,8 +475,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@img/sharp-libvips-darwin-x64@1.1.0':
- resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
+ '@img/sharp-libvips-darwin-x64@1.2.0':
+ resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
cpu: [x64]
os: [darwin]
@@ -485,8 +485,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@img/sharp-libvips-linux-arm64@1.1.0':
- resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
+ '@img/sharp-libvips-linux-arm64@1.2.0':
+ resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
cpu: [arm64]
os: [linux]
@@ -495,13 +495,13 @@ packages:
cpu: [arm]
os: [linux]
- '@img/sharp-libvips-linux-arm@1.1.0':
- resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
+ '@img/sharp-libvips-linux-arm@1.2.0':
+ resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
cpu: [arm]
os: [linux]
- '@img/sharp-libvips-linux-ppc64@1.1.0':
- resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
+ resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
cpu: [ppc64]
os: [linux]
@@ -510,8 +510,8 @@ packages:
cpu: [s390x]
os: [linux]
- '@img/sharp-libvips-linux-s390x@1.1.0':
- resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
+ '@img/sharp-libvips-linux-s390x@1.2.0':
+ resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
cpu: [s390x]
os: [linux]
@@ -520,8 +520,8 @@ packages:
cpu: [x64]
os: [linux]
- '@img/sharp-libvips-linux-x64@1.1.0':
- resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
+ '@img/sharp-libvips-linux-x64@1.2.0':
+ resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
cpu: [x64]
os: [linux]
@@ -530,8 +530,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
- resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
+ resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
cpu: [arm64]
os: [linux]
@@ -540,8 +540,8 @@ packages:
cpu: [x64]
os: [linux]
- '@img/sharp-libvips-linuxmusl-x64@1.1.0':
- resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
+ resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
cpu: [x64]
os: [linux]
@@ -551,8 +551,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@img/sharp-linux-arm64@0.34.1':
- resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==}
+ '@img/sharp-linux-arm64@0.34.3':
+ resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
@@ -563,20 +563,26 @@ packages:
cpu: [arm]
os: [linux]
- '@img/sharp-linux-arm@0.34.1':
- resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==}
+ '@img/sharp-linux-arm@0.34.3':
+ resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
+ '@img/sharp-linux-ppc64@0.34.3':
+ resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
- '@img/sharp-linux-s390x@0.34.1':
- resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==}
+ '@img/sharp-linux-s390x@0.34.3':
+ resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
@@ -587,8 +593,8 @@ packages:
cpu: [x64]
os: [linux]
- '@img/sharp-linux-x64@0.34.1':
- resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==}
+ '@img/sharp-linux-x64@0.34.3':
+ resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
@@ -599,8 +605,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@img/sharp-linuxmusl-arm64@0.34.1':
- resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==}
+ '@img/sharp-linuxmusl-arm64@0.34.3':
+ resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
@@ -611,8 +617,8 @@ packages:
cpu: [x64]
os: [linux]
- '@img/sharp-linuxmusl-x64@0.34.1':
- resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==}
+ '@img/sharp-linuxmusl-x64@0.34.3':
+ resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
@@ -622,19 +628,25 @@ packages:
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
- '@img/sharp-wasm32@0.34.1':
- resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==}
+ '@img/sharp-wasm32@0.34.3':
+ resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
+ '@img/sharp-win32-arm64@0.34.3':
+ resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
'@img/sharp-win32-ia32@0.33.5':
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
- '@img/sharp-win32-ia32@0.34.1':
- resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==}
+ '@img/sharp-win32-ia32@0.34.3':
+ resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
@@ -645,14 +657,31 @@ packages:
cpu: [x64]
os: [win32]
- '@img/sharp-win32-x64@0.34.1':
- resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==}
+ '@img/sharp-win32-x64@0.34.3':
+ resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
- '@napi-rs/wasm-runtime@0.2.8':
- resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.12':
+ resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.4':
+ resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+ '@jridgewell/trace-mapping@0.3.29':
+ resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@next/env@15.3.5':
resolution: {integrity: sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==}
@@ -737,8 +766,8 @@ packages:
'@radix-ui/colors@0.1.9':
resolution: {integrity: sha512-Vxq944ErPJsdVepjEUhOLO9ApUVOocA63knc+V2TkJ09D/AVOjiMIgkca/7VoYgODcla0qbSIBjje0SMfZMbAw==}
- '@radix-ui/number@1.1.0':
- resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
+ '@radix-ui/number@1.1.1':
+ resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
'@radix-ui/primitive@1.0.0':
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
@@ -746,14 +775,11 @@ packages:
'@radix-ui/primitive@1.0.1':
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
- '@radix-ui/primitive@1.1.1':
- resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==}
-
'@radix-ui/primitive@1.1.2':
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
- '@radix-ui/react-arrow@1.1.2':
- resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==}
+ '@radix-ui/react-arrow@1.1.7':
+ resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -765,8 +791,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-aspect-ratio@1.1.2':
- resolution: {integrity: sha512-TaJxYoCpxJ7vfEkv2PTNox/6zzmpKXT6ewvCuf2tTOIVN45/Jahhlld29Yw4pciOXS2Xq91/rSGEdmEnUWZCqA==}
+ '@radix-ui/react-aspect-ratio@1.1.7':
+ resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -791,8 +817,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-collection@1.1.2':
- resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==}
+ '@radix-ui/react-collection@1.1.7':
+ resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -818,15 +844,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-compose-refs@1.1.1':
- resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-compose-refs@1.1.2':
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
peerDependencies:
@@ -850,15 +867,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-context@1.1.1':
- resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-context@1.1.2':
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
peerDependencies:
@@ -887,8 +895,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-dialog@1.1.6':
- resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==}
+ '@radix-ui/react-dialog@1.1.14':
+ resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -900,8 +908,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-direction@1.1.0':
- resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
+ '@radix-ui/react-direction@1.1.1':
+ resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -928,8 +936,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-dismissable-layer@1.1.5':
- resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==}
+ '@radix-ui/react-dismissable-layer@1.1.10':
+ resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -941,8 +949,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-dropdown-menu@2.1.6':
- resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==}
+ '@radix-ui/react-dropdown-menu@2.1.15':
+ resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -968,8 +976,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-focus-guards@1.1.1':
- resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
+ '@radix-ui/react-focus-guards@1.1.2':
+ resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -996,8 +1004,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-focus-scope@1.1.2':
- resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==}
+ '@radix-ui/react-focus-scope@1.1.7':
+ resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1022,8 +1030,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-hover-card@1.1.6':
- resolution: {integrity: sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ==}
+ '@radix-ui/react-hover-card@1.1.14':
+ resolution: {integrity: sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1054,8 +1062,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-id@1.1.0':
- resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -1076,8 +1084,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-label@2.1.2':
- resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==}
+ '@radix-ui/react-label@2.1.7':
+ resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1089,8 +1097,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-menu@2.1.6':
- resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==}
+ '@radix-ui/react-menu@2.1.15':
+ resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1102,8 +1110,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-popover@1.1.6':
- resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==}
+ '@radix-ui/react-popover@1.1.14':
+ resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1115,8 +1123,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-popper@1.2.2':
- resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==}
+ '@radix-ui/react-popper@1.2.7':
+ resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1147,8 +1155,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-portal@1.1.4':
- resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==}
+ '@radix-ui/react-portal@1.1.9':
+ resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1179,19 +1187,6 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-presence@1.1.2':
- resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==}
- peerDependencies:
- '@types/react': 19.0.10
- '@types/react-dom': 19.0.4
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
'@radix-ui/react-presence@1.1.4':
resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==}
peerDependencies:
@@ -1224,19 +1219,6 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-primitive@2.0.2':
- resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==}
- peerDependencies:
- '@types/react': 19.0.10
- '@types/react-dom': 19.0.4
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
'@radix-ui/react-primitive@2.1.3':
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
peerDependencies:
@@ -1250,8 +1232,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-roving-focus@1.1.2':
- resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==}
+ '@radix-ui/react-roving-focus@1.1.10':
+ resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1263,8 +1245,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-select@2.1.6':
- resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==}
+ '@radix-ui/react-select@2.2.5':
+ resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1290,15 +1272,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-slot@1.1.2':
- resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-slot@1.2.3':
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
peerDependencies:
@@ -1308,8 +1281,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-switch@1.1.3':
- resolution: {integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==}
+ '@radix-ui/react-switch@1.2.5':
+ resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1321,8 +1294,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-tabs@1.1.3':
- resolution: {integrity: sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==}
+ '@radix-ui/react-tabs@1.1.12':
+ resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1334,8 +1307,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-toggle-group@1.1.2':
- resolution: {integrity: sha512-JBm6s6aVG/nwuY5eadhU2zDi/IwYS0sDM5ZWb4nymv/hn3hZdkw+gENn0LP4iY1yCd7+bgJaCwueMYJIU3vk4A==}
+ '@radix-ui/react-toggle-group@1.1.10':
+ resolution: {integrity: sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1347,8 +1320,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-toggle@1.1.2':
- resolution: {integrity: sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ==}
+ '@radix-ui/react-toggle@1.1.9':
+ resolution: {integrity: sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1360,8 +1333,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-tooltip@1.1.8':
- resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==}
+ '@radix-ui/react-tooltip@1.2.7':
+ resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1387,8 +1360,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-callback-ref@1.1.0':
- resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
+ '@radix-ui/react-use-callback-ref@1.1.1':
+ resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -1410,15 +1383,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-controllable-state@1.1.0':
- resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-use-controllable-state@1.2.2':
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
peerDependencies:
@@ -1451,8 +1415,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-escape-keydown@1.1.0':
- resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
+ '@radix-ui/react-use-escape-keydown@1.1.1':
+ resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -1474,15 +1438,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-layout-effect@1.1.0':
- resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-use-layout-effect@1.1.1':
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
peerDependencies:
@@ -1492,15 +1447,6 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-previous@1.1.0':
- resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@radix-ui/react-use-previous@1.1.1':
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
peerDependencies:
@@ -1510,17 +1456,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-use-rect@1.1.0':
- resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
- peerDependencies:
- '@types/react': 19.0.10
- react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
- '@radix-ui/react-use-size@1.1.0':
- resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==}
+ '@radix-ui/react-use-rect@1.1.1':
+ resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
'@types/react': 19.0.10
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
@@ -1537,8 +1474,8 @@ packages:
'@types/react':
optional: true
- '@radix-ui/react-visually-hidden@1.1.2':
- resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==}
+ '@radix-ui/react-visually-hidden@1.2.3':
+ resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
peerDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4
@@ -1550,8 +1487,8 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/rect@1.1.0':
- resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
+ '@radix-ui/rect@1.1.1':
+ resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
'@remirror/core-constants@3.0.0':
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
@@ -1559,8 +1496,8 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
- '@rushstack/eslint-patch@1.11.0':
- resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
+ '@rushstack/eslint-patch@1.12.0':
+ resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==}
'@stitches/react@1.2.8':
resolution: {integrity: sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==}
@@ -1573,121 +1510,133 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
- '@tailwindcss/node@4.1.3':
- resolution: {integrity: sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==}
+ '@tailwindcss/node@4.1.11':
+ resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
- '@tailwindcss/oxide-android-arm64@4.1.3':
- resolution: {integrity: sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==}
+ '@tailwindcss/oxide-android-arm64@4.1.11':
+ resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.1.3':
- resolution: {integrity: sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==}
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
+ resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.1.3':
- resolution: {integrity: sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==}
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
+ resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.1.3':
- resolution: {integrity: sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==}
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
+ resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
- resolution: {integrity: sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
+ resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
- resolution: {integrity: sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
+ resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-musl@4.1.3':
- resolution: {integrity: sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
+ resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-gnu@4.1.3':
- resolution: {integrity: sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
+ resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-linux-x64-musl@4.1.3':
- resolution: {integrity: sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==}
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
+ resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
- resolution: {integrity: sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==}
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
+ resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
+ resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.1.3':
- resolution: {integrity: sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.1.3':
- resolution: {integrity: sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==}
+ '@tailwindcss/oxide@4.1.11':
+ resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
engines: {node: '>= 10'}
- '@tailwindcss/postcss@4.1.3':
- resolution: {integrity: sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==}
+ '@tailwindcss/postcss@4.1.11':
+ resolution: {integrity: sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==}
- '@tanstack/react-table@8.21.2':
- resolution: {integrity: sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg==}
+ '@tanstack/react-table@8.21.3':
+ resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
- '@tanstack/table-core@8.21.2':
- resolution: {integrity: sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==}
+ '@tanstack/table-core@8.21.3':
+ resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
- '@tiptap/core@2.11.7':
- resolution: {integrity: sha512-zN+NFFxLsxNEL8Qioc+DL6b8+Tt2bmRbXH22Gk6F6nD30x83eaUSFlSv3wqvgyCq3I1i1NO394So+Agmayx6rQ==}
+ '@tiptap/core@2.26.1':
+ resolution: {integrity: sha512-fymyd/XZvYiHjBoLt1gxs024xP/LY26d43R1vluYq7AHBL/7DE3ywzy+1GEsGyAv5Je2L0KBhNIR/izbq3Kaqg==}
peerDependencies:
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-blockquote@2.11.7':
- resolution: {integrity: sha512-liD8kWowl3CcYCG9JQlVx1eSNc/aHlt6JpVsuWvzq6J8APWX693i3+zFqyK2eCDn0k+vW62muhSBe3u09hA3Zw==}
+ '@tiptap/extension-blockquote@2.26.1':
+ resolution: {integrity: sha512-viQ6AHRhjCYYipKK6ZepBzwZpkuMvO9yhRHeUZDvlSOAh8rvsUTSre0y74nu8QRYUt4a44lJJ6BpphJK7bEgYA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-bold@2.11.7':
- resolution: {integrity: sha512-VTR3JlldBixXbjpLTFme/Bxf1xeUgZZY3LTlt5JDlCW3CxO7k05CIa+kEZ8LXpog5annytZDUVtWqxrNjmsuHQ==}
+ '@tiptap/extension-bold@2.26.1':
+ resolution: {integrity: sha512-zCce9PRuTNhadFir71luLo99HERDpGJ0EEflGm7RN8I1SnNi9gD5ooK42BOIQtejGCJqg3hTPZiYDJC2hXvckQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-bubble-menu@2.11.7':
- resolution: {integrity: sha512-0vYqSUSSap3kk3/VT4tFE1/6StX70I3/NKQ4J68ZSFgkgyB3ZVlYv7/dY3AkEukjsEp3yN7m8Gw8ei2eEwyzwg==}
+ '@tiptap/extension-bubble-menu@2.26.1':
+ resolution: {integrity: sha512-oHevUcZbTMFOTpdCEo4YEDe044MB4P1ZrWyML8CGe5tnnKdlI9BN03AXpI1mEEa5CA3H1/eEckXx8EiCgYwQ3Q==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-bullet-list@2.11.7':
- resolution: {integrity: sha512-WbPogE2/Q3e3/QYgbT1Sj4KQUfGAJNc5pvb7GrUbvRQsAh7HhtuO8hqdDwH8dEdD/cNUehgt17TO7u8qV6qeBw==}
+ '@tiptap/extension-bullet-list@2.26.1':
+ resolution: {integrity: sha512-HHakuV4ckYCDOnBbne088FvCEP4YICw+wgPBz/V2dfpiFYQ4WzT0LPK9s7OFMCN+ROraoug+1ryN1Z1KdIgujQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-code-block-lowlight@2.11.7':
- resolution: {integrity: sha512-+eUMxvDgoYmAvkuJ2ljV2COyeH6HwH8LqCNWma+mFZCRDAoXNeqSHbBtI0Vzy4PqchfmxcmKERc99xEzoS9XUQ==}
+ '@tiptap/extension-code-block-lowlight@2.26.1':
+ resolution: {integrity: sha512-yptuTPYAzVMKHUTwNKYveuu0rYHYyFknPz3O2++PWeeBGxkNB+T6LhwZ/JhXceHcZxzlGyka9r2mXR7pslhugw==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-code-block': ^2.7.0
@@ -1695,164 +1644,166 @@ packages:
highlight.js: ^11
lowlight: ^2 || ^3
- '@tiptap/extension-code-block@2.11.7':
- resolution: {integrity: sha512-To/y/2H04VWqiANy53aXjV7S6fA86c2759RsH1hTIe57jA1KyE7I5tlAofljOLZK/covkGmPeBddSPHGJbz++Q==}
+ '@tiptap/extension-code-block@2.26.1':
+ resolution: {integrity: sha512-/TDDOwONl0qEUc4+B6V9NnWtSjz95eg7/8uCb8Y8iRbGvI9vT4/znRKofFxstvKmW4URu/H74/g0ywV57h0B+A==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-code@2.11.7':
- resolution: {integrity: sha512-VpPO1Uy/eF4hYOpohS/yMOcE1C07xmMj0/D989D9aS1x95jWwUVrSkwC+PlWMUBx9PbY2NRsg1ZDwVvlNKZ6yQ==}
+ '@tiptap/extension-code@2.26.1':
+ resolution: {integrity: sha512-GU9deB1A/Tr4FMPu71CvlcjGKwRhGYz60wQ8m4aM+ELZcVIcZRa1ebR8bExRIEWnvRztQuyRiCQzw2N0xQJ1QQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-document@2.11.7':
- resolution: {integrity: sha512-95ouJXPjdAm9+VBRgFo4lhDoMcHovyl/awORDI8gyEn0Rdglt+ZRZYoySFzbVzer9h0cre+QdIwr9AIzFFbfdA==}
+ '@tiptap/extension-document@2.26.1':
+ resolution: {integrity: sha512-2P2IZp1NRAE+21mRuFBiP3X2WKfZ6kUC23NJKpn8bcOamY3obYqCt0ltGPhE4eR8n8QAl2fI/3jIgjR07dC8ow==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-dropcursor@2.11.7':
- resolution: {integrity: sha512-63mL+nxQILizsr5NbmgDeOjFEWi34BLt7evwL6UUZEVM15K8V1G8pD9Y0kCXrZYpHWz0tqFRXdrhDz0Ppu8oVw==}
+ '@tiptap/extension-dropcursor@2.26.1':
+ resolution: {integrity: sha512-JkDQU2ZYFOuT5mNYb8OiWGwD1HcjbtmX8tLNugQbToECmz9WvVPqJmn7V/q8VGpP81iEECz/IsyRmuf2kSD4uA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-floating-menu@2.11.7':
- resolution: {integrity: sha512-DG54WoUu2vxHRVzKZiR5I5RMOYj45IlxQMkBAx1wjS0ch41W8DUYEeipvMMjCeKtEI+emz03xYUcOAP9LRmg+w==}
+ '@tiptap/extension-floating-menu@2.26.1':
+ resolution: {integrity: sha512-OJF+H6qhQogVTMedAGSWuoL1RPe3LZYXONuFCVyzHnvvMpK+BP1vm180E2zDNFnn/DVA+FOrzNGpZW7YjoFH1w==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-gapcursor@2.11.7':
- resolution: {integrity: sha512-EceesmPG7FyjXZ8EgeJPUov9G1mAf2AwdypxBNH275g6xd5dmU/KvjoFZjmQ0X1ve7mS+wNupVlGxAEUYoveew==}
+ '@tiptap/extension-gapcursor@2.26.1':
+ resolution: {integrity: sha512-KOiMZc3PwJS3hR0nSq5d0TJi2jkNZkLZElcT6pCEnhRHzPH6dRMu9GM5Jj798ZRUy0T9UFcKJalFZaDxnmRnpg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-hard-break@2.11.7':
- resolution: {integrity: sha512-zTkZSA6q+F5sLOdCkiC2+RqJQN0zdsJqvFIOVFL/IDVOnq6PZO5THzwRRLvOSnJJl3edRQCl/hUgS0L5sTInGQ==}
+ '@tiptap/extension-hard-break@2.26.1':
+ resolution: {integrity: sha512-d6uStdNKi8kjPlHAyO59M6KGWATNwhLCD7dng0NXfwGndc22fthzIk/6j9F6ltQx30huy5qQram6j3JXwNACoA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-heading@2.12.0':
- resolution: {integrity: sha512-9DfES4Wd5TX1foI70N9sAL+35NN1UHrtzDYN2+dTHupnmKir9RaMXyZcbkUb4aDVzYrGxIqxJzHBVkquKIlTrw==}
+ '@tiptap/extension-heading@2.26.1':
+ resolution: {integrity: sha512-KSzL8WZV3pjJG9ke4RaU70+B5UlYR2S6olNt5UCAawM+fi11mobVztiBoC19xtpSVqIXC1AmXOqUgnuSvmE4ZA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-history@2.11.7':
- resolution: {integrity: sha512-Cu5x3aS13I040QSRoLdd+w09G4OCVfU+azpUqxufZxeNs9BIJC+0jowPLeOxKDh6D5GGT2A8sQtxc6a/ssbs8g==}
+ '@tiptap/extension-history@2.26.1':
+ resolution: {integrity: sha512-m6YR1gkkauIDo3PRl0gP+7Oc4n5OqDzcjVh6LvWREmZP8nmi94hfseYbqOXUb6RPHIc0JKF02eiRifT4MSd2nw==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-horizontal-rule@2.11.7':
- resolution: {integrity: sha512-uVmQwD2dzZ5xwmvUlciy0ItxOdOfQjH6VLmu80zyJf8Yu7mvwP8JyxoXUX0vd1xHpwAhgQ9/ozjIWYGIw79DPQ==}
+ '@tiptap/extension-horizontal-rule@2.26.1':
+ resolution: {integrity: sha512-mT6baqOhs/NakgrAeDeed194E/ZJFGL692H0C7f1N7WDRaWxUu2oR0LrnRqSH5OyPjELkzu6nQnNy0+0tFGHHg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-italic@2.11.7':
- resolution: {integrity: sha512-r985bkQfG0HMpmCU0X0p/Xe7U1qgRm2mxvcp6iPCuts2FqxaCoyfNZ8YnMsgVK1mRhM7+CQ5SEg2NOmQNtHvPw==}
+ '@tiptap/extension-italic@2.26.1':
+ resolution: {integrity: sha512-pOs6oU4LyGO89IrYE4jbE8ZYsPwMMIiKkYfXcfeD9NtpGNBnjeVXXF5I9ndY2ANrCAgC8k58C3/powDRf0T2yA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-link@2.11.7':
- resolution: {integrity: sha512-qKIowE73aAUrnQCIifYP34xXOHOsZw46cT/LBDlb0T60knVfQoKVE4ku08fJzAV+s6zqgsaaZ4HVOXkQYLoW7g==}
+ '@tiptap/extension-link@2.26.1':
+ resolution: {integrity: sha512-7yfum5Jymkue/uOSTQPt2SmkZIdZx7t3QhZLqBU7R9ettkdSCBgEGok6N+scJM1R1Zes+maSckLm0JZw5BKYNA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-list-item@2.11.7':
- resolution: {integrity: sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==}
+ '@tiptap/extension-list-item@2.26.1':
+ resolution: {integrity: sha512-quOXckC73Luc3x+Dcm88YAEBW+Crh3x5uvtQOQtn2GEG91AshrvbnhGRiYnfvEN7UhWIS+FYI5liHFcRKSUKrQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-ordered-list@2.11.7':
- resolution: {integrity: sha512-bLGCHDMB0vbJk7uu8bRg8vES3GsvxkX7Cgjgm/6xysHFbK98y0asDtNxkW1VvuRreNGz4tyB6vkcVCfrxl4jKw==}
+ '@tiptap/extension-ordered-list@2.26.1':
+ resolution: {integrity: sha512-UHKNRxq6TBnXMGFSq91knD6QaHsyyOwLOsXMzupmKM5Su0s+CRXEjfav3qKlbb9e4m7D7S/a0aPm8nC9KIXNhQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-paragraph@2.11.7':
- resolution: {integrity: sha512-Pl3B4q6DJqTvvAdraqZaNP9Hh0UWEHL5nNdxhaRNuhKaUo7lq8wbDSIxIW3lvV0lyCs0NfyunkUvSm1CXb6d4Q==}
+ '@tiptap/extension-paragraph@2.26.1':
+ resolution: {integrity: sha512-UezvM9VDRAVJlX1tykgHWSD1g3MKfVMWWZ+Tg+PE4+kizOwoYkRWznVPgCAxjmyHajxpCKRXgqTZkOxjJ9Kjzg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-strike@2.11.7':
- resolution: {integrity: sha512-D6GYiW9F24bvAY7XMOARNZbC8YGPzdzWdXd8VOOJABhf4ynMi/oW4NNiko+kZ67jn3EGaKoz32VMJzNQgYi1HA==}
+ '@tiptap/extension-strike@2.26.1':
+ resolution: {integrity: sha512-CkoRH+pAi6MgdCh7K0cVZl4N2uR4pZdabXAnFSoLZRSg6imLvEUmWHfSi1dl3Z7JOvd3a4yZ4NxerQn5MWbJ7g==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-cell@2.11.7':
- resolution: {integrity: sha512-JMOkSYRckc5SJP86yGGiHzCxCR8ecrRENvTWAKib6qer2tutxs5u42W+Z8uTcHC2dRz7Fv54snOkDoqPwkf6cw==}
+ '@tiptap/extension-table-cell@2.26.1':
+ resolution: {integrity: sha512-0P5zY+WGFnULggJkX6+CevmFoBmVv1aUiBBXfcFuLG2mnUsS3QALQTowFtz/0/VbtbjzcOSStaGDHRJxPbk9XQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-header@2.11.7':
- resolution: {integrity: sha512-wPRKpliS5QQXgsp//ZjXrHMdLICMkjg2fUrQinOiBa7wDL5C7Y+SehtuK4s2tjeAkyAdj+nepfftyBRIlUSMXg==}
+ '@tiptap/extension-table-header@2.26.1':
+ resolution: {integrity: sha512-SAwTW9H+sjVYjoeU5z8pVDMHn3r3FCi+zp2KAxsEsmujcd7qrQdY0cAjQtWjckCq6H3sQkbICa+xlCCd7C8ZAQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-row@2.11.7':
- resolution: {integrity: sha512-K254RiXWGXGjz5Cm835hqfQiwnYXm8aw6oOa3isDh4A1B+1Ev4DB2vEDKMrgaOor3nbTsSYmAx2iEMrZSbpaRg==}
+ '@tiptap/extension-table-row@2.26.1':
+ resolution: {integrity: sha512-c4oLrUfj1EVVDpbfKX36v7nnaeI4NxML2KRTQXocvcY65VCe0bPQh8ujpPgPcnKEzdWYdIuAX9RbEAkiYWe8Ww==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table@2.11.7':
- resolution: {integrity: sha512-rfwWkNXz/EZuhc8lylsCWPbx0Xr5FlIhreWFyeoXYrDEO3x4ytYcVOpNmbabJYP2semfM0PvPR5o84zfFkLZyg==}
+ '@tiptap/extension-table@2.26.1':
+ resolution: {integrity: sha512-LQ63CK53qx2ZsbLTB4mUX0YCoGC0GbYQ82jS3kD+K7M/mb9MCkefvDk6rA8rXF8TjfGnv6o/Fseoot8uhH3qfg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-text-style@2.11.7':
- resolution: {integrity: sha512-LHO6DBg/9SkCQFdWlVfw9nolUmw+Cid94WkTY+7IwrpyG2+ZGQxnKpCJCKyeaFNbDoYAtvu0vuTsSXeCkgShcA==}
+ '@tiptap/extension-text-style@2.26.1':
+ resolution: {integrity: sha512-t9Nc/UkrbCfnSHEUi1gvUQ2ZPzvfdYFT5TExoV2DTiUCkhG6+mecT5bTVFGW3QkPmbToL+nFhGn4ZRMDD0SP3Q==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-text@2.11.7':
- resolution: {integrity: sha512-wObCn8qZkIFnXTLvBP+X8KgaEvTap/FJ/i4hBMfHBCKPGDx99KiJU6VIbDXG8d5ZcFZE0tOetK1pP5oI7qgMlQ==}
+ '@tiptap/extension-text@2.26.1':
+ resolution: {integrity: sha512-p2n8WVMd/2vckdJlol24acaTDIZAhI7qle5cM75bn01sOEZoFlSw6SwINOULrUCzNJsYb43qrLEibZb4j2LeQw==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-youtube@2.11.7':
- resolution: {integrity: sha512-Cf8rziGNIVdsQXGO6nIYcpG5wP6RO+7VkpLZcVYo2I2KvEaXv+3Nb3mG4X+F7AClPfCEIE4G2EIVCsh3DCZ96A==}
+ '@tiptap/extension-youtube@2.26.1':
+ resolution: {integrity: sha512-AxjF/kSO4R93OLJ+DNCTNNxrYSx4aEB9nnrqa8WAmoFw1+NfKyGMbGYRg2kwBI7Q9g6100hW+85vPo430clHDQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/html@2.11.7':
- resolution: {integrity: sha512-mNe3lQvbB78b1KmxjBdNK4AWkzkKmODtLMsKbNTaaKqDuPRjI9bcYkUqnr/4mw/B1EF+iafHHj5E3JfutRvC2w==}
+ '@tiptap/html@2.26.1':
+ resolution: {integrity: sha512-93keIjciRdjgQabxntXBjW6NTrAoM+uC/u3dwoM5CJFs40cpJC+/CEPYG7SJ5Cyv8srv/OqzyjCjoz7PQGvXyA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/pm@2.11.7':
- resolution: {integrity: sha512-7gEEfz2Q6bYKXM07vzLUD0vqXFhC5geWRA6LCozTiLdVFDdHWiBrvb2rtkL5T7mfLq03zc1QhH7rI3F6VntOEA==}
+ '@tiptap/pm@2.26.1':
+ resolution: {integrity: sha512-8aF+mY/vSHbGFqyG663ds84b+vca5Lge3tHdTMTKazxCnhXR9dn2oQJMnZ78YZvdRbkPkMJJHti9h3K7u2UQvw==}
- '@tiptap/react@2.11.7':
- resolution: {integrity: sha512-gQZEUkAoPsBptnB4T2gAtiUxswjVGhfsM9vOElQco+b11DYmy110T2Zuhg+2YGvB/CG3RoWJx34808P0FX1ijA==}
+ '@tiptap/react@2.26.1':
+ resolution: {integrity: sha512-Zxlwzi1iML7aELa+PyysFD2ncVo2mEcjTkhoDok9iTbMGpm1oU8hgR1i6iHrcSNQLfaRiW6M7HNhZZQPKIC9yw==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
- '@tiptap/starter-kit@2.11.7':
- resolution: {integrity: sha512-K+q51KwNU/l0kqRuV5e1824yOLVftj6kGplGQLvJG56P7Rb2dPbM/JeaDbxQhnHT/KDGamG0s0Po0M3pPY163A==}
+ '@tiptap/starter-kit@2.26.1':
+ resolution: {integrity: sha512-oziMGCds8SVQ3s5dRpBxVdEKZAmO/O//BjZ69mhA3q4vJdR0rnfLb5fTxSeQvHiqB878HBNn76kNaJrHrV35GA==}
- '@tybys/wasm-util@0.9.0':
- resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+ '@tybys/wasm-util@0.10.0':
+ resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==}
'@types/dompurify@3.2.0':
resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==}
deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.
- '@types/estree@1.0.7':
- resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
- '@types/hoist-non-react-statics@3.3.6':
- resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==}
+ '@types/hoist-non-react-statics@3.3.7':
+ resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==}
+ peerDependencies:
+ '@types/react': 19.0.10
'@types/html2canvas@1.0.0':
resolution: {integrity: sha512-BJpVf+FIN9UERmzhbtUgpXj6XBZpG67FMgBLLoj9HZKd9XifcCpSV+UnFcwTZfEyun4U/KmCrrVOG7829L589w==}
@@ -1919,125 +1870,157 @@ packages:
'@types/uuid@9.0.8':
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
- '@typescript-eslint/eslint-plugin@8.29.0':
- resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==}
+ '@typescript-eslint/eslint-plugin@8.39.0':
+ resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ '@typescript-eslint/parser': ^8.39.0
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/parser@8.29.0':
- resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==}
+ '@typescript-eslint/parser@8.39.0':
+ resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.29.0':
- resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==}
+ '@typescript-eslint/project-service@8.39.0':
+ resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.39.0':
+ resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/type-utils@8.29.0':
- resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==}
+ '@typescript-eslint/tsconfig-utils@8.39.0':
+ resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.39.0':
+ resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/types@8.29.0':
- resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==}
+ '@typescript-eslint/types@8.39.0':
+ resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.29.0':
- resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==}
+ '@typescript-eslint/typescript-estree@8.39.0':
+ resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.29.0':
- resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==}
+ '@typescript-eslint/utils@8.39.0':
+ resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- typescript: '>=4.8.4 <5.9.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@8.29.0':
- resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==}
+ '@typescript-eslint/visitor-keys@8.39.0':
+ resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@unrs/resolver-binding-darwin-arm64@1.3.3':
- resolution: {integrity: sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==}
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
+ cpu: [arm]
+ os: [android]
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
cpu: [arm64]
os: [darwin]
- '@unrs/resolver-binding-darwin-x64@1.3.3':
- resolution: {integrity: sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==}
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
cpu: [x64]
os: [darwin]
- '@unrs/resolver-binding-freebsd-x64@1.3.3':
- resolution: {integrity: sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==}
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
cpu: [x64]
os: [freebsd]
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3':
- resolution: {integrity: sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==}
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
cpu: [arm]
os: [linux]
- '@unrs/resolver-binding-linux-arm-musleabihf@1.3.3':
- resolution: {integrity: sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==}
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
cpu: [arm]
os: [linux]
- '@unrs/resolver-binding-linux-arm64-gnu@1.3.3':
- resolution: {integrity: sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==}
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
- '@unrs/resolver-binding-linux-arm64-musl@1.3.3':
- resolution: {integrity: sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==}
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
- '@unrs/resolver-binding-linux-ppc64-gnu@1.3.3':
- resolution: {integrity: sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==}
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
- '@unrs/resolver-binding-linux-s390x-gnu@1.3.3':
- resolution: {integrity: sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==}
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
- '@unrs/resolver-binding-linux-x64-gnu@1.3.3':
- resolution: {integrity: sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==}
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
- '@unrs/resolver-binding-linux-x64-musl@1.3.3':
- resolution: {integrity: sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==}
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
- '@unrs/resolver-binding-wasm32-wasi@1.3.3':
- resolution: {integrity: sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==}
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
- '@unrs/resolver-binding-win32-arm64-msvc@1.3.3':
- resolution: {integrity: sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==}
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
cpu: [arm64]
os: [win32]
- '@unrs/resolver-binding-win32-ia32-msvc@1.3.3':
- resolution: {integrity: sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==}
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
cpu: [ia32]
os: [win32]
- '@unrs/resolver-binding-win32-x64-msvc@1.3.3':
- resolution: {integrity: sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==}
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
cpu: [x64]
os: [win32]
@@ -2046,8 +2029,8 @@ packages:
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn@8.14.1:
- resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -2065,8 +2048,8 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- aria-hidden@1.2.4:
- resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
aria-query@5.3.2:
@@ -2077,8 +2060,8 @@ packages:
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
- array-includes@3.1.8:
- resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
engines: {node: '>= 0.4'}
array-move@3.0.1:
@@ -2146,11 +2129,11 @@ packages:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
- brace-expansion@1.1.11:
- resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@@ -2188,8 +2171,8 @@ packages:
camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
- caniuse-lite@1.0.30001712:
- resolution: {integrity: sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==}
+ caniuse-lite@1.0.30001731:
+ resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==}
canvg@3.0.11:
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
@@ -2199,6 +2182,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -2243,8 +2230,8 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
- core-js@3.42.0:
- resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==}
+ core-js@3.45.0:
+ resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==}
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
@@ -2266,8 +2253,8 @@ packages:
css-to-react-native@3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
- css-what@6.1.0:
- resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
+ css-what@6.2.2:
+ resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
engines: {node: '>= 6'}
csstype@3.1.3:
@@ -2313,8 +2300,8 @@ packages:
supports-color:
optional: true
- debug@4.4.0:
- resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
@@ -2345,8 +2332,8 @@ packages:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
- detect-libc@2.0.3:
- resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
+ detect-libc@2.0.4:
+ resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
detect-node-es@1.1.0:
@@ -2365,8 +2352,8 @@ packages:
dompurify@2.5.8:
resolution: {integrity: sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==}
- dompurify@3.2.5:
- resolution: {integrity: sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==}
+ dompurify@3.2.6:
+ resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
@@ -2387,8 +2374,8 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
- enhanced-resolve@5.18.1:
- resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
+ enhanced-resolve@5.18.3:
+ resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
engines: {node: '>=10.13.0'}
entities@4.5.0:
@@ -2399,8 +2386,8 @@ packages:
resolution: {integrity: sha512-BeJFvFRJddxobhvEdm5GqHzRV/X+ACeuw0/BuuxsCh1EUZcAIz8+kYmBp/LrQuloy6K1f3a0M7+IhmZ7QnkISA==}
engines: {node: '>=0.12'}
- es-abstract@1.23.9:
- resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
+ es-abstract@1.24.0:
+ resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
@@ -2447,8 +2434,8 @@ packages:
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
- eslint-import-resolver-typescript@3.10.0:
- resolution: {integrity: sha512-aV3/dVsT0/H9BtpNwbaqvl+0xGMRGzncLyhm793NFGvbwGGvzyAykqWZ8oZlZuGwuHkwJjhWJkG1cM3ynvd2pQ==}
+ eslint-import-resolver-typescript@3.10.1:
+ resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
eslint: '*'
@@ -2460,8 +2447,8 @@ packages:
eslint-plugin-import-x:
optional: true
- eslint-module-utils@2.12.0:
- resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
+ eslint-module-utils@2.12.1:
+ resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@@ -2481,8 +2468,8 @@ packages:
eslint-import-resolver-webpack:
optional: true
- eslint-plugin-import@2.31.0:
- resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
+ eslint-plugin-import@2.32.0:
+ resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
@@ -2523,20 +2510,20 @@ packages:
resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==}
engines: {node: '>=4.0.0'}
- eslint-scope@8.3.0:
- resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- eslint-visitor-keys@4.2.0:
- resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.24.0:
- resolution: {integrity: sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==}
+ eslint@9.32.0:
+ resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -2545,8 +2532,8 @@ packages:
jiti:
optional: true
- espree@10.3.0:
- resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
esquery@1.6.0:
@@ -2585,8 +2572,8 @@ packages:
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
- fdir@6.4.3:
- resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
+ fdir@6.4.6:
+ resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
@@ -2631,8 +2618,8 @@ packages:
peerDependencies:
react: '>=16.8.0'
- framer-motion@12.6.3:
- resolution: {integrity: sha512-2hsqknz23aloK85bzMc9nSR2/JP+fValQ459ZTVElFQ0xgwR2YqNjYSuDZdFBPOwVCt4Q9jgyTt6hg6sVOALzw==}
+ framer-motion@12.23.12:
+ resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -2675,8 +2662,8 @@ packages:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.10.0:
- resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
+ get-tsconfig@4.10.1:
+ resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
get-youtube-id@1.0.1:
resolution: {integrity: sha512-5yidLzoLXbtw82a/Wb7LrajkGn29BM6JuLWeHyNfzOGp1weGyW4+7eMz6cP23+etqj27VlOFtq8fFFDMLq/FXQ==}
@@ -2754,6 +2741,10 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -2828,6 +2819,10 @@ packages:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
@@ -2882,8 +2877,8 @@ packages:
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'}
- jiti@2.4.2:
- resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+ jiti@2.5.1:
+ resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
hasBin: true
jose@4.15.9:
@@ -2922,8 +2917,8 @@ packages:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
- katex@0.16.21:
- resolution: {integrity: sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==}
+ katex@0.16.22:
+ resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==}
hasBin: true
keyv@4.5.4:
@@ -2940,75 +2935,75 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
- lightningcss-darwin-arm64@1.29.2:
- resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
- lightningcss-darwin-x64@1.29.2:
- resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
- lightningcss-freebsd-x64@1.29.2:
- resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
- lightningcss-linux-arm-gnueabihf@1.29.2:
- resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
- lightningcss-linux-arm64-gnu@1.29.2:
- resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- lightningcss-linux-arm64-musl@1.29.2:
- resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
- lightningcss-linux-x64-gnu@1.29.2:
- resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- lightningcss-linux-x64-musl@1.29.2:
- resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
- lightningcss-win32-arm64-msvc@1.29.2:
- resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
- lightningcss-win32-x64-msvc@1.29.2:
- resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
- lightningcss@1.29.2:
- resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
engines: {node: '>= 12.0.0'}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
- linkifyjs@4.2.0:
- resolution: {integrity: sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==}
+ linkifyjs@4.3.2:
+ resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==}
load-script@1.0.0:
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
@@ -3052,6 +3047,9 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
@@ -3081,14 +3079,27 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
- module-details-from-path@1.0.3:
- resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
- motion-dom@12.6.3:
- resolution: {integrity: sha512-gRY08RjcnzgFYLemUZ1lo/e9RkBxR+6d4BRvoeZDSeArG4XQXERSPapKl3LNQRu22Sndjf1h+iavgY0O4NrYqA==}
+ minizlib@3.0.2:
+ resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
+ engines: {node: '>= 18'}
- motion-utils@12.6.3:
- resolution: {integrity: sha512-R/b3Ia2VxtTNZ4LTEO5pKYau1OUNHOuUfxuP0WFCTDYdHkeTBR9UtxR1cc8mDmKr8PEhmmfnTKGz3rSMjNRoRg==}
+ mkdirp@3.0.1:
+ resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ module-details-from-path@1.0.4:
+ resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
+
+ motion-dom@12.23.12:
+ resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==}
+
+ motion-utils@12.23.6:
+ resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==}
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@@ -3101,6 +3112,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ napi-postinstall@0.3.2:
+ resolution: {integrity: sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -3191,8 +3207,8 @@ packages:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
- oidc-token-hash@5.1.0:
- resolution: {integrity: sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==}
+ oidc-token-hash@5.1.1:
+ resolution: {integrity: sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==}
engines: {node: ^10.13.0 || >=12.0.0}
openid-client@5.7.1:
@@ -3254,8 +3270,8 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- picomatch@4.0.2:
- resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
plyr@3.7.8:
@@ -3280,8 +3296,8 @@ packages:
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
engines: {node: ^10 || ^12 || >=14}
- postcss@8.5.3:
- resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
preact-render-to-string@5.2.6:
@@ -3289,8 +3305,8 @@ packages:
peerDependencies:
preact: '>=10'
- preact@10.26.5:
- resolution: {integrity: sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w==}
+ preact@10.27.0:
+ resolution: {integrity: sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
@@ -3305,17 +3321,17 @@ packages:
property-expr@2.0.6:
resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
- prosemirror-changeset@2.2.1:
- resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
+ prosemirror-changeset@2.3.1:
+ resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==}
prosemirror-collab@1.3.1:
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
- prosemirror-commands@1.7.0:
- resolution: {integrity: sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==}
+ prosemirror-commands@1.7.1:
+ resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
- prosemirror-dropcursor@1.8.1:
- resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
+ prosemirror-dropcursor@1.8.2:
+ resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
prosemirror-gapcursor@1.3.2:
resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==}
@@ -3326,17 +3342,17 @@ packages:
prosemirror-inputrules@1.5.0:
resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==}
- prosemirror-keymap@1.2.2:
- resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==}
+ prosemirror-keymap@1.2.3:
+ resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
prosemirror-markdown@1.13.2:
resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==}
- prosemirror-menu@1.2.4:
- resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==}
+ prosemirror-menu@1.2.5:
+ resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==}
- prosemirror-model@1.25.0:
- resolution: {integrity: sha512-/8XUmxWf0pkj2BmtqZHYJipTBMHIdVjuvFzMvEoxrtyGNmfvdhBiRwYt/eFwy2wA9DtBW3RLqvZnjurEkHaFCw==}
+ prosemirror-model@1.25.2:
+ resolution: {integrity: sha512-BVypCAJ4SL6jOiTsDffP3Wp6wD69lRhI4zg/iT8JXjp3ccZFiq5WyguxvMKmdKFC3prhaig7wSr8dneDToHE1Q==}
prosemirror-schema-basic@1.2.4:
resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==}
@@ -3347,8 +3363,8 @@ packages:
prosemirror-state@1.4.3:
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
- prosemirror-tables@1.6.4:
- resolution: {integrity: sha512-TkDY3Gw52gRFRfRn2f4wJv5WOgAOXLJA2CQJYIJ5+kdFbfj3acR4JUW6LX2e1hiEBiUwvEhzH5a3cZ5YSztpIA==}
+ prosemirror-tables@1.7.1:
+ resolution: {integrity: sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==}
prosemirror-trailing-node@3.0.0:
resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
@@ -3357,11 +3373,11 @@ packages:
prosemirror-state: ^1.4.2
prosemirror-view: ^1.33.8
- prosemirror-transform@1.10.3:
- resolution: {integrity: sha512-Nhh/+1kZGRINbEHmVu39oynhcap4hWTs/BlU7NnxWj3+l0qi8I1mu67v6mMdEe/ltD8hHvU4FV6PHiCw2VSpMw==}
+ prosemirror-transform@1.10.4:
+ resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==}
- prosemirror-view@1.39.1:
- resolution: {integrity: sha512-GhLxH1xwnqa5VjhJ29LfcQITNDp+f1jzmMPXQfGW9oNrF0lfjPzKvV5y/bjIQkyKpwCX3Fp+GA4dBpMMk8g+ZQ==}
+ prosemirror-view@1.40.1:
+ resolution: {integrity: sha512-pbwUjt3G7TlsQQHDiYSupWBhJswpLVB09xXm1YiJPdkjkh9Pe7Y51XdLh5VWIZmROLY8UpUpG03lkdhm9lzIBA==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
@@ -3428,11 +3444,11 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
- react-katex@3.0.1:
- resolution: {integrity: sha512-wIUW1fU5dHlkKvq4POfDkHruQsYp3fM8xNb/jnc8dnQ+nNCnaj0sx5pw7E6UyuEdLRyFKK0HZjmXBo+AtXXy0A==}
+ react-katex@3.1.0:
+ resolution: {integrity: sha512-At9uLOkC75gwn2N+ZXc5HD8TlATsB+3Hkp9OGs6uA8tM3dwZ3Wljn74Bk3JyHFPgSnesY/EMrIAB1WJwqZqejA==}
peerDependencies:
prop-types: ^15.8.1
- react: '>=15.3.2 <=18'
+ react: '>=15.3.2 <20'
react-plyr@2.2.0:
resolution: {integrity: sha512-05oBKvCC9F2VMP7BNhh3x7xeK6PNUMYBPPWIip5sDRuey4HmpghfLQyOfsbNKTssR6kkxRSi2X9neYmfr91NuA==}
@@ -3481,8 +3497,8 @@ packages:
'@types/react':
optional: true
- react-remove-scroll@2.6.3:
- resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==}
+ react-remove-scroll@2.7.1:
+ resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': 19.0.10
@@ -3527,9 +3543,6 @@ packages:
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
- regenerator-runtime@0.14.1:
- resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
-
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
@@ -3594,8 +3607,8 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
- semver@7.7.1:
- resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
@@ -3621,8 +3634,8 @@ packages:
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
- sharp@0.34.1:
- resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
+ sharp@0.34.3:
+ resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shebang-command@2.0.0:
@@ -3666,6 +3679,10 @@ packages:
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
engines: {node: '>=0.1.14'}
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -3709,8 +3726,8 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
- styled-components@6.1.17:
- resolution: {integrity: sha512-97D7DwWanI7nN24v0D4SvbfjLE9656umNSJZkBkDIWL37aZqG/wRQ+Y9pWtXyBIM/NSfcBzHLErEsqHmJNSVUg==}
+ styled-components@6.1.19:
+ resolution: {integrity: sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==}
engines: {node: '>= 16'}
peerDependencies:
react: '>= 16.8.0'
@@ -3744,8 +3761,8 @@ packages:
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
engines: {node: '>=12.0.0'}
- swr@2.3.3:
- resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==}
+ swr@2.3.4:
+ resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -3760,13 +3777,17 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
- tailwindcss@4.1.3:
- resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==}
+ tailwindcss@4.1.11:
+ resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
- tapable@2.2.1:
- resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ tapable@2.2.2:
+ resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
engines: {node: '>=6'}
+ tar@7.4.3:
+ resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+ engines: {node: '>=18'}
+
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
@@ -3779,8 +3800,8 @@ packages:
tiny-warning@1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
- tinyglobby@0.2.12:
- resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
+ tinyglobby@0.2.14:
+ resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
tippy.js@6.3.7:
@@ -3853,8 +3874,8 @@ packages:
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
- unrs-resolver@1.3.3:
- resolution: {integrity: sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==}
+ unrs-resolver@1.11.1:
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
unsplash-js@7.0.19:
resolution: {integrity: sha512-j6qT2floy5Q2g2d939FJpwey1yw/GpQecFiSouyJtsHQPj3oqmqq3K4rI+GF8vU1zwGCT7ZwIGQd2dtCQLjYJw==}
@@ -3949,6 +3970,10 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
yargs-parser@18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
engines: {node: '>=6'}
@@ -3964,8 +3989,8 @@ packages:
youtube-player@5.5.2:
resolution: {integrity: sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==}
- yup@1.6.1:
- resolution: {integrity: sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==}
+ yup@1.7.0:
+ resolution: {integrity: sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q==}
zeed-dom@0.15.1:
resolution: {integrity: sha512-dtZ0aQSFyZmoJS0m06/xBN1SazUBPL5HpzlAcs/KcRW0rzadYw12deQBjeMhGKMMeGEp7bA9vmikMLaO4exBcg==}
@@ -3975,22 +4000,25 @@ snapshots:
'@alloc/quick-lru@5.2.0': {}
- '@babel/runtime@7.27.0':
+ '@ampproject/remapping@2.3.0':
dependencies:
- regenerator-runtime: 0.14.1
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
- '@emnapi/core@1.4.0':
+ '@babel/runtime@7.28.2': {}
+
+ '@emnapi/core@1.4.5':
dependencies:
- '@emnapi/wasi-threads': 1.0.1
+ '@emnapi/wasi-threads': 1.0.4
tslib: 2.8.1
optional: true
- '@emnapi/runtime@1.4.0':
+ '@emnapi/runtime@1.4.5':
dependencies:
tslib: 2.8.1
optional: true
- '@emnapi/wasi-threads@1.0.1':
+ '@emnapi/wasi-threads@1.0.4':
dependencies:
tslib: 2.8.1
optional: true
@@ -4010,36 +4038,32 @@ snapshots:
'@emotion/unitless@0.8.1': {}
- '@eslint-community/eslint-utils@4.5.1(eslint@9.24.0(jiti@2.4.2))':
+ '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.5.1))':
dependencies:
- eslint: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.1': {}
- '@eslint/config-array@0.20.0':
+ '@eslint/config-array@0.21.0':
dependencies:
'@eslint/object-schema': 2.1.6
- debug: 4.4.0
+ debug: 4.4.1
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
- '@eslint/config-helpers@0.2.1': {}
+ '@eslint/config-helpers@0.3.0': {}
- '@eslint/core@0.12.0':
- dependencies:
- '@types/json-schema': 7.0.15
-
- '@eslint/core@0.13.0':
+ '@eslint/core@0.15.1':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
- debug: 4.4.0
- espree: 10.3.0
+ debug: 4.4.1
+ espree: 10.4.0
globals: 14.0.0
ignore: 5.3.2
import-fresh: 3.3.1
@@ -4049,35 +4073,35 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.24.0': {}
+ '@eslint/js@9.32.0': {}
'@eslint/object-schema@2.1.6': {}
- '@eslint/plugin-kit@0.2.8':
+ '@eslint/plugin-kit@0.3.4':
dependencies:
- '@eslint/core': 0.13.0
+ '@eslint/core': 0.15.1
levn: 0.4.1
- '@floating-ui/core@1.6.9':
+ '@floating-ui/core@1.7.3':
dependencies:
- '@floating-ui/utils': 0.2.9
+ '@floating-ui/utils': 0.2.10
- '@floating-ui/dom@1.6.13':
+ '@floating-ui/dom@1.7.3':
dependencies:
- '@floating-ui/core': 1.6.9
- '@floating-ui/utils': 0.2.9
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
- '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@floating-ui/react-dom@2.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@floating-ui/dom': 1.6.13
+ '@floating-ui/dom': 1.7.3
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- '@floating-ui/utils@0.2.9': {}
+ '@floating-ui/utils@0.2.10': {}
'@hello-pangea/dnd@18.0.1(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
css-box-model: 1.2.1
raf-schd: 4.0.3
react: 19.0.0
@@ -4098,7 +4122,7 @@ snapshots:
'@humanwhocodes/retry@0.3.1': {}
- '@humanwhocodes/retry@0.4.2': {}
+ '@humanwhocodes/retry@0.4.3': {}
'@icons-pack/react-simple-icons@10.2.0(react@19.0.0)':
dependencies:
@@ -4109,9 +4133,9 @@ snapshots:
'@img/sharp-libvips-darwin-arm64': 1.0.4
optional: true
- '@img/sharp-darwin-arm64@0.34.1':
+ '@img/sharp-darwin-arm64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-darwin-arm64': 1.1.0
+ '@img/sharp-libvips-darwin-arm64': 1.2.0
optional: true
'@img/sharp-darwin-x64@0.33.5':
@@ -4119,60 +4143,60 @@ snapshots:
'@img/sharp-libvips-darwin-x64': 1.0.4
optional: true
- '@img/sharp-darwin-x64@0.34.1':
+ '@img/sharp-darwin-x64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-darwin-x64': 1.1.0
+ '@img/sharp-libvips-darwin-x64': 1.2.0
optional: true
'@img/sharp-libvips-darwin-arm64@1.0.4':
optional: true
- '@img/sharp-libvips-darwin-arm64@1.1.0':
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
optional: true
'@img/sharp-libvips-darwin-x64@1.0.4':
optional: true
- '@img/sharp-libvips-darwin-x64@1.1.0':
+ '@img/sharp-libvips-darwin-x64@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm64@1.0.4':
optional: true
- '@img/sharp-libvips-linux-arm64@1.1.0':
+ '@img/sharp-libvips-linux-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linux-arm@1.0.5':
optional: true
- '@img/sharp-libvips-linux-arm@1.1.0':
+ '@img/sharp-libvips-linux-arm@1.2.0':
optional: true
- '@img/sharp-libvips-linux-ppc64@1.1.0':
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
optional: true
'@img/sharp-libvips-linux-s390x@1.0.4':
optional: true
- '@img/sharp-libvips-linux-s390x@1.1.0':
+ '@img/sharp-libvips-linux-s390x@1.2.0':
optional: true
'@img/sharp-libvips-linux-x64@1.0.4':
optional: true
- '@img/sharp-libvips-linux-x64@1.1.0':
+ '@img/sharp-libvips-linux-x64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
optional: true
- '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
optional: true
- '@img/sharp-libvips-linuxmusl-x64@1.1.0':
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
optional: true
'@img/sharp-linux-arm64@0.33.5':
@@ -4180,9 +4204,9 @@ snapshots:
'@img/sharp-libvips-linux-arm64': 1.0.4
optional: true
- '@img/sharp-linux-arm64@0.34.1':
+ '@img/sharp-linux-arm64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linux-arm64': 1.1.0
+ '@img/sharp-libvips-linux-arm64': 1.2.0
optional: true
'@img/sharp-linux-arm@0.33.5':
@@ -4190,9 +4214,14 @@ snapshots:
'@img/sharp-libvips-linux-arm': 1.0.5
optional: true
- '@img/sharp-linux-arm@0.34.1':
+ '@img/sharp-linux-arm@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linux-arm': 1.1.0
+ '@img/sharp-libvips-linux-arm': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.0
optional: true
'@img/sharp-linux-s390x@0.33.5':
@@ -4200,9 +4229,9 @@ snapshots:
'@img/sharp-libvips-linux-s390x': 1.0.4
optional: true
- '@img/sharp-linux-s390x@0.34.1':
+ '@img/sharp-linux-s390x@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linux-s390x': 1.1.0
+ '@img/sharp-libvips-linux-s390x': 1.2.0
optional: true
'@img/sharp-linux-x64@0.33.5':
@@ -4210,9 +4239,9 @@ snapshots:
'@img/sharp-libvips-linux-x64': 1.0.4
optional: true
- '@img/sharp-linux-x64@0.34.1':
+ '@img/sharp-linux-x64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linux-x64': 1.1.0
+ '@img/sharp-libvips-linux-x64': 1.2.0
optional: true
'@img/sharp-linuxmusl-arm64@0.33.5':
@@ -4220,9 +4249,9 @@ snapshots:
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
optional: true
- '@img/sharp-linuxmusl-arm64@0.34.1':
+ '@img/sharp-linuxmusl-arm64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.0
optional: true
'@img/sharp-linuxmusl-x64@0.33.5':
@@ -4230,38 +4259,59 @@ snapshots:
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
optional: true
- '@img/sharp-linuxmusl-x64@0.34.1':
+ '@img/sharp-linuxmusl-x64@0.34.3':
optionalDependencies:
- '@img/sharp-libvips-linuxmusl-x64': 1.1.0
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.0
optional: true
'@img/sharp-wasm32@0.33.5':
dependencies:
- '@emnapi/runtime': 1.4.0
+ '@emnapi/runtime': 1.4.5
optional: true
- '@img/sharp-wasm32@0.34.1':
+ '@img/sharp-wasm32@0.34.3':
dependencies:
- '@emnapi/runtime': 1.4.0
+ '@emnapi/runtime': 1.4.5
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.3':
optional: true
'@img/sharp-win32-ia32@0.33.5':
optional: true
- '@img/sharp-win32-ia32@0.34.1':
+ '@img/sharp-win32-ia32@0.34.3':
optional: true
'@img/sharp-win32-x64@0.33.5':
optional: true
- '@img/sharp-win32-x64@0.34.1':
+ '@img/sharp-win32-x64@0.34.3':
optional: true
- '@napi-rs/wasm-runtime@0.2.8':
+ '@isaacs/fs-minipass@4.0.1':
dependencies:
- '@emnapi/core': 1.4.0
- '@emnapi/runtime': 1.4.0
- '@tybys/wasm-util': 0.9.0
+ minipass: 7.1.2
+
+ '@jridgewell/gen-mapping@0.3.12':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.4
+ '@jridgewell/trace-mapping': 0.3.29
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.4': {}
+
+ '@jridgewell/trace-mapping@0.3.29':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.4
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ dependencies:
+ '@emnapi/core': 1.4.5
+ '@emnapi/runtime': 1.4.5
+ '@tybys/wasm-util': 0.10.0
optional: true
'@next/env@15.3.5': {}
@@ -4317,32 +4367,30 @@ snapshots:
'@radix-ui/colors@0.1.9': {}
- '@radix-ui/number@1.1.0': {}
+ '@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.0.0':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive@1.0.1':
dependencies:
- '@babel/runtime': 7.27.0
-
- '@radix-ui/primitive@1.1.1': {}
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive@1.1.2': {}
- '@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-aspect-ratio@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4365,12 +4413,12 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-collection@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-collection@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4379,18 +4427,12 @@ snapshots:
'@radix-ui/react-compose-refs@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
'@radix-ui/react-compose-refs@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
- '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
+ '@babel/runtime': 7.28.2
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
@@ -4403,18 +4445,12 @@ snapshots:
'@radix-ui/react-context@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
'@radix-ui/react-context@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
- '@radix-ui/react-context@1.1.1(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
+ '@babel/runtime': 7.28.2
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
@@ -4427,7 +4463,7 @@ snapshots:
'@radix-ui/react-dialog@1.0.0(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0(react@19.0.0)
'@radix-ui/react-context': 1.0.0(react@19.0.0)
@@ -4440,7 +4476,7 @@ snapshots:
'@radix-ui/react-primitive': 1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot': 1.0.0(react@19.0.0)
'@radix-ui/react-use-controllable-state': 1.0.0(react@19.0.0)
- aria-hidden: 1.2.4
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-remove-scroll: 2.5.4(@types/react@19.0.10)(react@19.0.0)
@@ -4449,7 +4485,7 @@ snapshots:
'@radix-ui/react-dialog@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-context': 1.0.1(@types/react@19.0.10)(react@19.0.0)
@@ -4462,7 +4498,7 @@ snapshots:
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot': 1.0.2(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@19.0.10)(react@19.0.0)
- aria-hidden: 1.2.4
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-remove-scroll: 2.5.5(@types/react@19.0.10)(react@19.0.0)
@@ -4470,29 +4506,29 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- aria-hidden: 1.2.4
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
+ react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-direction@1.1.0(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-direction@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
react: 19.0.0
optionalDependencies:
@@ -4500,7 +4536,7 @@ snapshots:
'@radix-ui/react-dismissable-layer@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0(react@19.0.0)
'@radix-ui/react-primitive': 1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -4511,7 +4547,7 @@ snapshots:
'@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -4523,28 +4559,28 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-menu': 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4553,17 +4589,17 @@ snapshots:
'@radix-ui/react-focus-guards@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
'@radix-ui/react-focus-guards@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-focus-guards@1.1.2(@types/react@19.0.10)(react@19.0.0)':
dependencies:
react: 19.0.0
optionalDependencies:
@@ -4571,7 +4607,7 @@ snapshots:
'@radix-ui/react-focus-scope@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.0(react@19.0.0)
'@radix-ui/react-primitive': 1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-use-callback-ref': 1.0.0(react@19.0.0)
@@ -4580,7 +4616,7 @@ snapshots:
'@radix-ui/react-focus-scope@1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0)
@@ -4590,11 +4626,11 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4603,7 +4639,7 @@ snapshots:
'@radix-ui/react-form@0.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-context': 1.0.1(@types/react@19.0.10)(react@19.0.0)
@@ -4616,17 +4652,17 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-hover-card@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-hover-card@1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4639,28 +4675,28 @@ snapshots:
'@radix-ui/react-id@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-layout-effect': 1.0.0(react@19.0.0)
react: 19.0.0
'@radix-ui/react-id@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-id@1.1.0(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-id@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
'@radix-ui/react-label@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -4668,76 +4704,76 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-label@2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-label@2.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-menu@2.1.15(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- aria-hidden: 1.2.4
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
+ react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-popover@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-popover@1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- aria-hidden: 1.2.4
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
+ react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-popper@1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-arrow': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/rect': 1.1.0
+ '@floating-ui/react-dom': 2.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-rect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/rect': 1.1.1
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4746,14 +4782,14 @@ snapshots:
'@radix-ui/react-portal@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-primitive': 1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@radix-ui/react-portal@1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -4761,10 +4797,10 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-portal@1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4773,7 +4809,7 @@ snapshots:
'@radix-ui/react-presence@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.0(react@19.0.0)
'@radix-ui/react-use-layout-effect': 1.0.0(react@19.0.0)
react: 19.0.0
@@ -4781,7 +4817,7 @@ snapshots:
'@radix-ui/react-presence@1.0.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
@@ -4790,16 +4826,6 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- react: 19.0.0
- react-dom: 19.0.0(react@19.0.0)
- optionalDependencies:
- '@types/react': 19.0.10
- '@types/react-dom': 19.0.4(@types/react@19.0.10)
-
'@radix-ui/react-presence@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
@@ -4812,14 +4838,14 @@ snapshots:
'@radix-ui/react-primitive@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-slot': 1.0.0(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@radix-ui/react-primitive@1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-slot': 1.0.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -4827,15 +4853,6 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
- dependencies:
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- react: 19.0.0
- react-dom: 19.0.0(react@19.0.0)
- optionalDependencies:
- '@types/react': 19.0.10
- '@types/react-dom': 19.0.4(@types/react@19.0.10)
-
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
@@ -4845,73 +4862,66 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-select@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-select@2.2.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/number': 1.1.0
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- aria-hidden: 1.2.4
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ aria-hidden: 1.2.6
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
+ react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
'@radix-ui/react-slot@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.0(react@19.0.0)
react: 19.0.0
'@radix-ui/react-slot@1.0.2(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
'@radix-ui/react-slot@1.2.3(@types/react@19.0.10)(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
@@ -4919,77 +4929,77 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-switch@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-switch@1.2.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-tabs@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-toggle-group@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-toggle-group@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-toggle': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-toggle': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-toggle@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-toggle@1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/react-tooltip@1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/primitive': 1.1.1
- '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
@@ -4998,17 +5008,17 @@ snapshots:
'@radix-ui/react-use-callback-ref@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
'@radix-ui/react-use-callback-ref@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
react: 19.0.0
optionalDependencies:
@@ -5016,25 +5026,18 @@ snapshots:
'@radix-ui/react-use-controllable-state@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-callback-ref': 1.0.0(react@19.0.0)
react: 19.0.0
'@radix-ui/react-use-controllable-state@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.0.10)(react@19.0.0)':
dependencies:
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.0.10)(react@19.0.0)
@@ -5052,39 +5055,33 @@ snapshots:
'@radix-ui/react-use-escape-keydown@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-callback-ref': 1.0.0(react@19.0.0)
react: 19.0.0
'@radix-ui/react-use-escape-keydown@1.0.3(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
'@radix-ui/react-use-layout-effect@1.0.0(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
react: 19.0.0
'@radix-ui/react-use-layout-effect@1.0.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.27.0
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
- '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
+ '@babel/runtime': 7.28.2
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
@@ -5095,28 +5092,15 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-use-previous@1.1.0(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
'@radix-ui/react-use-previous@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
+ '@radix-ui/react-use-rect@1.1.1(@types/react@19.0.10)(react@19.0.0)':
dependencies:
- '@radix-ui/rect': 1.1.0
- react: 19.0.0
- optionalDependencies:
- '@types/react': 19.0.10
-
- '@radix-ui/react-use-size@1.1.0(@types/react@19.0.10)(react@19.0.0)':
- dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/rect': 1.1.1
react: 19.0.0
optionalDependencies:
'@types/react': 19.0.10
@@ -5128,22 +5112,22 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
- '@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
- '@radix-ui/rect@1.1.0': {}
+ '@radix-ui/rect@1.1.1': {}
'@remirror/core-constants@3.0.0': {}
'@rtsao/scc@1.1.0': {}
- '@rushstack/eslint-patch@1.11.0': {}
+ '@rushstack/eslint-patch@1.12.0': {}
'@stitches/react@1.2.8(react@19.0.0)':
dependencies:
@@ -5155,287 +5139,297 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@tailwindcss/node@4.1.3':
+ '@tailwindcss/node@4.1.11':
dependencies:
- enhanced-resolve: 5.18.1
- jiti: 2.4.2
- lightningcss: 1.29.2
- tailwindcss: 4.1.3
+ '@ampproject/remapping': 2.3.0
+ enhanced-resolve: 5.18.3
+ jiti: 2.5.1
+ lightningcss: 1.30.1
+ magic-string: 0.30.17
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.11
- '@tailwindcss/oxide-android-arm64@4.1.3':
+ '@tailwindcss/oxide-android-arm64@4.1.11':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.1.3':
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.1.3':
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.1.3':
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.1.3':
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.1.3':
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.1.3':
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.1.3':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
optional: true
- '@tailwindcss/oxide@4.1.3':
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.11':
+ dependencies:
+ detect-libc: 2.0.4
+ tar: 7.4.3
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.1.3
- '@tailwindcss/oxide-darwin-arm64': 4.1.3
- '@tailwindcss/oxide-darwin-x64': 4.1.3
- '@tailwindcss/oxide-freebsd-x64': 4.1.3
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.3
- '@tailwindcss/oxide-linux-arm64-gnu': 4.1.3
- '@tailwindcss/oxide-linux-arm64-musl': 4.1.3
- '@tailwindcss/oxide-linux-x64-gnu': 4.1.3
- '@tailwindcss/oxide-linux-x64-musl': 4.1.3
- '@tailwindcss/oxide-win32-arm64-msvc': 4.1.3
- '@tailwindcss/oxide-win32-x64-msvc': 4.1.3
+ '@tailwindcss/oxide-android-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-x64': 4.1.11
+ '@tailwindcss/oxide-freebsd-x64': 4.1.11
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.11
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.11
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.11
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.11
- '@tailwindcss/postcss@4.1.3':
+ '@tailwindcss/postcss@4.1.11':
dependencies:
'@alloc/quick-lru': 5.2.0
- '@tailwindcss/node': 4.1.3
- '@tailwindcss/oxide': 4.1.3
- postcss: 8.5.3
- tailwindcss: 4.1.3
+ '@tailwindcss/node': 4.1.11
+ '@tailwindcss/oxide': 4.1.11
+ postcss: 8.5.6
+ tailwindcss: 4.1.11
- '@tanstack/react-table@8.21.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@tanstack/react-table@8.21.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@tanstack/table-core': 8.21.2
+ '@tanstack/table-core': 8.21.3
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- '@tanstack/table-core@8.21.2': {}
+ '@tanstack/table-core@8.21.3': {}
- '@tiptap/core@2.11.7(@tiptap/pm@2.11.7)':
+ '@tiptap/core@2.26.1(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/pm': 2.11.7
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-blockquote@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-blockquote@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-bold@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-bold@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-bubble-menu@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-bubble-menu@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
tippy.js: 6.3.7
- '@tiptap/extension-bullet-list@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-bullet-list@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-code-block-lowlight@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)':
+ '@tiptap/extension-code-block-lowlight@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/extension-code-block@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)(highlight.js@11.11.1)(lowlight@3.3.0)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/extension-code-block': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/extension-code-block': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
highlight.js: 11.11.1
lowlight: 3.3.0
- '@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-code-block@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-code@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-code@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-document@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-document@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-dropcursor@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-dropcursor@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-floating-menu@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-floating-menu@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
tippy.js: 6.3.7
- '@tiptap/extension-gapcursor@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-gapcursor@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-hard-break@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-hard-break@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-heading@2.12.0(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-heading@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-history@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-history@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-horizontal-rule@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-horizontal-rule@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-italic@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-italic@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-link@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-link@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
- linkifyjs: 4.2.0
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
+ linkifyjs: 4.3.2
- '@tiptap/extension-list-item@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-list-item@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-ordered-list@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-ordered-list@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-paragraph@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-paragraph@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-strike@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-strike@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-table-cell@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-table-cell@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-table-header@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-table-header@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-table-row@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-table-row@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-table@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/extension-table@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
- '@tiptap/extension-text-style@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-text-style@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-text@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-text@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/extension-youtube@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
+ '@tiptap/extension-youtube@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
- '@tiptap/html@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
+ '@tiptap/html@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
zeed-dom: 0.15.1
- '@tiptap/pm@2.11.7':
+ '@tiptap/pm@2.26.1':
dependencies:
- prosemirror-changeset: 2.2.1
+ prosemirror-changeset: 2.3.1
prosemirror-collab: 1.3.1
- prosemirror-commands: 1.7.0
- prosemirror-dropcursor: 1.8.1
+ prosemirror-commands: 1.7.1
+ prosemirror-dropcursor: 1.8.2
prosemirror-gapcursor: 1.3.2
prosemirror-history: 1.4.1
prosemirror-inputrules: 1.5.0
- prosemirror-keymap: 1.2.2
+ prosemirror-keymap: 1.2.3
prosemirror-markdown: 1.13.2
- prosemirror-menu: 1.2.4
- prosemirror-model: 1.25.0
+ prosemirror-menu: 1.2.5
+ prosemirror-model: 1.25.2
prosemirror-schema-basic: 1.2.4
prosemirror-schema-list: 1.5.1
prosemirror-state: 1.4.3
- prosemirror-tables: 1.6.4
- prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.0)(prosemirror-state@1.4.3)(prosemirror-view@1.39.1)
- prosemirror-transform: 1.10.3
- prosemirror-view: 1.39.1
+ prosemirror-tables: 1.7.1
+ prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1)
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.40.1
- '@tiptap/react@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@tiptap/react@2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/extension-bubble-menu': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-floating-menu': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/extension-bubble-menu': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-floating-menu': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/pm': 2.26.1
'@types/use-sync-external-store': 0.0.6
fast-deep-equal: 3.1.3
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
use-sync-external-store: 1.5.0(react@19.0.0)
- '@tiptap/starter-kit@2.11.7':
+ '@tiptap/starter-kit@2.26.1':
dependencies:
- '@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
- '@tiptap/extension-blockquote': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-bold': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-bullet-list': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-code': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-code-block': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-document': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-dropcursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-gapcursor': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-hard-break': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-heading': 2.12.0(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-history': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-horizontal-rule': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
- '@tiptap/extension-italic': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-list-item': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-ordered-list': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-paragraph': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-strike': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-text': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/extension-text-style': 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))
- '@tiptap/pm': 2.11.7
+ '@tiptap/core': 2.26.1(@tiptap/pm@2.26.1)
+ '@tiptap/extension-blockquote': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-bold': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-bullet-list': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-code': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-code-block': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-document': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-dropcursor': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-gapcursor': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-hard-break': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-heading': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-history': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-horizontal-rule': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)
+ '@tiptap/extension-italic': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-list-item': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-ordered-list': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-paragraph': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-strike': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-text': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/extension-text-style': 2.26.1(@tiptap/core@2.26.1(@tiptap/pm@2.26.1))
+ '@tiptap/pm': 2.26.1
- '@tybys/wasm-util@0.9.0':
+ '@tybys/wasm-util@0.10.0':
dependencies:
tslib: 2.8.1
optional: true
'@types/dompurify@3.2.0':
dependencies:
- dompurify: 3.2.5
+ dompurify: 3.2.6
- '@types/estree@1.0.7': {}
+ '@types/estree@1.0.8': {}
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
- '@types/hoist-non-react-statics@3.3.6':
+ '@types/hoist-non-react-statics@3.3.7(@types/react@19.0.10)':
dependencies:
'@types/react': 19.0.10
hoist-non-react-statics: 3.3.2
@@ -5488,7 +5482,7 @@ snapshots:
'@types/styled-components@5.1.34':
dependencies:
- '@types/hoist-non-react-statics': 3.3.6
+ '@types/hoist-non-react-statics': 3.3.7(@types/react@19.0.10)
'@types/react': 19.0.10
csstype: 3.1.3
@@ -5503,135 +5497,163 @@ snapshots:
'@types/uuid@9.0.8': {}
- '@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)':
+ '@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)':
dependencies:
'@eslint-community/regexpp': 4.12.1
- '@typescript-eslint/parser': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- '@typescript-eslint/scope-manager': 8.29.0
- '@typescript-eslint/type-utils': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- '@typescript-eslint/utils': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- '@typescript-eslint/visitor-keys': 8.29.0
- eslint: 9.24.0(jiti@2.4.2)
+ '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ '@typescript-eslint/scope-manager': 8.39.0
+ '@typescript-eslint/type-utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ '@typescript-eslint/visitor-keys': 8.39.0
+ eslint: 9.32.0(jiti@2.5.1)
graphemer: 1.4.0
- ignore: 5.3.2
+ ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@5.4.4)
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)':
+ '@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/scope-manager': 8.29.0
- '@typescript-eslint/types': 8.29.0
- '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.4.4)
- '@typescript-eslint/visitor-keys': 8.29.0
- debug: 4.4.0
- eslint: 9.24.0(jiti@2.4.2)
+ '@typescript-eslint/scope-manager': 8.39.0
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.4.4)
+ '@typescript-eslint/visitor-keys': 8.39.0
+ debug: 4.4.1
+ eslint: 9.32.0(jiti@2.5.1)
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.29.0':
+ '@typescript-eslint/project-service@8.39.0(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/types': 8.29.0
- '@typescript-eslint/visitor-keys': 8.29.0
+ '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.4.4)
+ '@typescript-eslint/types': 8.39.0
+ debug: 4.4.1
+ typescript: 5.4.4
+ transitivePeerDependencies:
+ - supports-color
- '@typescript-eslint/type-utils@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)':
+ '@typescript-eslint/scope-manager@8.39.0':
dependencies:
- '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.4.4)
- '@typescript-eslint/utils': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- debug: 4.4.0
- eslint: 9.24.0(jiti@2.4.2)
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/visitor-keys': 8.39.0
+
+ '@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.4.4)':
+ dependencies:
+ typescript: 5.4.4
+
+ '@typescript-eslint/type-utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)':
+ dependencies:
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.4.4)
+ '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ debug: 4.4.1
+ eslint: 9.32.0(jiti@2.5.1)
ts-api-utils: 2.1.0(typescript@5.4.4)
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.29.0': {}
+ '@typescript-eslint/types@8.39.0': {}
- '@typescript-eslint/typescript-estree@8.29.0(typescript@5.4.4)':
+ '@typescript-eslint/typescript-estree@8.39.0(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/types': 8.29.0
- '@typescript-eslint/visitor-keys': 8.29.0
- debug: 4.4.0
+ '@typescript-eslint/project-service': 8.39.0(typescript@5.4.4)
+ '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.4.4)
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/visitor-keys': 8.39.0
+ debug: 4.4.1
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
- semver: 7.7.1
+ semver: 7.7.2
ts-api-utils: 2.1.0(typescript@5.4.4)
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)':
+ '@typescript-eslint/utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)':
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.24.0(jiti@2.4.2))
- '@typescript-eslint/scope-manager': 8.29.0
- '@typescript-eslint/types': 8.29.0
- '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.4.4)
- eslint: 9.24.0(jiti@2.4.2)
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+ '@typescript-eslint/scope-manager': 8.39.0
+ '@typescript-eslint/types': 8.39.0
+ '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.4.4)
+ eslint: 9.32.0(jiti@2.5.1)
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.29.0':
+ '@typescript-eslint/visitor-keys@8.39.0':
dependencies:
- '@typescript-eslint/types': 8.29.0
- eslint-visitor-keys: 4.2.0
+ '@typescript-eslint/types': 8.39.0
+ eslint-visitor-keys: 4.2.1
- '@unrs/resolver-binding-darwin-arm64@1.3.3':
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
optional: true
- '@unrs/resolver-binding-darwin-x64@1.3.3':
+ '@unrs/resolver-binding-android-arm64@1.11.1':
optional: true
- '@unrs/resolver-binding-freebsd-x64@1.3.3':
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3':
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-arm-musleabihf@1.3.3':
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-arm64-gnu@1.3.3':
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-arm64-musl@1.3.3':
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-ppc64-gnu@1.3.3':
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-s390x-gnu@1.3.3':
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-x64-gnu@1.3.3':
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
optional: true
- '@unrs/resolver-binding-linux-x64-musl@1.3.3':
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
optional: true
- '@unrs/resolver-binding-wasm32-wasi@1.3.3':
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
dependencies:
- '@napi-rs/wasm-runtime': 0.2.8
+ '@napi-rs/wasm-runtime': 0.2.12
optional: true
- '@unrs/resolver-binding-win32-arm64-msvc@1.3.3':
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
optional: true
- '@unrs/resolver-binding-win32-ia32-msvc@1.3.3':
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
optional: true
- '@unrs/resolver-binding-win32-x64-msvc@1.3.3':
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true
- acorn-jsx@5.3.2(acorn@8.14.1):
+ acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
- acorn: 8.14.1
+ acorn: 8.15.0
- acorn@8.14.1: {}
+ acorn@8.15.0: {}
ajv@6.12.6:
dependencies:
@@ -5648,7 +5670,7 @@ snapshots:
argparse@2.0.1: {}
- aria-hidden@1.2.4:
+ aria-hidden@1.2.6:
dependencies:
tslib: 2.8.1
@@ -5659,14 +5681,16 @@ snapshots:
call-bound: 1.0.4
is-array-buffer: 3.0.5
- array-includes@3.1.8:
+ array-includes@3.1.9:
dependencies:
call-bind: 1.0.8
+ call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ 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
array-move@3.0.1: {}
@@ -5674,7 +5698,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@@ -5684,7 +5708,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
@@ -5693,21 +5717,21 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-shim-unscopables: 1.1.0
array.prototype.flatmap@1.3.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-shim-unscopables: 1.1.0
array.prototype.tosorted@1.1.4:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-shim-unscopables: 1.1.0
@@ -5716,7 +5740,7 @@ snapshots:
array-buffer-byte-length: 1.0.2
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
@@ -5747,12 +5771,12 @@ snapshots:
base64-arraybuffer@1.0.2: {}
- brace-expansion@1.1.11:
+ brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
- brace-expansion@2.0.1:
+ brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
@@ -5789,13 +5813,13 @@ snapshots:
camelize@1.0.1: {}
- caniuse-lite@1.0.30001712: {}
+ caniuse-lite@1.0.30001731: {}
canvg@3.0.11:
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@types/raf': 3.4.3
- core-js: 3.42.0
+ core-js: 3.45.0
raf: 3.4.1
regenerator-runtime: 0.13.11
rgbcolor: 1.0.1
@@ -5808,6 +5832,8 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ chownr@3.0.0: {}
+
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -5852,7 +5878,7 @@ snapshots:
cookie@0.7.2: {}
- core-js@3.42.0: {}
+ core-js@3.45.0: {}
crelt@1.0.6: {}
@@ -5878,7 +5904,7 @@ snapshots:
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
- css-what@6.1.0: {}
+ css-what@6.2.2: {}
csstype@3.1.3: {}
@@ -5919,7 +5945,7 @@ snapshots:
dependencies:
ms: 2.1.3
- debug@4.4.0:
+ debug@4.4.1:
dependencies:
ms: 2.1.3
@@ -5943,7 +5969,7 @@ snapshots:
dequal@2.0.3: {}
- detect-libc@2.0.3: {}
+ detect-libc@2.0.4: {}
detect-node-es@1.1.0: {}
@@ -5960,7 +5986,7 @@ snapshots:
dompurify@2.5.8:
optional: true
- dompurify@3.2.5:
+ dompurify@3.2.6:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -5973,8 +5999,8 @@ snapshots:
emblor@1.4.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
'@radix-ui/react-dialog': 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-popover': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-popover': 1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
class-variance-authority: 0.7.1
clsx: 2.1.1
cmdk: 0.2.1(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -5992,16 +6018,16 @@ snapshots:
emoji-regex@9.2.2: {}
- enhanced-resolve@5.18.1:
+ enhanced-resolve@5.18.3:
dependencies:
graceful-fs: 4.2.11
- tapable: 2.2.1
+ tapable: 2.2.2
entities@4.5.0: {}
entities@5.0.0: {}
- es-abstract@1.23.9:
+ es-abstract@1.24.0:
dependencies:
array-buffer-byte-length: 1.0.2
arraybuffer.prototype.slice: 1.0.4
@@ -6030,7 +6056,9 @@ snapshots:
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
@@ -6045,6 +6073,7 @@ snapshots:
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
@@ -6064,7 +6093,7 @@ snapshots:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-set-tostringtag: 2.1.0
function-bind: 1.1.2
@@ -6101,19 +6130,19 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-next@15.2.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4):
+ eslint-config-next@15.2.1(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4):
dependencies:
'@next/eslint-plugin-next': 15.2.1
- '@rushstack/eslint-patch': 1.11.0
- '@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- '@typescript-eslint/parser': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- eslint: 9.24.0(jiti@2.4.2)
+ '@rushstack/eslint-patch': 1.12.0
+ '@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.0)(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-jsx-a11y: 6.10.2(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-react: 7.37.5(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-react-hooks: 5.2.0(eslint@9.24.0(jiti@2.4.2))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.5.1))
+ eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.5.1))
+ eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.5.1))
optionalDependencies:
typescript: 5.4.4
transitivePeerDependencies:
@@ -6129,44 +6158,44 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@9.24.0(jiti@2.4.2)):
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
- debug: 4.4.0
- eslint: 9.24.0(jiti@2.4.2)
- get-tsconfig: 4.10.0
+ debug: 4.4.1
+ eslint: 9.32.0(jiti@2.5.1)
+ get-tsconfig: 4.10.1
is-bun-module: 2.0.0
stable-hash: 0.0.5
- tinyglobby: 0.2.12
- unrs-resolver: 1.3.3
+ tinyglobby: 0.2.14
+ unrs-resolver: 1.11.1
optionalDependencies:
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.0)(eslint@9.24.0(jiti@2.4.2))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@9.24.0(jiti@2.4.2)))(eslint@9.24.0(jiti@2.4.2)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
- eslint: 9.24.0(jiti@2.4.2)
+ '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
+ eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@9.24.0(jiti@2.4.2))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.0)(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@rtsao/scc': 1.1.0
- array-includes: 3.1.8
+ 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: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@9.24.0(jiti@2.4.2)))(eslint@9.24.0(jiti@2.4.2))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -6178,23 +6207,23 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 8.29.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.4.4)
+ '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.4.4)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-jsx-a11y@6.10.2(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.32.0(jiti@2.5.1)):
dependencies:
aria-query: 5.3.2
- array-includes: 3.1.8
+ array-includes: 3.1.9
array.prototype.flatmap: 1.3.3
ast-types-flow: 0.0.8
axe-core: 4.10.3
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- eslint: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@@ -6203,19 +6232,19 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
- eslint-plugin-react-hooks@5.2.0(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-react-hooks@5.2.0(eslint@9.32.0(jiti@2.5.1)):
dependencies:
- eslint: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
- eslint-plugin-react@7.37.5(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-react@7.37.5(eslint@9.32.0(jiti@2.5.1)):
dependencies:
- array-includes: 3.1.8
+ array-includes: 3.1.9
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
- eslint: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -6229,45 +6258,45 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
- eslint-plugin-unused-imports@3.2.0(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-unused-imports@3.2.0(eslint@9.32.0(jiti@2.5.1)):
dependencies:
- eslint: 9.24.0(jiti@2.4.2)
+ eslint: 9.32.0(jiti@2.5.1)
eslint-rule-composer: 0.3.0
eslint-rule-composer@0.3.0: {}
- eslint-scope@8.3.0:
+ eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
- eslint-visitor-keys@4.2.0: {}
+ eslint-visitor-keys@4.2.1: {}
- eslint@9.24.0(jiti@2.4.2):
+ eslint@9.32.0(jiti@2.5.1):
dependencies:
- '@eslint-community/eslint-utils': 4.5.1(eslint@9.24.0(jiti@2.4.2))
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
'@eslint-community/regexpp': 4.12.1
- '@eslint/config-array': 0.20.0
- '@eslint/config-helpers': 0.2.1
- '@eslint/core': 0.12.0
+ '@eslint/config-array': 0.21.0
+ '@eslint/config-helpers': 0.3.0
+ '@eslint/core': 0.15.1
'@eslint/eslintrc': 3.3.1
- '@eslint/js': 9.24.0
- '@eslint/plugin-kit': 0.2.8
+ '@eslint/js': 9.32.0
+ '@eslint/plugin-kit': 0.3.4
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
- '@humanwhocodes/retry': 0.4.2
- '@types/estree': 1.0.7
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
'@types/json-schema': 7.0.15
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.0
+ debug: 4.4.1
escape-string-regexp: 4.0.0
- eslint-scope: 8.3.0
- eslint-visitor-keys: 4.2.0
- espree: 10.3.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
esquery: 1.6.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@@ -6283,15 +6312,15 @@ snapshots:
natural-compare: 1.4.0
optionator: 0.9.4
optionalDependencies:
- jiti: 2.4.2
+ jiti: 2.5.1
transitivePeerDependencies:
- supports-color
- espree@10.3.0:
+ espree@10.4.0:
dependencies:
- acorn: 8.14.1
- acorn-jsx: 5.3.2(acorn@8.14.1)
- eslint-visitor-keys: 4.2.0
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
esquery@1.6.0:
dependencies:
@@ -6331,9 +6360,9 @@ snapshots:
dependencies:
reusify: 1.1.0
- fdir@6.4.3(picomatch@4.0.2):
+ fdir@6.4.6(picomatch@4.0.3):
optionalDependencies:
- picomatch: 4.0.2
+ picomatch: 4.0.3
fflate@0.8.2: {}
@@ -6368,9 +6397,9 @@ snapshots:
dependencies:
is-callable: 1.2.7
- formik@2.4.6(react@19.0.0):
+ formik@2.4.6(@types/react@19.0.10)(react@19.0.0):
dependencies:
- '@types/hoist-non-react-statics': 3.3.6
+ '@types/hoist-non-react-statics': 3.3.7(@types/react@19.0.10)
deepmerge: 2.2.1
hoist-non-react-statics: 3.3.2
lodash: 4.17.21
@@ -6379,11 +6408,13 @@ snapshots:
react-fast-compare: 2.0.4
tiny-warning: 1.0.3
tslib: 2.8.1
+ transitivePeerDependencies:
+ - '@types/react'
- framer-motion@12.6.3(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
+ framer-motion@12.23.12(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
- motion-dom: 12.6.3
- motion-utils: 12.6.3
+ motion-dom: 12.23.12
+ motion-utils: 12.23.6
tslib: 2.8.1
optionalDependencies:
'@emotion/is-prop-valid': 1.2.2
@@ -6431,7 +6462,7 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
- get-tsconfig@4.10.0:
+ get-tsconfig@4.10.1:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -6497,6 +6528,8 @@ snapshots:
ignore@5.3.2: {}
+ ignore@7.0.5: {}
+
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -6537,7 +6570,7 @@ snapshots:
is-bun-module@2.0.0:
dependencies:
- semver: 7.7.1
+ semver: 7.7.2
is-callable@1.2.7: {}
@@ -6577,6 +6610,8 @@ snapshots:
is-map@2.0.3: {}
+ is-negative-zero@2.0.3: {}
+
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -6636,7 +6671,7 @@ snapshots:
has-symbols: 1.1.0
set-function-name: 2.0.2
- jiti@2.4.2: {}
+ jiti@2.5.1: {}
jose@4.15.9: {}
@@ -6663,36 +6698,36 @@ snapshots:
jspdf@2.5.2:
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
atob: 2.1.2
btoa: 1.2.1
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.11
- core-js: 3.42.0
+ core-js: 3.45.0
dompurify: 2.5.8
html2canvas: 1.4.1
jspdf@3.0.1:
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
atob: 2.1.2
btoa: 1.2.1
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.11
- core-js: 3.42.0
- dompurify: 3.2.5
+ core-js: 3.45.0
+ dompurify: 3.2.6
html2canvas: 1.4.1
jsx-ast-utils@3.3.5:
dependencies:
- array-includes: 3.1.8
+ array-includes: 3.1.9
array.prototype.flat: 1.3.3
object.assign: 4.1.7
object.values: 1.2.1
- katex@0.16.21:
+ katex@0.16.22:
dependencies:
commander: 8.3.0
@@ -6711,56 +6746,56 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
- lightningcss-darwin-arm64@1.29.2:
+ lightningcss-darwin-arm64@1.30.1:
optional: true
- lightningcss-darwin-x64@1.29.2:
+ lightningcss-darwin-x64@1.30.1:
optional: true
- lightningcss-freebsd-x64@1.29.2:
+ lightningcss-freebsd-x64@1.30.1:
optional: true
- lightningcss-linux-arm-gnueabihf@1.29.2:
+ lightningcss-linux-arm-gnueabihf@1.30.1:
optional: true
- lightningcss-linux-arm64-gnu@1.29.2:
+ lightningcss-linux-arm64-gnu@1.30.1:
optional: true
- lightningcss-linux-arm64-musl@1.29.2:
+ lightningcss-linux-arm64-musl@1.30.1:
optional: true
- lightningcss-linux-x64-gnu@1.29.2:
+ lightningcss-linux-x64-gnu@1.30.1:
optional: true
- lightningcss-linux-x64-musl@1.29.2:
+ lightningcss-linux-x64-musl@1.30.1:
optional: true
- lightningcss-win32-arm64-msvc@1.29.2:
+ lightningcss-win32-arm64-msvc@1.30.1:
optional: true
- lightningcss-win32-x64-msvc@1.29.2:
+ lightningcss-win32-x64-msvc@1.30.1:
optional: true
- lightningcss@1.29.2:
+ lightningcss@1.30.1:
dependencies:
- detect-libc: 2.0.3
+ detect-libc: 2.0.4
optionalDependencies:
- lightningcss-darwin-arm64: 1.29.2
- lightningcss-darwin-x64: 1.29.2
- lightningcss-freebsd-x64: 1.29.2
- lightningcss-linux-arm-gnueabihf: 1.29.2
- lightningcss-linux-arm64-gnu: 1.29.2
- lightningcss-linux-arm64-musl: 1.29.2
- lightningcss-linux-x64-gnu: 1.29.2
- lightningcss-linux-x64-musl: 1.29.2
- lightningcss-win32-arm64-msvc: 1.29.2
- lightningcss-win32-x64-msvc: 1.29.2
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
- linkifyjs@4.2.0: {}
+ linkifyjs@4.3.2: {}
load-script@1.0.0: {}
@@ -6800,6 +6835,10 @@ snapshots:
dependencies:
react: 19.0.0
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.4
+
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
@@ -6822,21 +6861,29 @@ snapshots:
minimatch@3.1.2:
dependencies:
- brace-expansion: 1.1.11
+ brace-expansion: 1.1.12
minimatch@9.0.5:
dependencies:
- brace-expansion: 2.0.1
+ brace-expansion: 2.0.2
minimist@1.2.8: {}
- module-details-from-path@1.0.3: {}
+ minipass@7.1.2: {}
- motion-dom@12.6.3:
+ minizlib@3.0.2:
dependencies:
- motion-utils: 12.6.3
+ minipass: 7.1.2
- motion-utils@12.6.3: {}
+ mkdirp@3.0.1: {}
+
+ module-details-from-path@1.0.4: {}
+
+ motion-dom@12.23.12:
+ dependencies:
+ motion-utils: 12.23.6
+
+ motion-utils@12.23.6: {}
ms@2.0.0: {}
@@ -6844,19 +6891,21 @@ snapshots:
nanoid@3.3.11: {}
+ napi-postinstall@0.3.2: {}
+
natural-compare@1.4.0: {}
next-auth@4.24.11(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
- '@babel/runtime': 7.27.0
+ '@babel/runtime': 7.28.2
'@panva/hkdf': 1.2.1
cookie: 0.7.2
jose: 4.15.9
next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
oauth: 0.9.15
openid-client: 5.7.1
- preact: 10.26.5
- preact-render-to-string: 5.2.6(preact@10.26.5)
+ preact: 10.27.0
+ preact-render-to-string: 5.2.6(preact@10.27.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
uuid: 8.3.2
@@ -6867,7 +6916,7 @@ snapshots:
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
- caniuse-lite: 1.0.30001712
+ caniuse-lite: 1.0.30001731
postcss: 8.4.31
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -6882,7 +6931,7 @@ snapshots:
'@next/swc-win32-arm64-msvc': 15.3.5
'@next/swc-win32-x64-msvc': 15.3.5
'@opentelemetry/api': 1.9.0
- sharp: 0.34.1
+ sharp: 0.34.3
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -6929,14 +6978,14 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-object-atoms: 1.1.1
object.groupby@1.0.3:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
object.values@1.2.1:
dependencies:
@@ -6945,14 +6994,14 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
- oidc-token-hash@5.1.0: {}
+ oidc-token-hash@5.1.1: {}
openid-client@5.7.1:
dependencies:
jose: 4.15.9
lru-cache: 6.0.0
object-hash: 2.2.0
- oidc-token-hash: 5.1.0
+ oidc-token-hash: 5.1.1
optionator@0.9.4:
dependencies:
@@ -7006,11 +7055,11 @@ snapshots:
picomatch@2.3.1: {}
- picomatch@4.0.2: {}
+ picomatch@4.0.3: {}
plyr@3.7.8:
dependencies:
- core-js: 3.42.0
+ core-js: 3.45.0
custom-event-polyfill: 1.0.7
loadjs: 4.3.0
rangetouch: 2.0.1
@@ -7034,18 +7083,18 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
- postcss@8.5.3:
+ postcss@8.5.6:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
- preact-render-to-string@5.2.6(preact@10.26.5):
+ preact-render-to-string@5.2.6(preact@10.27.0):
dependencies:
- preact: 10.26.5
+ preact: 10.27.0
pretty-format: 3.8.0
- preact@10.26.5: {}
+ preact@10.27.0: {}
prelude-ls@1.2.1: {}
@@ -7059,46 +7108,46 @@ snapshots:
property-expr@2.0.6: {}
- prosemirror-changeset@2.2.1:
+ prosemirror-changeset@2.3.1:
dependencies:
- prosemirror-transform: 1.10.3
+ prosemirror-transform: 1.10.4
prosemirror-collab@1.3.1:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-commands@1.7.0:
+ prosemirror-commands@1.7.1:
dependencies:
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
+ prosemirror-transform: 1.10.4
- prosemirror-dropcursor@1.8.1:
+ prosemirror-dropcursor@1.8.2:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
- prosemirror-view: 1.39.1
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.40.1
prosemirror-gapcursor@1.3.2:
dependencies:
- prosemirror-keymap: 1.2.2
- prosemirror-model: 1.25.0
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-view: 1.39.1
+ prosemirror-view: 1.40.1
prosemirror-history@1.4.1:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
- prosemirror-view: 1.39.1
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.40.1
rope-sequence: 1.3.4
prosemirror-inputrules@1.5.0:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
+ prosemirror-transform: 1.10.4
- prosemirror-keymap@1.2.2:
+ prosemirror-keymap@1.2.3:
dependencies:
prosemirror-state: 1.4.3
w3c-keyname: 2.2.8
@@ -7107,60 +7156,60 @@ snapshots:
dependencies:
'@types/markdown-it': 14.1.2
markdown-it: 14.1.0
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
- prosemirror-menu@1.2.4:
+ prosemirror-menu@1.2.5:
dependencies:
crelt: 1.0.6
- prosemirror-commands: 1.7.0
+ prosemirror-commands: 1.7.1
prosemirror-history: 1.4.1
prosemirror-state: 1.4.3
- prosemirror-model@1.25.0:
+ prosemirror-model@1.25.2:
dependencies:
orderedmap: 2.1.1
prosemirror-schema-basic@1.2.4:
dependencies:
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
prosemirror-schema-list@1.5.1:
dependencies:
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
+ prosemirror-transform: 1.10.4
prosemirror-state@1.4.3:
dependencies:
- prosemirror-model: 1.25.0
- prosemirror-transform: 1.10.3
- prosemirror-view: 1.39.1
+ prosemirror-model: 1.25.2
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.40.1
- prosemirror-tables@1.6.4:
+ prosemirror-tables@1.7.1:
dependencies:
- prosemirror-keymap: 1.2.2
- prosemirror-model: 1.25.0
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
- prosemirror-view: 1.39.1
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.40.1
- prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.0)(prosemirror-state@1.4.3)(prosemirror-view@1.39.1):
+ prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.2)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1):
dependencies:
'@remirror/core-constants': 3.0.0
escape-string-regexp: 4.0.0
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-view: 1.39.1
+ prosemirror-view: 1.40.1
- prosemirror-transform@1.10.3:
+ prosemirror-transform@1.10.4:
dependencies:
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
- prosemirror-view@1.39.1:
+ prosemirror-view@1.40.1:
dependencies:
- prosemirror-model: 1.25.0
+ prosemirror-model: 1.25.2
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.3
+ prosemirror-transform: 1.10.4
punycode.js@2.3.1: {}
@@ -7218,9 +7267,9 @@ snapshots:
react-is@16.13.1: {}
- react-katex@3.0.1(prop-types@15.8.1)(react@19.0.0):
+ react-katex@3.1.0(prop-types@15.8.1)(react@19.0.0):
dependencies:
- katex: 0.16.21
+ katex: 0.16.22
prop-types: 15.8.1
react: 19.0.0
@@ -7269,7 +7318,7 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
- react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0):
+ react-remove-scroll@2.7.1(@types/react@19.0.10)(react@19.0.0):
dependencies:
react: 19.0.0
react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0)
@@ -7310,7 +7359,7 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
@@ -7320,8 +7369,6 @@ snapshots:
regenerator-runtime@0.13.11:
optional: true
- regenerator-runtime@0.14.1: {}
-
regexp.prototype.flags@1.5.4:
dependencies:
call-bind: 1.0.8
@@ -7335,8 +7382,8 @@ snapshots:
require-in-the-middle@7.5.2:
dependencies:
- debug: 4.4.0
- module-details-from-path: 1.0.3
+ debug: 4.4.1
+ module-details-from-path: 1.0.4
resolve: 1.22.10
transitivePeerDependencies:
- supports-color
@@ -7393,7 +7440,7 @@ snapshots:
semver@6.3.1: {}
- semver@7.7.1: {}
+ semver@7.7.2: {}
set-blocking@2.0.0: {}
@@ -7424,8 +7471,8 @@ snapshots:
sharp@0.33.5:
dependencies:
color: 4.2.3
- detect-libc: 2.0.3
- semver: 7.7.1
+ detect-libc: 2.0.4
+ semver: 7.7.2
optionalDependencies:
'@img/sharp-darwin-arm64': 0.33.5
'@img/sharp-darwin-x64': 0.33.5
@@ -7447,32 +7494,34 @@ snapshots:
'@img/sharp-win32-ia32': 0.33.5
'@img/sharp-win32-x64': 0.33.5
- sharp@0.34.1:
+ sharp@0.34.3:
dependencies:
color: 4.2.3
- detect-libc: 2.0.3
- semver: 7.7.1
+ detect-libc: 2.0.4
+ semver: 7.7.2
optionalDependencies:
- '@img/sharp-darwin-arm64': 0.34.1
- '@img/sharp-darwin-x64': 0.34.1
- '@img/sharp-libvips-darwin-arm64': 1.1.0
- '@img/sharp-libvips-darwin-x64': 1.1.0
- '@img/sharp-libvips-linux-arm': 1.1.0
- '@img/sharp-libvips-linux-arm64': 1.1.0
- '@img/sharp-libvips-linux-ppc64': 1.1.0
- '@img/sharp-libvips-linux-s390x': 1.1.0
- '@img/sharp-libvips-linux-x64': 1.1.0
- '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
- '@img/sharp-libvips-linuxmusl-x64': 1.1.0
- '@img/sharp-linux-arm': 0.34.1
- '@img/sharp-linux-arm64': 0.34.1
- '@img/sharp-linux-s390x': 0.34.1
- '@img/sharp-linux-x64': 0.34.1
- '@img/sharp-linuxmusl-arm64': 0.34.1
- '@img/sharp-linuxmusl-x64': 0.34.1
- '@img/sharp-wasm32': 0.34.1
- '@img/sharp-win32-ia32': 0.34.1
- '@img/sharp-win32-x64': 0.34.1
+ '@img/sharp-darwin-arm64': 0.34.3
+ '@img/sharp-darwin-x64': 0.34.3
+ '@img/sharp-libvips-darwin-arm64': 1.2.0
+ '@img/sharp-libvips-darwin-x64': 1.2.0
+ '@img/sharp-libvips-linux-arm': 1.2.0
+ '@img/sharp-libvips-linux-arm64': 1.2.0
+ '@img/sharp-libvips-linux-ppc64': 1.2.0
+ '@img/sharp-libvips-linux-s390x': 1.2.0
+ '@img/sharp-libvips-linux-x64': 1.2.0
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.0
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.0
+ '@img/sharp-linux-arm': 0.34.3
+ '@img/sharp-linux-arm64': 0.34.3
+ '@img/sharp-linux-ppc64': 0.34.3
+ '@img/sharp-linux-s390x': 0.34.3
+ '@img/sharp-linux-x64': 0.34.3
+ '@img/sharp-linuxmusl-arm64': 0.34.3
+ '@img/sharp-linuxmusl-x64': 0.34.3
+ '@img/sharp-wasm32': 0.34.3
+ '@img/sharp-win32-arm64': 0.34.3
+ '@img/sharp-win32-ia32': 0.34.3
+ '@img/sharp-win32-x64': 0.34.3
optional: true
shebang-command@2.0.0:
@@ -7522,6 +7571,11 @@ snapshots:
stackblur-canvas@2.7.0:
optional: true
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
streamsearch@1.1.0: {}
string-width@4.2.3:
@@ -7534,14 +7588,14 @@ snapshots:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
string.prototype.matchall@4.0.12:
dependencies:
call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-errors: 1.3.0
es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
@@ -7555,7 +7609,7 @@ snapshots:
string.prototype.repeat@1.0.0:
dependencies:
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
string.prototype.trim@1.2.10:
dependencies:
@@ -7563,7 +7617,7 @@ snapshots:
call-bound: 1.0.4
define-data-property: 1.1.4
define-properties: 1.2.1
- es-abstract: 1.23.9
+ es-abstract: 1.24.0
es-object-atoms: 1.1.1
has-property-descriptors: 1.0.2
@@ -7588,7 +7642,7 @@ snapshots:
strip-json-comments@3.1.1: {}
- styled-components@6.1.17(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
+ styled-components@6.1.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
'@emotion/is-prop-valid': 1.2.2
'@emotion/unitless': 0.8.1
@@ -7618,7 +7672,7 @@ snapshots:
svg-pathdata@6.0.3:
optional: true
- swr@2.3.3(react@19.0.0):
+ swr@2.3.4(react@19.0.0):
dependencies:
dequal: 2.0.3
react: 19.0.0
@@ -7628,13 +7682,22 @@ snapshots:
tailwind-merge@3.3.1: {}
- tailwindcss-animate@1.0.7(tailwindcss@4.1.3):
+ tailwindcss-animate@1.0.7(tailwindcss@4.1.11):
dependencies:
- tailwindcss: 4.1.3
+ tailwindcss: 4.1.11
- tailwindcss@4.1.3: {}
+ tailwindcss@4.1.11: {}
- tapable@2.2.1: {}
+ tapable@2.2.2: {}
+
+ tar@7.4.3:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.0.2
+ mkdirp: 3.0.1
+ yallist: 5.0.0
text-segmentation@1.0.3:
dependencies:
@@ -7646,10 +7709,10 @@ snapshots:
tiny-warning@1.0.3: {}
- tinyglobby@0.2.12:
+ tinyglobby@0.2.14:
dependencies:
- fdir: 6.4.3(picomatch@4.0.2)
- picomatch: 4.0.2
+ fdir: 6.4.6(picomatch@4.0.3)
+ picomatch: 4.0.3
tippy.js@6.3.7:
dependencies:
@@ -7732,23 +7795,29 @@ snapshots:
undici-types@5.26.5: {}
- unrs-resolver@1.3.3:
+ unrs-resolver@1.11.1:
+ dependencies:
+ napi-postinstall: 0.3.2
optionalDependencies:
- '@unrs/resolver-binding-darwin-arm64': 1.3.3
- '@unrs/resolver-binding-darwin-x64': 1.3.3
- '@unrs/resolver-binding-freebsd-x64': 1.3.3
- '@unrs/resolver-binding-linux-arm-gnueabihf': 1.3.3
- '@unrs/resolver-binding-linux-arm-musleabihf': 1.3.3
- '@unrs/resolver-binding-linux-arm64-gnu': 1.3.3
- '@unrs/resolver-binding-linux-arm64-musl': 1.3.3
- '@unrs/resolver-binding-linux-ppc64-gnu': 1.3.3
- '@unrs/resolver-binding-linux-s390x-gnu': 1.3.3
- '@unrs/resolver-binding-linux-x64-gnu': 1.3.3
- '@unrs/resolver-binding-linux-x64-musl': 1.3.3
- '@unrs/resolver-binding-wasm32-wasi': 1.3.3
- '@unrs/resolver-binding-win32-arm64-msvc': 1.3.3
- '@unrs/resolver-binding-win32-ia32-msvc': 1.3.3
- '@unrs/resolver-binding-win32-x64-msvc': 1.3.3
+ '@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
unsplash-js@7.0.19: {}
@@ -7851,6 +7920,8 @@ snapshots:
yallist@4.0.0: {}
+ yallist@5.0.0: {}
+
yargs-parser@18.1.3:
dependencies:
camelcase: 5.3.1
@@ -7880,7 +7951,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- yup@1.6.1:
+ yup@1.7.0:
dependencies:
property-expr: 2.0.6
tiny-case: 1.0.3
@@ -7889,5 +7960,5 @@ snapshots:
zeed-dom@0.15.1:
dependencies:
- css-what: 6.1.0
+ css-what: 6.2.2
entities: 5.0.0
diff --git a/apps/web/services/courses/courses.ts b/apps/web/services/courses/courses.ts
index 5bdfc5f7..95345e94 100644
--- a/apps/web/services/courses/courses.ts
+++ b/apps/web/services/courses/courses.ts
@@ -161,11 +161,20 @@ export async function bulkAddContributors(course_uuid: string, data: any, access
return res
}
-export async function bulkRemoveContributors(course_uuid: string, data: any, access_token:string | null | undefined) {
+export async function bulkRemoveContributors(course_uuid: string, data: any, access_token: string | null | undefined) {
const result: any = await fetch(
`${getAPIUrl()}courses/${course_uuid}/bulk-remove-contributors`,
- RequestBodyWithAuthHeader('PUT', data, null,access_token || undefined)
+ RequestBodyWithAuthHeader('PUT', data, null, access_token || undefined)
)
- const res = await getResponseMetadata(result)
+ const res = await errorHandling(result)
+ return res
+}
+
+export async function getCourseRights(course_uuid: string, access_token: string | null | undefined) {
+ const result: any = await fetch(
+ `${getAPIUrl()}courses/${course_uuid}/rights`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token || undefined)
+ )
+ const res = await errorHandling(result)
return res
}
\ No newline at end of file
diff --git a/apps/web/services/roles/roles.ts b/apps/web/services/roles/roles.ts
new file mode 100644
index 00000000..9bcbd043
--- /dev/null
+++ b/apps/web/services/roles/roles.ts
@@ -0,0 +1,68 @@
+import { getAPIUrl } from '@services/config/config'
+import { RequestBodyWithAuthHeader, getResponseMetadata } from '@services/utils/ts/requests'
+
+/*
+ Roles service matching available endpoints:
+ - GET roles/org/{org_id}
+ - POST roles/org/{org_id}
+ - GET roles/{role_id}
+ - PUT roles/{role_id}
+ - DELETE roles/{role_id}
+
+ Note: GET requests are usually fetched with SWR directly from components.
+*/
+
+export type CreateOrUpdateRoleBody = {
+ name: string
+ description?: string
+ rights: any
+ org_id?: number
+}
+
+export async function createRole(body: CreateOrUpdateRoleBody, access_token: string) {
+ const { org_id, ...payload } = body
+ if (!org_id) throw new Error('createRole requires org_id in body')
+ const result = await fetch(
+ `${getAPIUrl()}roles/org/${org_id}`,
+ RequestBodyWithAuthHeader('POST', payload, null, access_token)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
+
+export async function getRole(role_id: number | string, access_token?: string) {
+ const result = await fetch(
+ `${getAPIUrl()}roles/${role_id}`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
+
+export async function updateRole(
+ role_id: number | string,
+ body: CreateOrUpdateRoleBody,
+ access_token: string
+) {
+ const result = await fetch(
+ `${getAPIUrl()}roles/${role_id}`,
+ RequestBodyWithAuthHeader('PUT', body, null, access_token)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
+
+export async function deleteRole(
+ role_id: number | string,
+ _org_id: number | string | undefined,
+ access_token: string
+) {
+ const result = await fetch(
+ `${getAPIUrl()}roles/${role_id}`,
+ RequestBodyWithAuthHeader('DELETE', null, null, access_token)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
+
+
\ No newline at end of file