diff --git a/server/.gitignore b/server/.gitignore
index 7adb7fc0..2a82a747 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -113,7 +113,7 @@ ipython_config.py
__pypackages__/
# Celery stuff
-celerybeat-schedule
+celerybeat-schedule.db
celerybeat.pid
# SageMath parsed files
diff --git a/server/migrations/versions/1340c04426b8_add_meeting.py b/server/migrations/versions/1340c04426b8_add_meeting.py
new file mode 100644
index 00000000..272acaae
--- /dev/null
+++ b/server/migrations/versions/1340c04426b8_add_meeting.py
@@ -0,0 +1,26 @@
+"""add meeting
+
+Revision ID: 1340c04426b8
+Revises: b9348748bbbc
+Create Date: 2024-07-31 16:41:29.415218
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "1340c04426b8"
+down_revision: Union[str, None] = "b9348748bbbc"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ op.add_column("transcript", sa.Column("meeting_id", sa.String(), nullable=True))
+
+
+def downgrade() -> None:
+ op.drop_column("transcript", "meeting_id")
diff --git a/server/reflector/app.py b/server/reflector/app.py
index c952ebae..84ed2ea8 100644
--- a/server/reflector/app.py
+++ b/server/reflector/app.py
@@ -11,6 +11,8 @@ from reflector.events import subscribers_shutdown, subscribers_startup
from reflector.logger import logger
from reflector.metrics import metrics_init
from reflector.settings import settings
+from reflector.views.meetings import router as meetings_router
+from reflector.views.rooms import router as rooms_router
from reflector.views.rtc_offer import router as rtc_offer_router
from reflector.views.transcripts import router as transcripts_router
from reflector.views.transcripts_audio import router as transcripts_audio_router
@@ -68,6 +70,8 @@ metrics_init(app, instrumentator)
# register views
app.include_router(rtc_offer_router)
+app.include_router(meetings_router, prefix="/v1")
+app.include_router(rooms_router, prefix="/v1")
app.include_router(transcripts_router, prefix="/v1")
app.include_router(transcripts_audio_router, prefix="/v1")
app.include_router(transcripts_participants_router, prefix="/v1")
diff --git a/server/reflector/db/__init__.py b/server/reflector/db/__init__.py
index 9871c633..3378d0c0 100644
--- a/server/reflector/db/__init__.py
+++ b/server/reflector/db/__init__.py
@@ -7,6 +7,8 @@ database = databases.Database(settings.DATABASE_URL)
metadata = sqlalchemy.MetaData()
# import models
+import reflector.db.meetings # noqa
+import reflector.db.rooms # noqa
import reflector.db.transcripts # noqa
engine = sqlalchemy.create_engine(
diff --git a/server/reflector/db/meetings.py b/server/reflector/db/meetings.py
new file mode 100644
index 00000000..ba85bb38
--- /dev/null
+++ b/server/reflector/db/meetings.py
@@ -0,0 +1,115 @@
+from datetime import datetime, timezone
+
+import sqlalchemy
+from fastapi import HTTPException
+from pydantic import BaseModel
+from reflector.db import database, metadata
+
+meetings = sqlalchemy.Table(
+ "meeting",
+ metadata,
+ sqlalchemy.Column("id", sqlalchemy.String, primary_key=True),
+ sqlalchemy.Column("room_name", sqlalchemy.String),
+ sqlalchemy.Column("room_url", sqlalchemy.String),
+ sqlalchemy.Column("host_room_url", sqlalchemy.String),
+ sqlalchemy.Column("viewer_room_url", sqlalchemy.String),
+ sqlalchemy.Column("start_date", sqlalchemy.DateTime),
+ sqlalchemy.Column("end_date", sqlalchemy.DateTime),
+ sqlalchemy.Column("user_id", sqlalchemy.String),
+ sqlalchemy.Column("room_id", sqlalchemy.String),
+)
+
+
+class Meeting(BaseModel):
+ id: str
+ room_name: str
+ room_url: str
+ host_room_url: str
+ viewer_room_url: str
+ start_date: datetime
+ end_date: datetime
+ user_id: str | None = None
+ room_id: str | None = None
+
+
+class MeetingController:
+ async def create(
+ self,
+ id: str,
+ room_name: str,
+ room_url: str,
+ host_room_url: str,
+ viewer_room_url: str,
+ start_date: datetime,
+ end_date: datetime,
+ user_id: str,
+ room_id: str = None,
+ ):
+ """
+ Create a new meeting
+ """
+ meeting = Meeting(
+ id=id,
+ room_name=room_name,
+ room_url=room_url,
+ host_room_url=host_room_url,
+ viewer_room_url=viewer_room_url,
+ start_date=start_date,
+ end_date=end_date,
+ user_id=user_id,
+ room_id=room_id,
+ )
+ query = meetings.insert().values(**meeting.model_dump())
+ await database.execute(query)
+ return meeting
+
+ async def get_by_room_name(
+ self,
+ room_name: str,
+ ) -> Meeting:
+ """
+ Get a meeting by room name.
+ """
+ query = meetings.select().where(meetings.c.room_name == room_name)
+ result = await database.fetch_one(query)
+ if not result:
+ return None
+
+ return Meeting(**result)
+
+ async def get_latest(self, room_id: str) -> Meeting:
+ """
+ Get latest meeting for a room.
+ """
+ end_date = getattr(meetings.c, "end_date")
+ query = (
+ meetings.select()
+ .where(meetings.c.room_id == room_id)
+ .where(meetings.c.end_date > datetime.now(timezone.utc))
+ .order_by(end_date.desc())
+ )
+ result = await database.fetch_one(query)
+ if not result:
+ return None
+
+ return Meeting(**result)
+
+ async def get_by_id_for_http(self, meeting_id: str, user_id: str | None) -> Meeting:
+ """
+ Get a meeting by ID for HTTP request.
+
+ If not found, it will raise a 404 error.
+ """
+ query = meetings.select().where(meetings.c.id == meeting_id)
+ result = await database.fetch_one(query)
+ if not result:
+ raise HTTPException(status_code=404, detail="Meeting not found")
+
+ meeting = Meeting(**result)
+ if result["user_id"] != user_id:
+ meeting.host_room_url = ""
+
+ return meeting
+
+
+meetings_controller = MeetingController()
diff --git a/server/reflector/db/rooms.py b/server/reflector/db/rooms.py
new file mode 100644
index 00000000..5cd33dd9
--- /dev/null
+++ b/server/reflector/db/rooms.py
@@ -0,0 +1,139 @@
+from datetime import datetime
+
+import sqlalchemy
+from fastapi import HTTPException
+from pydantic import BaseModel, Field
+from reflector.db import database, metadata
+from reflector.db.transcripts import generate_uuid4
+from sqlalchemy.sql import false
+
+rooms = sqlalchemy.Table(
+ "room",
+ metadata,
+ sqlalchemy.Column("id", sqlalchemy.String, primary_key=True),
+ sqlalchemy.Column("name", sqlalchemy.String, nullable=False),
+ sqlalchemy.Column("user_id", sqlalchemy.String, nullable=False),
+ sqlalchemy.Column("created_at", sqlalchemy.DateTime, nullable=False),
+ sqlalchemy.Column(
+ "zulip_auto_post", sqlalchemy.Boolean, nullable=False, server_default=false()
+ ),
+ sqlalchemy.Column("zulip_stream", sqlalchemy.String),
+ sqlalchemy.Column("zulip_topic", sqlalchemy.String),
+)
+
+
+class Room(BaseModel):
+ id: str = Field(default_factory=generate_uuid4)
+ name: str
+ user_id: str
+ created_at: datetime = Field(default_factory=datetime.utcnow)
+ zulip_auto_post: bool = False
+ zulip_stream: str = ""
+ zulip_topic: str = ""
+
+
+class RoomController:
+ async def get_all(
+ self,
+ user_id: str | None = None,
+ order_by: str | None = None,
+ return_query: bool = False,
+ ) -> list[Room]:
+ """
+ Get all rooms
+
+ If `user_id` is specified, only return rooms that belong to the user.
+ Otherwise, return all rooms.
+
+ Parameters:
+ - `order_by`: field to order by, e.g. "-created_at"
+ """
+ query = rooms.select()
+ if user_id is not None:
+ query = query.where(rooms.c.user_id == user_id)
+
+ if order_by is not None:
+ field = getattr(rooms.c, order_by[1:])
+ if order_by.startswith("-"):
+ field = field.desc()
+ query = query.order_by(field)
+
+ if return_query:
+ return query
+
+ results = await database.fetch_all(query)
+ return results
+
+ async def add(
+ self,
+ name: str,
+ user_id: str,
+ ):
+ """
+ Add a new room
+ """
+ room = Room(
+ name=name,
+ user_id=user_id,
+ )
+ query = rooms.insert().values(**room.model_dump())
+ await database.execute(query)
+ return room
+
+ async def get_by_id(self, room_id: str, **kwargs) -> Room | None:
+ """
+ Get a room by id
+ """
+ query = rooms.select().where(rooms.c.id == room_id)
+ if "user_id" in kwargs:
+ query = query.where(rooms.c.user_id == kwargs["user_id"])
+ result = await database.fetch_one(query)
+ if not result:
+ return None
+ return Room(**result)
+
+ async def get_by_name(self, room_name: str, **kwargs) -> Room | None:
+ """
+ Get a room by name
+ """
+ query = rooms.select().where(rooms.c.name == room_name)
+ if "user_id" in kwargs:
+ query = query.where(rooms.c.user_id == kwargs["user_id"])
+ result = await database.fetch_one(query)
+ if not result:
+ return None
+ return Room(**result)
+
+ async def get_by_id_for_http(self, meeting_id: str, user_id: str | None) -> Room:
+ """
+ Get a room by ID for HTTP request.
+
+ If not found, it will raise a 404 error.
+ """
+ query = rooms.select().where(rooms.c.id == meeting_id)
+ result = await database.fetch_one(query)
+ if not result:
+ raise HTTPException(status_code=404, detail="Room not found")
+
+ room = Room(**result)
+
+ return room
+
+ async def remove_by_id(
+ self,
+ room_id: str,
+ user_id: str | None = None,
+ ) -> None:
+ """
+ Remove a room by id
+ """
+ room = await self.get_by_id(room_id, user_id=user_id)
+ if not room:
+ return
+ if user_id is not None and room.user_id != user_id:
+ return
+ query = rooms.delete().where(rooms.c.id == room_id)
+ await database.execute(query)
+
+
+rooms_controller = RoomController()
diff --git a/server/reflector/db/transcripts.py b/server/reflector/db/transcripts.py
index e25de7a1..37c63384 100644
--- a/server/reflector/db/transcripts.py
+++ b/server/reflector/db/transcripts.py
@@ -50,6 +50,10 @@ transcripts = sqlalchemy.Table(
nullable=False,
server_default="private",
),
+ sqlalchemy.Column(
+ "meeting_id",
+ sqlalchemy.String,
+ ),
)
@@ -145,6 +149,7 @@ class Transcript(BaseModel):
share_mode: Literal["private", "semi-private", "public"] = "private"
audio_location: str = "local"
reviewed: bool = False
+ meeting_id: str | None = None
def add_event(self, event: str, data: BaseModel) -> TranscriptEvent:
ev = TranscriptEvent(event=event, data=data.model_dump())
@@ -329,6 +334,18 @@ class TranscriptController:
return None
return Transcript(**result)
+ async def get_by_meeting_id(self, meeting_id: str, **kwargs) -> Transcript | None:
+ """
+ Get a transcript by meeting_id
+ """
+ query = transcripts.select().where(transcripts.c.meeting_id == meeting_id)
+ if "user_id" in kwargs:
+ query = query.where(transcripts.c.user_id == kwargs["user_id"])
+ result = await database.fetch_one(query)
+ if not result:
+ return None
+ return Transcript(**result)
+
async def get_by_id_for_http(
self,
transcript_id: str,
@@ -376,6 +393,8 @@ class TranscriptController:
source_language: str = "en",
target_language: str = "en",
user_id: str | None = None,
+ meeting_id: str | None = None,
+ share_mode: str = "private",
):
"""
Add a new transcript
@@ -385,6 +404,8 @@ class TranscriptController:
source_language=source_language,
target_language=target_language,
user_id=user_id,
+ meeting_id=meeting_id,
+ share_mode=share_mode,
)
query = transcripts.insert().values(**transcript.model_dump())
await database.execute(query)
diff --git a/server/reflector/settings.py b/server/reflector/settings.py
index d0ddc91a..a8b232d0 100644
--- a/server/reflector/settings.py
+++ b/server/reflector/settings.py
@@ -131,5 +131,11 @@ class Settings(BaseSettings):
# Healthcheck
HEALTHCHECK_URL: str | None = None
+ AWS_PROCESS_RECORDING_QUEUE_URL: str | None = None
+
+ WHEREBY_API_URL: str = "https://api.whereby.dev/v1/meetings"
+
+ WHEREBY_API_KEY: str | None = None
+
settings = Settings()
diff --git a/server/reflector/views/meetings.py b/server/reflector/views/meetings.py
new file mode 100644
index 00000000..d3a79a8c
--- /dev/null
+++ b/server/reflector/views/meetings.py
@@ -0,0 +1,56 @@
+from datetime import datetime, timedelta, timezone
+from typing import Annotated, Optional
+
+import reflector.auth as auth
+from fastapi import APIRouter, Depends
+from pydantic import BaseModel
+from reflector.db.meetings import meetings_controller
+from reflector.whereby import create_meeting
+
+router = APIRouter()
+
+
+class GetMeeting(BaseModel):
+ id: str
+ room_name: str
+ room_url: str
+ host_room_url: str
+ viewer_room_url: str
+ start_date: datetime
+ end_date: datetime
+
+
+@router.get("/meetings/{meeting_id}", response_model=GetMeeting)
+async def meeting_get(
+ meeting_id: str,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+ return await meetings_controller.get_by_id_for_http(meeting_id, user_id=user_id)
+
+
+@router.post("/meetings/", response_model=GetMeeting)
+async def meeting_create(
+ room_id: str,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+ meeting = await meetings_controller.get_latest(room_id)
+ if meeting is None:
+ start_date = datetime.now(timezone.utc)
+ end_date = start_date + timedelta(hours=1)
+ meeting = await create_meeting("", start_date=start_date, end_date=end_date)
+
+ meeting = await meetings_controller.add(
+ id=meeting["meetingId"],
+ room_name=meeting["roomName"],
+ room_url=meeting["roomUrl"],
+ host_room_url=meeting["hostRoomUrl"],
+ viewer_room_url=meeting["viewerRoomUrl"],
+ start_date=datetime.fromisoformat(meeting["startDate"]),
+ end_date=datetime.fromisoformat(meeting["endDate"]),
+ user_id=user_id,
+ room_id=room_id,
+ )
+
+ return await meetings_controller.get_by_id_for_http(meeting.id, user_id=user_id)
diff --git a/server/reflector/views/rooms.py b/server/reflector/views/rooms.py
new file mode 100644
index 00000000..a122dbe4
--- /dev/null
+++ b/server/reflector/views/rooms.py
@@ -0,0 +1,107 @@
+from datetime import datetime, timedelta, timezone
+from http.client import HTTPException
+from typing import Annotated, Optional
+
+import reflector.auth as auth
+from fastapi import APIRouter, Depends
+from fastapi_pagination import Page
+from fastapi_pagination.ext.databases import paginate
+from pydantic import BaseModel
+from reflector.db import database
+from reflector.db.meetings import meetings_controller
+from reflector.db.rooms import rooms_controller
+from reflector.settings import settings
+from reflector.views.meetings import GetMeeting
+from reflector.whereby import create_meeting
+
+router = APIRouter()
+
+
+class Room(BaseModel):
+ id: str
+ name: str
+ user_id: str
+ created_at: datetime
+
+
+class CreateRoom(BaseModel):
+ name: str
+
+
+class DeletionStatus(BaseModel):
+ status: str
+
+
+@router.get("/rooms", response_model=Page[Room])
+async def rooms_list(
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+) -> list[Room]:
+ user_id = user["sub"] if user else None
+
+ if not user and not settings.PUBLIC_MODE:
+ raise HTTPException(status_code=401, detail="Not authenticated")
+
+ user_id = user["sub"] if user else None
+ return await paginate(
+ database,
+ await rooms_controller.get_all(
+ user_id=user_id, order_by="-created_at", return_query=True
+ ),
+ )
+
+
+@router.post("/rooms", response_model=Room)
+async def rooms_create(
+ room: CreateRoom,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+
+ return await rooms_controller.add(
+ name=room.name,
+ user_id=user_id,
+ )
+
+
+@router.delete("/rooms/{room_id}", response_model=DeletionStatus)
+async def rooms_delete(
+ room_id: str,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+ room = await rooms_controller.get_by_id(room_id, user_id=user_id)
+ if not room:
+ raise HTTPException(status_code=404, detail="Room not found")
+ await rooms_controller.remove_by_id(room.id, user_id=user_id)
+ return DeletionStatus(status="ok")
+
+
+@router.post("/rooms/{room_name}/meeting", response_model=GetMeeting)
+async def rooms_create_meeting(
+ room_name: str,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+ room = await rooms_controller.get_by_name(room_name)
+ if not room:
+ raise HTTPException(status_code=404, detail="Room not found")
+
+ meeting = await meetings_controller.get_latest(room_id=room.id)
+ if meeting is None:
+ start_date = datetime.now(timezone.utc)
+ end_date = start_date + timedelta(hours=1)
+ meeting = await create_meeting("", start_date=start_date, end_date=end_date)
+
+ meeting = await meetings_controller.create(
+ id=meeting["meetingId"],
+ room_name=meeting["roomName"],
+ room_url=meeting["roomUrl"],
+ host_room_url=meeting["hostRoomUrl"],
+ viewer_room_url=meeting["viewerRoomUrl"],
+ start_date=datetime.fromisoformat(meeting["startDate"]),
+ end_date=datetime.fromisoformat(meeting["endDate"]),
+ user_id=user_id,
+ room_id=room.id,
+ )
+
+ return meeting
diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py
index bfc822b1..0ef17c88 100644
--- a/server/reflector/views/transcripts.py
+++ b/server/reflector/views/transcripts.py
@@ -1,4 +1,4 @@
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
from typing import Annotated, Literal, Optional
import reflector.auth as auth
@@ -7,6 +7,7 @@ from fastapi_pagination import Page
from fastapi_pagination.ext.databases import paginate
from jose import jwt
from pydantic import BaseModel, Field
+from reflector.db.meetings import meetings_controller
from reflector.db.transcripts import (
TranscriptParticipant,
TranscriptTopic,
@@ -15,6 +16,7 @@ from reflector.db.transcripts import (
from reflector.processors.types import Transcript as ProcessorTranscript
from reflector.processors.types import Word
from reflector.settings import settings
+from reflector.whereby import create_meeting
router = APIRouter()
@@ -51,6 +53,7 @@ class GetTranscript(BaseModel):
target_language: str | None
participants: list[TranscriptParticipant] | None
reviewed: bool
+ meeting_id: str | None
class CreateTranscript(BaseModel):
@@ -108,6 +111,37 @@ async def transcripts_create(
)
+@router.post("/transcripts/meeting", response_model=GetTranscript)
+async def transcripts_create_meeting(
+ info: CreateTranscript,
+ user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
+):
+ user_id = user["sub"] if user else None
+ start_date = datetime.now(timezone.utc)
+ end_date = start_date + timedelta(hours=1)
+ meeting = await create_meeting("", start_date=start_date, end_date=end_date)
+
+ meeting = await meetings_controller.create(
+ id=meeting["meetingId"],
+ room_name=meeting["roomName"],
+ room_url=meeting["roomUrl"],
+ host_room_url=meeting["hostRoomUrl"],
+ viewer_room_url=meeting["viewerRoomUrl"],
+ start_date=datetime.fromisoformat(meeting["startDate"]),
+ end_date=datetime.fromisoformat(meeting["endDate"]),
+ user_id=user_id,
+ )
+
+ return await transcripts_controller.add(
+ "",
+ source_language=info.source_language,
+ target_language=info.target_language,
+ user_id=user_id,
+ meeting_id=meeting.id,
+ share_mode="public",
+ )
+
+
# ==============================================================
# Single transcript
# ==============================================================
diff --git a/server/reflector/whereby.py b/server/reflector/whereby.py
new file mode 100644
index 00000000..46fdbff9
--- /dev/null
+++ b/server/reflector/whereby.py
@@ -0,0 +1,29 @@
+from datetime import datetime
+
+import httpx
+from reflector.settings import settings
+
+
+async def create_meeting(
+ room_name_prefix: str, start_date: datetime, end_date: datetime
+):
+ headers = {
+ "Content-Type": "application/json; charset=utf-8",
+ "Authorization": f"Bearer {settings.WHEREBY_API_KEY}",
+ }
+ data = {
+ "templateType": "viewerMode",
+ "isLocked": False,
+ "roomNamePrefix": room_name_prefix,
+ "roomNamePattern": "uuid",
+ "roomMode": "normal",
+ "startDate": start_date.isoformat(),
+ "endDate": end_date.isoformat(),
+ }
+
+ async with httpx.AsyncClient() as client:
+ response = await client.post(
+ settings.WHEREBY_API_URL, headers=headers, json=data, timeout=10
+ )
+ response.raise_for_status()
+ return response.json()
diff --git a/server/reflector/worker/app.py b/server/reflector/worker/app.py
index 5f1e4e74..3fb65c4e 100644
--- a/server/reflector/worker/app.py
+++ b/server/reflector/worker/app.py
@@ -16,11 +16,17 @@ else:
[
"reflector.pipelines.main_live_pipeline",
"reflector.worker.healthcheck",
+ "reflector.worker.process",
]
)
# crontab
- app.conf.beat_schedule = {}
+ app.conf.beat_schedule = {
+ "process_messages": {
+ "task": "reflector.worker.process.process_messages",
+ "schedule": 60.0,
+ }
+ }
if settings.HEALTHCHECK_URL:
app.conf.beat_schedule["healthcheck_ping"] = {
diff --git a/server/reflector/worker/process.py b/server/reflector/worker/process.py
new file mode 100644
index 00000000..7541b6cd
--- /dev/null
+++ b/server/reflector/worker/process.py
@@ -0,0 +1,104 @@
+import json
+import os
+from urllib.parse import unquote
+
+import av
+import boto3
+import structlog
+from celery import shared_task
+from celery.utils.log import get_task_logger
+from reflector.db.meetings import meetings_controller
+from reflector.db.transcripts import transcripts_controller
+from reflector.pipelines.main_live_pipeline import asynctask, task_pipeline_process
+from reflector.settings import settings
+
+logger = structlog.wrap_logger(get_task_logger(__name__))
+
+
+@shared_task
+def process_messages():
+ queue_url = settings.AWS_PROCESS_RECORDING_QUEUE_URL
+ if not queue_url:
+ logger.warning("No process recording queue url")
+ return
+ try:
+ logger.info("Receiving messages from: %s", queue_url)
+ sqs = boto3.client(
+ "sqs",
+ region_name=settings.TRANSCRIPT_STORAGE_AWS_REGION,
+ aws_access_key_id=settings.TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=settings.TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY,
+ )
+
+ response = sqs.receive_message(
+ QueueUrl=queue_url,
+ AttributeNames=["SentTimestamp"],
+ MaxNumberOfMessages=1,
+ MessageAttributeNames=["All"],
+ VisibilityTimeout=0,
+ WaitTimeSeconds=0,
+ )
+
+ for message in response.get("Messages", []):
+ receipt_handle = message["ReceiptHandle"]
+ body = json.loads(message["Body"])
+
+ for record in body.get("Records", []):
+ if record["eventName"].startswith("ObjectCreated"):
+ bucket = record["s3"]["bucket"]["name"]
+ key = unquote(record["s3"]["object"]["key"])
+ process_recording.delay(bucket, key)
+
+ sqs.delete_message(QueueUrl=queue_url, ReceiptHandle=receipt_handle)
+ logger.info("Processed and deleted message: %s", message)
+
+ except Exception as e:
+ logger.error("process_messages", error=str(e))
+
+
+@shared_task
+@asynctask
+async def process_recording(bucket_name: str, object_key: str):
+ logger.info("Processing recording: %s/%s", bucket_name, object_key)
+
+ # extract a guid from the object key
+ room_name = f"/{object_key[:36]}"
+ meeting = await meetings_controller.get_by_room_name(room_name)
+ transcript = await transcripts_controller.get_by_meeting_id(meeting.id)
+ if transcript is None:
+ transcript = await transcripts_controller.add(
+ "",
+ source_language="en",
+ target_language="en",
+ user_id=meeting.user_id,
+ meeting_id=meeting.id,
+ share_mode="public",
+ )
+
+ _, extension = os.path.splitext(object_key)
+ upload_filename = transcript.data_path / f"upload{extension}"
+ upload_filename.parent.mkdir(parents=True, exist_ok=True)
+
+ s3 = boto3.client(
+ "s3",
+ region_name=settings.TRANSCRIPT_STORAGE_AWS_REGION,
+ aws_access_key_id=settings.TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=settings.TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY,
+ )
+
+ with open(upload_filename, "wb") as f:
+ s3.download_fileobj(bucket_name, object_key, f)
+
+ container = av.open(upload_filename.as_posix())
+ try:
+ if not len(container.streams.audio):
+ raise Exception("File has no audio stream")
+ except Exception:
+ upload_filename.unlink()
+ raise
+ finally:
+ container.close()
+
+ await transcripts_controller.update(transcript, {"status": "uploaded"})
+
+ task_pipeline_process.delay(transcript_id=transcript.id)
diff --git a/www/app/[domain]/layout.tsx b/www/app/[domain]/layout.tsx
index 95d0f544..f4145d32 100644
--- a/www/app/[domain]/layout.tsx
+++ b/www/app/[domain]/layout.tsx
@@ -76,7 +76,7 @@ type LayoutProps = {
export default async function RootLayout({ children, params }: LayoutProps) {
const config = await getConfig(params.domain);
- const { requireLogin, privacy, browse } = config.features;
+ const { requireLogin, privacy, browse, rooms } = config.features;
const hasAuthCookie = !!cookies().get(SESSION_COOKIE_NAME);
return (
@@ -154,6 +154,21 @@ export default async function RootLayout({ children, params }: LayoutProps) {
) : (
<>>
)}
+ {rooms ? (
+ <>
+ ·
+
+ Rooms
+
+ >
+ ) : (
+ <>>
+ )}
·
{privacy ? (
diff --git a/www/app/[domain]/rooms/[roomName]/page.tsx b/www/app/[domain]/rooms/[roomName]/page.tsx
new file mode 100644
index 00000000..3d16e532
--- /dev/null
+++ b/www/app/[domain]/rooms/[roomName]/page.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import "@whereby.com/browser-sdk/embed";
+import { useCallback, useEffect, useRef } from "react";
+import useRoomMeeting from "../../rooms/useRoomMeeting";
+
+export type RoomDetails = {
+ params: {
+ roomName: string;
+ };
+};
+
+export default function Room(details: RoomDetails) {
+ const wherebyRef = useRef(null);
+ const roomName = details.params.roomName;
+ const meeting = useRoomMeeting(roomName);
+
+ const roomUrl = meeting?.response?.host_room_url
+ ? meeting?.response?.host_room_url
+ : meeting?.response?.room_url;
+
+ return (
+ <>
+ {roomUrl && (
+
+ )}
+ >
+ );
+}
diff --git a/www/app/[domain]/rooms/page.tsx b/www/app/[domain]/rooms/page.tsx
new file mode 100644
index 00000000..dc6c61ba
--- /dev/null
+++ b/www/app/[domain]/rooms/page.tsx
@@ -0,0 +1,171 @@
+"use client";
+
+import {
+ Box,
+ Button,
+ Card,
+ CardBody,
+ Flex,
+ FormControl,
+ FormHelperText,
+ FormLabel,
+ Grid,
+ Heading,
+ Input,
+ Link,
+ Modal,
+ ModalBody,
+ ModalCloseButton,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ ModalOverlay,
+ Spacer,
+ Spinner,
+ useDisclosure,
+ VStack,
+ Text,
+ Menu,
+ MenuButton,
+ MenuList,
+ MenuItem,
+ AlertDialog,
+ IconButton,
+} from "@chakra-ui/react";
+import NextLink from "next";
+import React, { ReactNode, useState } from "react";
+import { Container } from "@chakra-ui/react";
+import { PlusSquareIcon } from "@chakra-ui/icons";
+import useApi from "../../lib/useApi";
+import useRoomList from "./useRoomList";
+import { FaEllipsisVertical, FaTrash } from "react-icons/fa6";
+import next from "next";
+
+export default function RoomsList() {
+ const { isOpen, onOpen, onClose } = useDisclosure();
+ const [roomName, setRoomName] = useState("");
+ const api = useApi();
+ const [page, setPage] = useState(1);
+ const { loading, response, refetch } = useRoomList(page);
+
+ const handleAddRoom = async () => {
+ try {
+ const response = await api?.v1RoomsCreate({
+ requestBody: { name: roomName },
+ });
+ setRoomName("");
+ refetch();
+ } catch (err) {}
+ onClose();
+ };
+
+ const handleDeleteRoom = async (roomId: string) => {
+ try {
+ const response = await api?.v1RoomsDelete({
+ roomId,
+ });
+ refetch();
+ } catch (err) {}
+ };
+
+ const handleRoomNameChange = (e) => {
+ setRoomName(e.target.value);
+ };
+
+ if (loading && !response)
+ return (
+
+
+
+ );
+
+ return (
+ <>
+
+
+ Rooms
+
+
+
+
+
+ Add Room
+
+
+
+ Room name
+
+ Please enter room name
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {response?.items && response.items.length > 0 ? (
+ response.items.map((room) => (
+
+
+
+
+
+ {room.name}
+
+
+
+
+
+
+
+ ))
+ ) : (
+
+ No rooms found
+
+ )}
+
+
+ >
+ );
+}
diff --git a/www/app/[domain]/rooms/useRoomList.tsx b/www/app/[domain]/rooms/useRoomList.tsx
new file mode 100644
index 00000000..d0aad727
--- /dev/null
+++ b/www/app/[domain]/rooms/useRoomList.tsx
@@ -0,0 +1,47 @@
+import { useEffect, useState } from "react";
+import { useError } from "../../(errors)/errorContext";
+import useApi from "../../lib/useApi";
+import { Page_Room_ } from "../../api";
+
+type RoomList = {
+ response: Page_Room_ | null;
+ loading: boolean;
+ error: Error | null;
+ refetch: () => void;
+};
+
+//always protected
+const useRoomList = (page: number): RoomList => {
+ const [response, setResponse] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setErrorState] = useState(null);
+ const { setError } = useError();
+ const api = useApi();
+ const [refetchCount, setRefetchCount] = useState(0);
+
+ const refetch = () => {
+ setLoading(true);
+ setRefetchCount(refetchCount + 1);
+ };
+
+ useEffect(() => {
+ if (!api) return;
+ setLoading(true);
+ api
+ .v1RoomsList({ page })
+ .then((response) => {
+ setResponse(response);
+ setLoading(false);
+ })
+ .catch((err) => {
+ setResponse(null);
+ setLoading(false);
+ setError(err);
+ setErrorState(err);
+ });
+ }, [!api, page, refetchCount]);
+
+ return { response, loading, error, refetch };
+};
+
+export default useRoomList;
diff --git a/www/app/[domain]/rooms/useRoomMeeting.tsx b/www/app/[domain]/rooms/useRoomMeeting.tsx
new file mode 100644
index 00000000..1d625d25
--- /dev/null
+++ b/www/app/[domain]/rooms/useRoomMeeting.tsx
@@ -0,0 +1,70 @@
+import { useEffect, useState } from "react";
+import { useError } from "../../(errors)/errorContext";
+import { GetMeeting } from "../../api";
+import { shouldShowError } from "../../lib/errorUtils";
+import useApi from "../../lib/useApi";
+
+type ErrorMeeting = {
+ error: Error;
+ loading: false;
+ response: null;
+ reload: () => void;
+};
+
+type LoadingMeeting = {
+ response: null;
+ loading: true;
+ error: false;
+ reload: () => void;
+};
+
+type SuccessMeeting = {
+ response: GetMeeting;
+ loading: false;
+ error: null;
+ reload: () => void;
+};
+
+const useRoomMeeting = (
+ roomName: string | null | undefined,
+): ErrorMeeting | LoadingMeeting | SuccessMeeting => {
+ const [response, setResponse] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setErrorState] = useState(null);
+ const [reload, setReload] = useState(0);
+ const { setError } = useError();
+ const api = useApi();
+ const reloadHandler = () => setReload((prev) => prev + 1);
+
+ useEffect(() => {
+ if (!roomName || !api) return;
+
+ if (!response) {
+ setLoading(true);
+ }
+
+ api
+ .v1RoomsCreateMeeting({ roomName })
+ .then((result) => {
+ setResponse(result);
+ setLoading(false);
+ console.debug("Meeting Loaded:", result);
+ })
+ .catch((error) => {
+ const shouldShowHuman = shouldShowError(error);
+ if (shouldShowHuman) {
+ setError(error, "There was an error loading the meeting");
+ } else {
+ setError(error);
+ }
+ setErrorState(error);
+ });
+ }, [roomName, !api, reload]);
+
+ return { response, loading, error, reload: reloadHandler } as
+ | ErrorMeeting
+ | LoadingMeeting
+ | SuccessMeeting;
+};
+
+export default useRoomMeeting;
diff --git a/www/app/[domain]/transcripts/[transcriptId]/meeting/page.tsx b/www/app/[domain]/transcripts/[transcriptId]/meeting/page.tsx
new file mode 100644
index 00000000..6dde62f1
--- /dev/null
+++ b/www/app/[domain]/transcripts/[transcriptId]/meeting/page.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import "@whereby.com/browser-sdk/embed";
+import { useCallback, useEffect, useRef } from "react";
+import useTranscript from "../../useTranscript";
+import useMeeting from "../../useMeeting";
+
+export type TranscriptDetails = {
+ params: {
+ transcriptId: string;
+ };
+};
+
+export default function TranscriptMeeting(details: TranscriptDetails) {
+ const wherebyRef = useRef(null);
+
+ const transcript = useTranscript(details.params.transcriptId);
+ const meeting = useMeeting(transcript?.response?.meeting_id);
+ const roomUrl = meeting?.response?.host_room_url
+ ? meeting?.response?.host_room_url
+ : meeting?.response?.room_url;
+
+ return (
+ <>
+ {roomUrl && (
+
+ )}
+ >
+ );
+}
diff --git a/www/app/[domain]/transcripts/createTranscript.ts b/www/app/[domain]/transcripts/createTranscript.ts
index 015c82de..8bf2aee8 100644
--- a/www/app/[domain]/transcripts/createTranscript.ts
+++ b/www/app/[domain]/transcripts/createTranscript.ts
@@ -9,6 +9,7 @@ type UseCreateTranscript = {
loading: boolean;
error: Error | null;
create: (transcriptCreationDetails: CreateTranscript) => void;
+ createMeeting: (transcriptCreationDetails: CreateTranscript) => void;
};
const useCreateTranscript = (): UseCreateTranscript => {
@@ -39,7 +40,28 @@ const useCreateTranscript = (): UseCreateTranscript => {
});
};
- return { transcript, loading, error, create };
+ const createMeeting = (transcriptCreationDetails: CreateTranscript) => {
+ if (loading || !api) return;
+
+ setLoading(true);
+
+ api
+ .v1TranscriptsCreateMeeting({ requestBody: transcriptCreationDetails })
+ .then((transcript) => {
+ setTranscript(transcript);
+ setLoading(false);
+ })
+ .catch((err) => {
+ setError(
+ err,
+ "There was an issue creating a transcript, please try again.",
+ );
+ setErrorState(err);
+ setLoading(false);
+ });
+ };
+
+ return { transcript, loading, error, create, createMeeting };
};
export default useCreateTranscript;
diff --git a/www/app/[domain]/transcripts/new/page.tsx b/www/app/[domain]/transcripts/new/page.tsx
index 4f02d6de..57d85ff8 100644
--- a/www/app/[domain]/transcripts/new/page.tsx
+++ b/www/app/[domain]/transcripts/new/page.tsx
@@ -32,6 +32,7 @@ const TranscriptCreate = () => {
const [loadingRecord, setLoadingRecord] = useState(false);
const [loadingUpload, setLoadingUpload] = useState(false);
+ const [loadingMeeting, setLoadingMeeting] = useState(false);
const send = () => {
if (loadingRecord || createTranscript.loading || permissionDenied) return;
@@ -45,8 +46,17 @@ const TranscriptCreate = () => {
createTranscript.create({ name, target_language: targetLanguage });
};
+ const startMeeting = () => {
+ if (loadingMeeting || createTranscript.loading || permissionDenied) return;
+ setLoadingMeeting(true);
+ createTranscript.createMeeting({ name, target_language: targetLanguage });
+ };
+
useEffect(() => {
- const action = loadingRecord ? "record" : "upload";
+ let action = "record";
+ if (loadingUpload) action = "upload";
+ if (loadingMeeting) action = "meeting";
+
createTranscript.transcript &&
router.push(`/transcripts/${createTranscript.transcript.id}/${action}`);
}, [createTranscript.transcript]);
@@ -152,6 +162,23 @@ const TranscriptCreate = () => {
>
{loadingUpload ? "Loading..." : "Upload File"}
+
+ {requireLogin && (
+ <>
+
+ OR
+
+
+ >
+ )}
)}
diff --git a/www/app/[domain]/transcripts/useMeeting.ts b/www/app/[domain]/transcripts/useMeeting.ts
new file mode 100644
index 00000000..f99b5ca8
--- /dev/null
+++ b/www/app/[domain]/transcripts/useMeeting.ts
@@ -0,0 +1,70 @@
+import { useEffect, useState } from "react";
+import { useError } from "../../(errors)/errorContext";
+import { GetMeeting } from "../../api";
+import { shouldShowError } from "../../lib/errorUtils";
+import useApi from "../../lib/useApi";
+
+type ErrorMeeting = {
+ error: Error;
+ loading: false;
+ response: null;
+ reload: () => void;
+};
+
+type LoadingMeeting = {
+ response: null;
+ loading: true;
+ error: false;
+ reload: () => void;
+};
+
+type SuccessMeeting = {
+ response: GetMeeting;
+ loading: false;
+ error: null;
+ reload: () => void;
+};
+
+const useMeeting = (
+ id: string | null | undefined,
+): ErrorMeeting | LoadingMeeting | SuccessMeeting => {
+ const [response, setResponse] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setErrorState] = useState(null);
+ const [reload, setReload] = useState(0);
+ const { setError } = useError();
+ const api = useApi();
+ const reloadHandler = () => setReload((prev) => prev + 1);
+
+ useEffect(() => {
+ if (!id || !api) return;
+
+ if (!response) {
+ setLoading(true);
+ }
+
+ api
+ .v1MeetingGet({ meetingId: id })
+ .then((result) => {
+ setResponse(result);
+ setLoading(false);
+ console.debug("Meeting Loaded:", result);
+ })
+ .catch((error) => {
+ const shouldShowHuman = shouldShowError(error);
+ if (shouldShowHuman) {
+ setError(error, "There was an error loading the meeting");
+ } else {
+ setError(error);
+ }
+ setErrorState(error);
+ });
+ }, [id, !api, reload]);
+
+ return { response, loading, error, reload: reloadHandler } as
+ | ErrorMeeting
+ | LoadingMeeting
+ | SuccessMeeting;
+};
+
+export default useMeeting;
diff --git a/www/app/api/schemas.gen.ts b/www/app/api/schemas.gen.ts
index afa34829..513ffbec 100644
--- a/www/app/api/schemas.gen.ts
+++ b/www/app/api/schemas.gen.ts
@@ -53,6 +53,18 @@ export const $CreateParticipant = {
title: "CreateParticipant",
} as const;
+export const $CreateRoom = {
+ properties: {
+ name: {
+ type: "string",
+ title: "Name",
+ },
+ },
+ type: "object",
+ required: ["name"],
+ title: "CreateRoom",
+} as const;
+
export const $CreateTranscript = {
properties: {
name: {
@@ -87,6 +99,52 @@ export const $DeletionStatus = {
title: "DeletionStatus",
} as const;
+export const $GetMeeting = {
+ properties: {
+ id: {
+ type: "string",
+ title: "Id",
+ },
+ room_name: {
+ type: "string",
+ title: "Room Name",
+ },
+ room_url: {
+ type: "string",
+ title: "Room Url",
+ },
+ host_room_url: {
+ type: "string",
+ title: "Host Room Url",
+ },
+ viewer_room_url: {
+ type: "string",
+ title: "Viewer Room Url",
+ },
+ start_date: {
+ type: "string",
+ format: "date-time",
+ title: "Start Date",
+ },
+ end_date: {
+ type: "string",
+ format: "date-time",
+ title: "End Date",
+ },
+ },
+ type: "object",
+ required: [
+ "id",
+ "room_name",
+ "room_url",
+ "host_room_url",
+ "viewer_room_url",
+ "start_date",
+ "end_date",
+ ],
+ title: "GetMeeting",
+} as const;
+
export const $GetTranscript = {
properties: {
id: {
@@ -203,6 +261,17 @@ export const $GetTranscript = {
type: "boolean",
title: "Reviewed",
},
+ meeting_id: {
+ anyOf: [
+ {
+ type: "string",
+ },
+ {
+ type: "null",
+ },
+ ],
+ title: "Meeting Id",
+ },
},
type: "object",
required: [
@@ -220,6 +289,7 @@ export const $GetTranscript = {
"target_language",
"participants",
"reviewed",
+ "meeting_id",
],
title: "GetTranscript",
} as const;
@@ -471,6 +541,62 @@ export const $Page_GetTranscript_ = {
title: "Page[GetTranscript]",
} as const;
+export const $Page_Room_ = {
+ properties: {
+ items: {
+ items: {
+ $ref: "#/components/schemas/Room",
+ },
+ type: "array",
+ title: "Items",
+ },
+ total: {
+ type: "integer",
+ minimum: 0,
+ title: "Total",
+ },
+ page: {
+ anyOf: [
+ {
+ type: "integer",
+ minimum: 1,
+ },
+ {
+ type: "null",
+ },
+ ],
+ title: "Page",
+ },
+ size: {
+ anyOf: [
+ {
+ type: "integer",
+ minimum: 1,
+ },
+ {
+ type: "null",
+ },
+ ],
+ title: "Size",
+ },
+ pages: {
+ anyOf: [
+ {
+ type: "integer",
+ minimum: 0,
+ },
+ {
+ type: "null",
+ },
+ ],
+ title: "Pages",
+ },
+ },
+ type: "object",
+ required: ["items", "total", "page", "size"],
+ title: "Page[Room]",
+} as const;
+
export const $Participant = {
properties: {
id: {
@@ -498,6 +624,31 @@ export const $Participant = {
title: "Participant",
} as const;
+export const $Room = {
+ properties: {
+ id: {
+ type: "string",
+ title: "Id",
+ },
+ name: {
+ type: "string",
+ title: "Name",
+ },
+ user_id: {
+ type: "string",
+ title: "User Id",
+ },
+ created_at: {
+ type: "string",
+ format: "date-time",
+ title: "Created At",
+ },
+ },
+ type: "object",
+ required: ["id", "name", "user_id", "created_at"],
+ title: "Room",
+} as const;
+
export const $RtcOffer = {
properties: {
sdp: {
diff --git a/www/app/api/services.gen.ts b/www/app/api/services.gen.ts
index c1690b81..91eeb7bd 100644
--- a/www/app/api/services.gen.ts
+++ b/www/app/api/services.gen.ts
@@ -4,10 +4,24 @@ import type { CancelablePromise } from "./core/CancelablePromise";
import type { BaseHttpRequest } from "./core/BaseHttpRequest";
import type {
MetricsResponse,
+ V1MeetingGetData,
+ V1MeetingGetResponse,
+ V1MeetingCreateData,
+ V1MeetingCreateResponse,
+ V1RoomsListData,
+ V1RoomsListResponse,
+ V1RoomsCreateData,
+ V1RoomsCreateResponse,
+ V1RoomsDeleteData,
+ V1RoomsDeleteResponse,
+ V1RoomsCreateMeetingData,
+ V1RoomsCreateMeetingResponse,
V1TranscriptsListData,
V1TranscriptsListResponse,
V1TranscriptsCreateData,
V1TranscriptsCreateResponse,
+ V1TranscriptsCreateMeetingData,
+ V1TranscriptsCreateMeetingResponse,
V1TranscriptGetData,
V1TranscriptGetResponse,
V1TranscriptUpdateData,
@@ -67,6 +81,139 @@ export class DefaultService {
});
}
+ /**
+ * Meeting Get
+ * @param data The data for the request.
+ * @param data.meetingId
+ * @returns GetMeeting Successful Response
+ * @throws ApiError
+ */
+ public v1MeetingGet(
+ data: V1MeetingGetData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "GET",
+ url: "/v1/meetings/{meeting_id}",
+ path: {
+ meeting_id: data.meetingId,
+ },
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Meeting Create
+ * @param data The data for the request.
+ * @param data.roomId
+ * @returns GetMeeting Successful Response
+ * @throws ApiError
+ */
+ public v1MeetingCreate(
+ data: V1MeetingCreateData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "POST",
+ url: "/v1/meetings/",
+ query: {
+ room_id: data.roomId,
+ },
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Rooms List
+ * @param data The data for the request.
+ * @param data.page Page number
+ * @param data.size Page size
+ * @returns Page_Room_ Successful Response
+ * @throws ApiError
+ */
+ public v1RoomsList(
+ data: V1RoomsListData = {},
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "GET",
+ url: "/v1/rooms",
+ query: {
+ page: data.page,
+ size: data.size,
+ },
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Rooms Create
+ * @param data The data for the request.
+ * @param data.requestBody
+ * @returns Room Successful Response
+ * @throws ApiError
+ */
+ public v1RoomsCreate(
+ data: V1RoomsCreateData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "POST",
+ url: "/v1/rooms",
+ body: data.requestBody,
+ mediaType: "application/json",
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Rooms Delete
+ * @param data The data for the request.
+ * @param data.roomId
+ * @returns DeletionStatus Successful Response
+ * @throws ApiError
+ */
+ public v1RoomsDelete(
+ data: V1RoomsDeleteData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "DELETE",
+ url: "/v1/rooms/{room_id}",
+ path: {
+ room_id: data.roomId,
+ },
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
+ /**
+ * Rooms Create Meeting
+ * @param data The data for the request.
+ * @param data.roomName
+ * @returns GetMeeting Successful Response
+ * @throws ApiError
+ */
+ public v1RoomsCreateMeeting(
+ data: V1RoomsCreateMeetingData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "POST",
+ url: "/v1/rooms/{room_name}/meeting",
+ path: {
+ room_name: data.roomName,
+ },
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
/**
* Transcripts List
* @param data The data for the request.
@@ -112,6 +259,27 @@ export class DefaultService {
});
}
+ /**
+ * Transcripts Create Meeting
+ * @param data The data for the request.
+ * @param data.requestBody
+ * @returns GetTranscript Successful Response
+ * @throws ApiError
+ */
+ public v1TranscriptsCreateMeeting(
+ data: V1TranscriptsCreateMeetingData,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: "POST",
+ url: "/v1/transcripts/meeting",
+ body: data.requestBody,
+ mediaType: "application/json",
+ errors: {
+ 422: "Validation Error",
+ },
+ });
+ }
+
/**
* Transcript Get
* @param data The data for the request.
diff --git a/www/app/api/types.gen.ts b/www/app/api/types.gen.ts
index bb811560..e0fd3f78 100644
--- a/www/app/api/types.gen.ts
+++ b/www/app/api/types.gen.ts
@@ -14,6 +14,10 @@ export type CreateParticipant = {
name: string;
};
+export type CreateRoom = {
+ name: string;
+};
+
export type CreateTranscript = {
name: string;
source_language?: string;
@@ -24,6 +28,16 @@ export type DeletionStatus = {
status: string;
};
+export type GetMeeting = {
+ id: string;
+ room_name: string;
+ room_url: string;
+ host_room_url: string;
+ viewer_room_url: string;
+ start_date: string;
+ end_date: string;
+};
+
export type GetTranscript = {
id: string;
user_id: string | null;
@@ -40,6 +54,7 @@ export type GetTranscript = {
target_language: string | null;
participants: Array | null;
reviewed: boolean;
+ meeting_id: string | null;
};
export type GetTranscriptSegmentTopic = {
@@ -92,12 +107,27 @@ export type Page_GetTranscript_ = {
pages?: number | null;
};
+export type Page_Room_ = {
+ items: Array;
+ total: number;
+ page: number | null;
+ size: number | null;
+ pages?: number | null;
+};
+
export type Participant = {
id: string;
speaker: number | null;
name: string;
};
+export type Room = {
+ id: string;
+ name: string;
+ user_id: string;
+ created_at: string;
+};
+
export type RtcOffer = {
sdp: string;
type: string;
@@ -167,6 +197,49 @@ export type Word = {
export type MetricsResponse = unknown;
+export type V1MeetingGetData = {
+ meetingId: string;
+};
+
+export type V1MeetingGetResponse = GetMeeting;
+
+export type V1MeetingCreateData = {
+ roomId: string;
+};
+
+export type V1MeetingCreateResponse = GetMeeting;
+
+export type V1RoomsListData = {
+ /**
+ * Page number
+ */
+ page?: number;
+ /**
+ * Page size
+ */
+ size?: number;
+};
+
+export type V1RoomsListResponse = Page_Room_;
+
+export type V1RoomsCreateData = {
+ requestBody: CreateRoom;
+};
+
+export type V1RoomsCreateResponse = Room;
+
+export type V1RoomsDeleteData = {
+ roomId: string;
+};
+
+export type V1RoomsDeleteResponse = DeletionStatus;
+
+export type V1RoomsCreateMeetingData = {
+ roomName: string;
+};
+
+export type V1RoomsCreateMeetingResponse = GetMeeting;
+
export type V1TranscriptsListData = {
/**
* Page number
@@ -186,6 +259,12 @@ export type V1TranscriptsCreateData = {
export type V1TranscriptsCreateResponse = GetTranscript;
+export type V1TranscriptsCreateMeetingData = {
+ requestBody: CreateTranscript;
+};
+
+export type V1TranscriptsCreateMeetingResponse = GetTranscript;
+
export type V1TranscriptGetData = {
transcriptId: string;
};
@@ -336,6 +415,94 @@ export type $OpenApiTs = {
};
};
};
+ "/v1/meetings/{meeting_id}": {
+ get: {
+ req: V1MeetingGetData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: GetMeeting;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
+ "/v1/meetings/": {
+ post: {
+ req: V1MeetingCreateData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: GetMeeting;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
+ "/v1/rooms": {
+ get: {
+ req: V1RoomsListData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: Page_Room_;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ post: {
+ req: V1RoomsCreateData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: Room;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
+ "/v1/rooms/{room_id}": {
+ delete: {
+ req: V1RoomsDeleteData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: DeletionStatus;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
+ "/v1/rooms/{room_name}/meeting": {
+ post: {
+ req: V1RoomsCreateMeetingData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: GetMeeting;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
"/v1/transcripts": {
get: {
req: V1TranscriptsListData;
@@ -364,6 +531,21 @@ export type $OpenApiTs = {
};
};
};
+ "/v1/transcripts/meeting": {
+ post: {
+ req: V1TranscriptsCreateMeetingData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: GetTranscript;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
"/v1/transcripts/{transcript_id}": {
get: {
req: V1TranscriptGetData;
diff --git a/www/app/providers.tsx b/www/app/providers.tsx
index 3f06dd22..25c8e4c9 100644
--- a/www/app/providers.tsx
+++ b/www/app/providers.tsx
@@ -3,6 +3,12 @@
import { ChakraProvider } from "@chakra-ui/react";
import theme from "./styles/theme";
+import { WherebyProvider } from "@whereby.com/browser-sdk/react";
+
export function Providers({ children }: { children: React.ReactNode }) {
- return {children};
+ return (
+
+ {children}
+
+ );
}
diff --git a/www/config-template.ts b/www/config-template.ts
index 6f0cf194..e8d4c01c 100644
--- a/www/config-template.ts
+++ b/www/config-template.ts
@@ -4,6 +4,7 @@ export const localConfig = {
privacy: true,
browse: true,
sendToZulip: true,
+ rooms: true,
},
api_url: "http://127.0.0.1:1250",
websocket_url: "ws://127.0.0.1:1250",
diff --git a/www/middleware.ts b/www/middleware.ts
index 4bf13ed0..59416982 100644
--- a/www/middleware.ts
+++ b/www/middleware.ts
@@ -14,8 +14,9 @@ export async function middleware(request: NextRequest) {
) {
// Feature-flag protedted paths
if (
- !config.features.browse &&
- request.nextUrl.pathname.startsWith("/browse")
+ (!config.features.browse &&
+ request.nextUrl.pathname.startsWith("/browse")) ||
+ (!config.features.rooms && request.nextUrl.pathname.startsWith("/rooms"))
) {
return NextResponse.redirect(request.nextUrl.origin);
}
@@ -27,7 +28,8 @@ export async function middleware(request: NextRequest) {
if (
request.nextUrl.pathname == "/" ||
request.nextUrl.pathname.startsWith("/transcripts") ||
- request.nextUrl.pathname.startsWith("/browse")
+ request.nextUrl.pathname.startsWith("/browse") ||
+ request.nextUrl.pathname.startsWith("/rooms")
) {
if (!fiefResponse.headers.get("x-middleware-rewrite")) {
fiefResponse.headers.set(
diff --git a/www/package.json b/www/package.json
index f206bfbc..7d5afbb6 100644
--- a/www/package.json
+++ b/www/package.json
@@ -24,6 +24,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@sentry/nextjs": "^7.77.0",
"@vercel/edge-config": "^0.4.1",
+ "@whereby.com/browser-sdk": "^3.3.4",
"autoprefixer": "10.4.14",
"axios": "^1.6.2",
"chakra-react-select": "^4.7.6",
@@ -55,8 +56,8 @@
"author": "Andreas ",
"license": "All Rights Reserved",
"devDependencies": {
- "@types/react": "18.2.20",
"@hey-api/openapi-ts": "^0.48.0",
+ "@types/react": "18.2.20",
"prettier": "^3.0.0"
}
}
diff --git a/www/yarn.lock b/www/yarn.lock
index c7fafc37..09d98356 100644
--- a/www/yarn.lock
+++ b/www/yarn.lock
@@ -1064,6 +1064,21 @@
dependencies:
"@floating-ui/utils" "^0.1.3"
+"@floating-ui/core@^1.6.0":
+ version "1.6.4"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.4.tgz#0140cf5091c8dee602bff9da5ab330840ff91df6"
+ integrity sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==
+ dependencies:
+ "@floating-ui/utils" "^0.2.4"
+
+"@floating-ui/dom@^1.0.0":
+ version "1.6.7"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.7.tgz#85d22f731fcc5b209db504478fb1df5116a83015"
+ integrity sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==
+ dependencies:
+ "@floating-ui/core" "^1.6.0"
+ "@floating-ui/utils" "^0.2.4"
+
"@floating-ui/dom@^1.0.1":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa"
@@ -1072,11 +1087,23 @@
"@floating-ui/core" "^1.4.2"
"@floating-ui/utils" "^0.1.3"
+"@floating-ui/react-dom@^2.0.0":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.1.tgz#cca58b6b04fc92b4c39288252e285e0422291fb0"
+ integrity sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==
+ dependencies:
+ "@floating-ui/dom" "^1.0.0"
+
"@floating-ui/utils@^0.1.3":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
+"@floating-ui/utils@^0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.4.tgz#1d459cee5031893a08a0e064c406ad2130cced7c"
+ integrity sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==
+
"@fortawesome/fontawesome-common-types@6.4.0":
version "6.4.0"
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz"
@@ -1277,6 +1304,180 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
+"@radix-ui/primitive@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2"
+ integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==
+
+"@radix-ui/react-arrow@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a"
+ integrity sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==
+ dependencies:
+ "@radix-ui/react-primitive" "2.0.0"
+
+"@radix-ui/react-compose-refs@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
+ integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
+
+"@radix-ui/react-context@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
+ integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
+
+"@radix-ui/react-dismissable-layer@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz#2cd0a49a732372513733754e6032d3fb7988834e"
+ integrity sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-escape-keydown" "1.1.0"
+
+"@radix-ui/react-focus-guards@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13"
+ integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==
+
+"@radix-ui/react-focus-scope@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2"
+ integrity sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+
+"@radix-ui/react-id@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed"
+ integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==
+ dependencies:
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+
+"@radix-ui/react-popover@^1.0.7":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.1.tgz#604b783cdb3494ed4f16a58c17f0e81e61ab7775"
+ integrity sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==
+ dependencies:
+ "@radix-ui/primitive" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-dismissable-layer" "1.1.0"
+ "@radix-ui/react-focus-guards" "1.1.0"
+ "@radix-ui/react-focus-scope" "1.1.0"
+ "@radix-ui/react-id" "1.1.0"
+ "@radix-ui/react-popper" "1.2.0"
+ "@radix-ui/react-portal" "1.1.1"
+ "@radix-ui/react-presence" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-slot" "1.1.0"
+ "@radix-ui/react-use-controllable-state" "1.1.0"
+ aria-hidden "^1.1.1"
+ react-remove-scroll "2.5.7"
+
+"@radix-ui/react-popper@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz#a3e500193d144fe2d8f5d5e60e393d64111f2a7a"
+ integrity sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==
+ dependencies:
+ "@floating-ui/react-dom" "^2.0.0"
+ "@radix-ui/react-arrow" "1.1.0"
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-context" "1.1.0"
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+ "@radix-ui/react-use-rect" "1.1.0"
+ "@radix-ui/react-use-size" "1.1.0"
+ "@radix-ui/rect" "1.1.0"
+
+"@radix-ui/react-portal@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.1.tgz#1957f1eb2e1aedfb4a5475bd6867d67b50b1d15f"
+ integrity sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==
+ dependencies:
+ "@radix-ui/react-primitive" "2.0.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+
+"@radix-ui/react-presence@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.0.tgz#227d84d20ca6bfe7da97104b1a8b48a833bfb478"
+ integrity sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.0"
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+
+"@radix-ui/react-primitive@2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884"
+ integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==
+ dependencies:
+ "@radix-ui/react-slot" "1.1.0"
+
+"@radix-ui/react-slot@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84"
+ integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
+ dependencies:
+ "@radix-ui/react-compose-refs" "1.1.0"
+
+"@radix-ui/react-use-callback-ref@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1"
+ integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==
+
+"@radix-ui/react-use-controllable-state@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0"
+ integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==
+ dependencies:
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+
+"@radix-ui/react-use-escape-keydown@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754"
+ integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==
+ dependencies:
+ "@radix-ui/react-use-callback-ref" "1.1.0"
+
+"@radix-ui/react-use-layout-effect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27"
+ integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==
+
+"@radix-ui/react-use-rect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88"
+ integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==
+ dependencies:
+ "@radix-ui/rect" "1.1.0"
+
+"@radix-ui/react-use-size@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b"
+ integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==
+ dependencies:
+ "@radix-ui/react-use-layout-effect" "1.1.0"
+
+"@radix-ui/rect@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438"
+ integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==
+
+"@reduxjs/toolkit@^2.2.3":
+ version "2.2.6"
+ resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.2.6.tgz#4a8356dad9d0c1ab255607a555d492168e0e3bc1"
+ integrity sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==
+ dependencies:
+ immer "^10.0.3"
+ redux "^5.0.1"
+ redux-thunk "^3.1.0"
+ reselect "^5.1.0"
+
"@rollup/plugin-commonjs@24.0.0":
version "24.0.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz#fb7cf4a6029f07ec42b25daa535c75b05a43f75c"
@@ -1437,6 +1638,11 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
+"@socket.io/component-emitter@~3.1.0":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2"
+ integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==
+
"@swc/helpers@0.5.2":
version "0.5.2"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d"
@@ -1451,6 +1657,13 @@
dependencies:
"@types/ms" "*"
+"@types/debug@^4.1.12":
+ version "4.1.12"
+ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917"
+ integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
+ dependencies:
+ "@types/ms" "*"
+
"@types/estree@*", "@types/estree@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
@@ -1521,6 +1734,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.5.tgz#9dc0a5cb1ccce4f7a731660935ab70b9c00a5d69"
integrity sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==
+"@types/npm-events-package@npm:@types/events@^3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529"
+ integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==
+
"@types/parse-json@^4.0.0":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239"
@@ -1566,6 +1784,11 @@
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.0.tgz#988ae8af1e5239e89f9fbb1ade4c935f4eeedf9a"
integrity sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==
+"@types/use-sync-external-store@^0.0.3":
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
+ integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
@@ -1624,11 +1847,41 @@
"@typescript-eslint/types" "6.16.0"
eslint-visitor-keys "^3.4.1"
+"@ungap/create-content@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@ungap/create-content/-/create-content-0.2.0.tgz#f123a7c79f391d853600abf6dba44034a8ced58e"
+ integrity sha512-CvmX0Mr5PfFARDBbSef0B+SAqSeMKaHOG/twJi9nbPtp/MiNPgyBLqZndiyO3RXQ0RXy6TqwarvB6KWzTmc4MQ==
+
+"@ungap/event@^0.2.2":
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/@ungap/event/-/event-0.2.2.tgz#69d76a1e2c6c1cd4ec63455ee3f91082eb0c6f5e"
+ integrity sha512-31PwUE7asaFeXdRatnlsNYyfmO8xSEhsRAP+v7lm77hnn/oOjlpt7pJgc7C76LGlZjiEH9nGSx9vTc/5MZ6W7A==
+
+"@ungap/import-node@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@ungap/import-node/-/import-node-0.2.0.tgz#5fd4b753a0ae52f6478f16ab669ddae461009135"
+ integrity sha512-VuWVBAMRjoOc63n8Cc19brS7KlhYJ+57790LF+lVw60nMRemCrz1T6HnoNx74IEW3FS+TM+vveJ70C6NyTKODQ==
+
"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+"@ungap/trim@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@ungap/trim/-/trim-0.2.0.tgz#71cc7998ef41a1b08ed94705c2f81f70c3cd2e10"
+ integrity sha512-CfsUxeZ2R/O3EGCOe+IkAU32yHOdO+mCRmtavSIQ4HZN3Jiq/ynGzq8/asyamd28U26UJmpSV/TC7+p7qELKrg==
+
+"@ungap/weakmap@^0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@ungap/weakmap/-/weakmap-0.2.1.tgz#b690f139bb7aa97255182faff86777ca855c115d"
+ integrity sha512-GmVAWB+JuFKqSbzlofYK4qxk955gEv4Kd9/aj2hLOxneXMAm/J7OXcl5DlElS9tmkqwCcxGysSZGOrjzNvmjFQ==
+
+"@ungap/weakset@^0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@ungap/weakset/-/weakset-0.2.1.tgz#4c7632241d1783b37ab07ac0866e8a9ee726a964"
+ integrity sha512-0tu3cD+yO4d5lGMC1DTAhIi29RZnP+tSfWc+T6WMvrMeNExO76iZ5eCciuiyUUIyBxqGDF4iNaeIXpYyJP+vcA==
+
"@vercel/edge-config-fs@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@vercel/edge-config-fs/-/edge-config-fs-0.1.0.tgz#cda8327f611418c1d1cbb28c6bed02d782be928b"
@@ -1641,6 +1894,48 @@
dependencies:
"@vercel/edge-config-fs" "0.1.0"
+"@whereby.com/browser-sdk@^3.3.4":
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/@whereby.com/browser-sdk/-/browser-sdk-3.3.4.tgz#25b170bc2c6ea577ae4b2d7af54d70d1efa7a21d"
+ integrity sha512-sJfgXosf73E/TsiDc1bcnfom+po+74JD8hNcA0s46jG9jZ7pNBRq8oWrV4Qd/SEUtcWGoEuqgYjL0SImln824g==
+ dependencies:
+ "@radix-ui/react-popover" "^1.0.7"
+ "@reduxjs/toolkit" "^2.2.3"
+ "@whereby.com/core" "0.19.5"
+ clsx "^2.1.1"
+ heresy "^1.0.4"
+ react-redux "^9.1.1"
+ runes "^0.4.3"
+
+"@whereby.com/core@0.19.5":
+ version "0.19.5"
+ resolved "https://registry.yarnpkg.com/@whereby.com/core/-/core-0.19.5.tgz#09b36962311cb717be1fcc3c8af6d124670409bd"
+ integrity sha512-vAsK8e/EpmS2dwWtknnZmfMgmbHLFOvuTOCOmiHwsz5yTImR1L9vsDLVkoF+ixpeY6v5fYBpU+eft5XB1E3qrw==
+ dependencies:
+ "@reduxjs/toolkit" "^2.2.3"
+ "@whereby.com/media" "1.6.5"
+ axios "^1.2.3"
+ btoa "^1.2.1"
+ events "^3.3.0"
+
+"@whereby.com/media@1.6.5":
+ version "1.6.5"
+ resolved "https://registry.yarnpkg.com/@whereby.com/media/-/media-1.6.5.tgz#cfc3ac36b126523fe9d935532ffc1660451b85d5"
+ integrity sha512-q/+9+oiMU6WUNCDZjqPcYoVOA1fIeH2sqdQaW9qHCzf/hgmDuoaw95u9eeTiwoDHngW/LRcPLY9Q4tsEgm2jNg==
+ dependencies:
+ check-ip "^1.1.1"
+ events "^3.3.0"
+ ip-address "^9.0.5"
+ mediasoup-client "3.7.12"
+ rtcstats "github:whereby/rtcstats#5.4.0"
+ sdp "^3.2.0"
+ sdp-transform "^2.14.2"
+ socket.io-client "4.7.2"
+ typescript "^5.3.3"
+ uuid "^9.0.1"
+ uuid-validate "^0.0.3"
+ webrtc-adapter "^8.2.3"
+
"@zag-js/dom-query@0.16.0":
version "0.16.0"
resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-0.16.0.tgz#bca46bcd78f78c900064478646d95f9781ed098e"
@@ -1732,6 +2027,13 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+aria-hidden@^1.1.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522"
+ integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==
+ dependencies:
+ tslib "^2.0.0"
+
aria-hidden@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
@@ -1847,6 +2149,14 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+augmentor@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/augmentor/-/augmentor-2.2.0.tgz#e639e0696d88a74caed840b5ee08671bab225a27"
+ integrity sha512-BEQAG1w74b794ec4FpWpu9AZZUZngKy5zGgb8OIhU2hcVhm8F5Y/VS0/3EAA+ExgtJDCTR+L2rvnjtIt2oo1xQ==
+ dependencies:
+ reraf "^1.1.1"
+ umap "^1.0.2"
+
autoprefixer@10.4.14:
version "10.4.14"
resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz"
@@ -1864,11 +2174,27 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
+awaitqueue@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/awaitqueue/-/awaitqueue-3.0.2.tgz#a37a212b137b784dc6bd701d1ecfa4a07ec89625"
+ integrity sha512-AVAtRwmf0DNSesMdyanFKKejTrOnjdKtz5LIDQFu2OTUgXvB/CRTYMrkPAF/2GCF9XBtYVxSwxDORlD41S+RyQ==
+ dependencies:
+ debug "^4.3.4"
+
axe-core@=4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
+axios@^1.2.3:
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621"
+ integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.0"
+ proxy-from-env "^1.1.0"
+
axios@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
@@ -1946,6 +2272,11 @@ browserslist@^4.21.5:
node-releases "^2.0.12"
update-browserslist-db "^1.0.11"
+btoa@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73"
+ integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==
+
buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
@@ -2053,6 +2384,13 @@ character-entities@^2.0.0:
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
+check-ip@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/check-ip/-/check-ip-1.1.1.tgz#77270efaa69a7853fec3709f143036a8e20e7ea6"
+ integrity sha512-LuLBA6r4aS/4B7pvOqmT4Bi+GKnNNC/V18K0zDTRFjAxNeUzGsr0wmsOfFhFH7fGjwdx6GX6wyIQBkUhFox2Pw==
+ dependencies:
+ ip-range-check "^0.0.2"
+
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
@@ -2110,6 +2448,11 @@ client-only@0.0.1:
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+clsx@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+ integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -2269,6 +2612,13 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
+debug@^4.3.5, debug@~4.3.1, debug@~4.3.2:
+ version "4.3.5"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
+ integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
+ dependencies:
+ ms "2.1.2"
+
decode-named-character-reference@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e"
@@ -2378,6 +2728,31 @@ dom-helpers@^5.0.1:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"
+domconstants@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/domconstants/-/domconstants-0.1.2.tgz#cfb75c667e32ce69f9044b72cc31db2e06835815"
+ integrity sha512-sPOoOckTxtwy5t8PFf6zl11gOEhOpl1k0ZCc/NfCNmHoMw8n9HnCQCzxWKX9gdBp+qM+2DTFkst++Yw6C41izQ==
+
+domsanitizer@^0.2.2, domsanitizer@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/domsanitizer/-/domsanitizer-0.2.3.tgz#5f7a045ba80227719388358c0f6b6549760d05ad"
+ integrity sha512-qglHc+5k5C2+WSEzck0WGbpa2exCKZuZKb0n4RU2Wuy7BPkpmf67mHD1BJWGxs0iJYl709f2YVeJMh06c1ILlA==
+ dependencies:
+ domconstants "^0.1.2"
+
+domtagger@^0.7.1:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/domtagger/-/domtagger-0.7.2.tgz#48c91ec7af7a444239bf6922d47ffd52e49d2cb1"
+ integrity sha512-h7g5eduvnLwowJJPkcB5lNzo8vd/Hx4e3I4IOtLpX0qB2wBiuryGLNa61MeFre4b6gMaQIhegMIZ2I8rQCAJwQ==
+ dependencies:
+ "@ungap/create-content" "^0.2.0"
+ "@ungap/import-node" "^0.2.0"
+ "@ungap/trim" "^0.2.0"
+ "@ungap/weakmap" "^0.2.1"
+ domconstants "^0.1.2"
+ domsanitizer "^0.2.2"
+ umap "^1.0.2"
+
dotenv@^16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
@@ -2400,6 +2775,22 @@ encoding@^0.1.13:
dependencies:
iconv-lite "^0.6.2"
+engine.io-client@~6.5.2:
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.4.tgz#b8bc71ed3f25d0d51d587729262486b4b33bd0d0"
+ integrity sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.1"
+ engine.io-parser "~5.2.1"
+ ws "~8.17.1"
+ xmlhttprequest-ssl "~2.0.0"
+
+engine.io-parser@~5.2.1:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f"
+ integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==
+
enhanced-resolve@^5.12.0:
version "5.15.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35"
@@ -2736,6 +3127,16 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+event-target-shim@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-6.0.2.tgz#ea5348c3618ee8b62ff1d344f01908ee2b8a2b71"
+ integrity sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==
+
+events@^3.3.0, "npm-events-package@npm:events@^3.3.0":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
execa@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
@@ -2756,6 +3157,14 @@ extend@^3.0.0:
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+fake-mediastreamtrack@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fake-mediastreamtrack/-/fake-mediastreamtrack-1.2.0.tgz#11e6e0c50d36d3bc988461c034beb81debee548b"
+ integrity sha512-AxHtlEmka1sqNoe3Ej1H1hJc9gjjO/6vCbCPm4D4QeEXvzhjYumA+iZ7wOi2WrmkAhGElHhBgWoNgJhFccectA==
+ dependencies:
+ event-target-shim "^6.0.2"
+ uuid "^9.0.0"
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -2858,6 +3267,11 @@ follow-redirects@^1.15.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+follow-redirects@^1.15.6:
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
+ integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
+
fontawesome@^5.6.3:
version "5.6.3"
resolved "https://registry.npmjs.org/fontawesome/-/fontawesome-5.6.3.tgz"
@@ -3125,6 +3539,14 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+h264-profile-level-id@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/h264-profile-level-id/-/h264-profile-level-id-2.0.0.tgz#b7ea45badbac8f5dbb9583d34b06db09764f2535"
+ integrity sha512-X4CLryVbVA0CtjTExS4G5U1gb2Z4wa32AF8ukVmFuLdw2JRq2aHisor7SY5SYTUUrUSqq0KdPIO18sql6IWIQw==
+ dependencies:
+ "@types/debug" "^4.1.12"
+ debug "^4.3.4"
+
handlebars@4.7.8:
version "4.7.8"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9"
@@ -3212,6 +3634,18 @@ hast-util-whitespace@^3.0.0:
dependencies:
"@types/hast" "^3.0.0"
+heresy@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/heresy/-/heresy-1.0.4.tgz#45bb255379f9e9b32d32dfb0e693783f9d707565"
+ integrity sha512-2oLrI6lv2RXEXZPsNOTPffFdGPjpwCHrt1BZYLOBnEc7pv8fUJWdeVbLVcq9oLBpOYSy3dbP71jTRRCN5fGcuA==
+ dependencies:
+ "@ungap/event" "^0.2.2"
+ "@ungap/weakmap" "^0.2.1"
+ "@ungap/weakset" "^0.2.1"
+ augmentor "^2.2.0"
+ lighterhtml "^4.1.2"
+ uhyphen "^0.1.0"
+
hexoid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
@@ -3242,6 +3676,11 @@ human-signals@^5.0.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
+hyperhtml-style@^0.1.2:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/hyperhtml-style/-/hyperhtml-style-0.1.3.tgz#ba7f704741c6d2f84c4d67e6544c01ebbd3f21d3"
+ integrity sha512-IvLy8MzHTSJ0fDpSzrb8rcdnla6yROEmNBSxInEMyIFu2DQkbmpadTf6B4fHvnytN6iHL2gGwpe5/jHL3wMi+A==
+
iconv-lite@^0.6.2:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
@@ -3264,6 +3703,11 @@ immediate@~3.0.5:
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+immer@^10.0.3:
+ version "10.1.1"
+ resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc"
+ integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==
+
immutable@^4.0.0:
version "4.3.1"
resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz"
@@ -3316,6 +3760,26 @@ invariant@^2.2.4:
dependencies:
loose-envify "^1.0.0"
+ip-address@^9.0.5:
+ version "9.0.5"
+ resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
+ integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
+ dependencies:
+ jsbn "1.1.0"
+ sprintf-js "^1.1.3"
+
+ip-range-check@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/ip-range-check/-/ip-range-check-0.0.2.tgz#605c859687aa4f18463918d46190d8b3699a293c"
+ integrity sha512-sHbyog8viObPK2vZFNYpBM/d2mqs51uuxOhB+0EIMSWmmrflAWne7CeXOWunb5R6bWQVOijbLx7bEY0sE05bug==
+ dependencies:
+ ipaddr.js "^1.0.1"
+
+ipaddr.js@^1.0.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+ integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
@@ -3579,6 +4043,11 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
+jsbn@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
+ integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
+
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
@@ -3650,6 +4119,21 @@ lie@3.1.1:
dependencies:
immediate "~3.0.5"
+lighterhtml@^4.1.2:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lighterhtml/-/lighterhtml-4.2.0.tgz#2f7fc9071bdd82abbb8b101e187f904fcd364881"
+ integrity sha512-HAb+Ri17iT+vYmFtarlt45O63BltoJY/ltDZGhnf2A1s4kno2j6su5KiZAgYD4/5AjODYQqflSy9KJZFwL5VwQ==
+ dependencies:
+ "@ungap/create-content" "^0.2.0"
+ "@ungap/weakmap" "^0.2.1"
+ domsanitizer "^0.2.3"
+ domtagger "^0.7.1"
+ hyperhtml-style "^0.1.2"
+ udomdiff "^1.1.0"
+ uhandlers "^0.4.2"
+ umap "^1.0.2"
+ uwire "^1.1.0"
+
lilconfig@^2.0.5, lilconfig@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz"
@@ -3744,6 +4228,23 @@ mdast-util-to-string@^4.0.0:
dependencies:
"@types/mdast" "^4.0.0"
+mediasoup-client@3.7.12:
+ version "3.7.12"
+ resolved "https://registry.yarnpkg.com/mediasoup-client/-/mediasoup-client-3.7.12.tgz#7ea1bb5c644a7393698de62c23294e51a652cdfc"
+ integrity sha512-AuQ3tgKGGQf7AmzzQShHNdYKewhLDof4MDC3JTNLFKvqEGzAZDBGgRs+m1zRfqgsSlGcd9xaf79Z7hs+Wd/A3Q==
+ dependencies:
+ "@types/debug" "^4.1.12"
+ "@types/npm-events-package" "npm:@types/events@^3.0.3"
+ awaitqueue "^3.0.2"
+ debug "^4.3.5"
+ fake-mediastreamtrack "^1.2.0"
+ h264-profile-level-id "^2.0.0"
+ npm-events-package "npm:events@^3.3.0"
+ queue-microtask "^1.2.3"
+ sdp-transform "^2.14.2"
+ supports-color "^9.4.0"
+ ua-parser-js "^1.0.38"
+
memoize-one@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
@@ -4583,6 +5084,14 @@ react-qr-code@^2.0.12:
prop-types "^15.8.1"
qr.js "0.0.0"
+react-redux@^9.1.1:
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b"
+ integrity sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==
+ dependencies:
+ "@types/use-sync-external-store" "^0.0.3"
+ use-sync-external-store "^1.0.0"
+
react-remove-scroll-bar@^2.3.4:
version "2.3.4"
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
@@ -4591,7 +5100,7 @@ react-remove-scroll-bar@^2.3.4:
react-style-singleton "^2.2.1"
tslib "^2.0.0"
-react-remove-scroll@^2.5.6:
+react-remove-scroll@2.5.7, react-remove-scroll@^2.5.6:
version "2.5.7"
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb"
integrity sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==
@@ -4671,6 +5180,16 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+redux-thunk@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
+ integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
+
+redux@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b"
+ integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==
+
reflect.getprototypeof@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3"
@@ -4718,6 +5237,16 @@ remark-rehype@^11.0.0:
unified "^11.0.0"
vfile "^6.0.0"
+reraf@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/reraf/-/reraf-1.1.1.tgz#c75584660edd9b6ee3c31eeb694d938f529b7042"
+ integrity sha512-uwOsqdTxJAQCxqvJF4Kiz4orwO9B8OBJkGeW7/NWNirapPutgig/3xU3emruvfwUbb+t51V0zHtNw6d83RDlJQ==
+
+reselect@^5.1.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e"
+ integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -4765,6 +5294,10 @@ rollup@2.78.0:
optionalDependencies:
fsevents "~2.3.2"
+"rtcstats@github:whereby/rtcstats#5.4.0":
+ version "5.4.0"
+ resolved "https://codeload.github.com/whereby/rtcstats/tar.gz/6f6623b4b53e9f6b41f530339793de227f34ea51"
+
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
@@ -4772,6 +5305,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+runes@^0.4.3:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/runes/-/runes-0.4.3.tgz#32f7738844bc767b65cc68171528e3373c7bb355"
+ integrity sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg==
+
safe-array-concat@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c"
@@ -4817,6 +5355,16 @@ scheduler@^0.23.0:
dependencies:
loose-envify "^1.1.0"
+sdp-transform@^2.14.2:
+ version "2.14.2"
+ resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.2.tgz#d2cee6a1f7abe44e6332ac6cbb94e8600f32d813"
+ integrity sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==
+
+sdp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/sdp/-/sdp-3.2.0.tgz#8961420552b36663b4d13ddba6f478d1461896a5"
+ integrity sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==
+
semver@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
@@ -4892,6 +5440,24 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+socket.io-client@4.7.2:
+ version "4.7.2"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.2.tgz#f2f13f68058bd4e40f94f2a1541f275157ff2c08"
+ integrity sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.2"
+ engine.io-client "~6.5.2"
+ socket.io-parser "~4.2.4"
+
+socket.io-parser@~4.2.4:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
+ integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
+ dependencies:
+ "@socket.io/component-emitter" "~3.1.0"
+ debug "~4.3.1"
+
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
@@ -4912,6 +5478,11 @@ space-separated-tokens@^2.0.0:
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f"
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
+sprintf-js@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
+ integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
+
stacktrace-parser@^0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a"
@@ -5277,6 +5848,26 @@ typescript@^5.1.6:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
+typescript@^5.3.3:
+ version "5.5.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa"
+ integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==
+
+ua-parser-js@^1.0.38:
+ version "1.0.38"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.38.tgz#66bb0c4c0e322fe48edfe6d446df6042e62f25e2"
+ integrity sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==
+
+uarray@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/uarray/-/uarray-1.0.0.tgz#e89bf0ea2129596f4bf653b66e53246ed6562b6c"
+ integrity sha512-LHmiAd5QuAv7pU2vbh+Zq9YOnqVK0H764p2Ozinpfy9ka58OID4IsGLiXsitqH7n0NAIDxvax1A/kDXpii/Ckg==
+
+udomdiff@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/udomdiff/-/udomdiff-1.1.0.tgz#1d0c4741b93a2ffd10728ca4cea15b277cc3082f"
+ integrity sha512-aqjTs5x/wsShZBkVagdafJkP8S3UMGhkHKszsu1cszjjZ7iOp86+Qb3QOFYh01oWjPMy5ZTuxD6hw5uTKxd+VA==
+
ufo@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344"
@@ -5287,6 +5878,23 @@ uglify-js@^3.1.4:
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"
integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==
+uhandlers@^0.4.2:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/uhandlers/-/uhandlers-0.4.2.tgz#3cda71eaf1bec63b079ddacc897a68a3b7f614b8"
+ integrity sha512-4M3yo0saEReMHiUz3yTDX/e9Z1Z+X8fVvDEywdjvWHPKqzpI6xEPJ21llrBf6Tvf7E5xaPCf9l5JXaYZRU7QRA==
+ dependencies:
+ uarray "^1.0.0"
+
+uhyphen@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/uhyphen/-/uhyphen-0.1.0.tgz#3cc22afa790daa802b9f6789f3583108d5b4a08c"
+ integrity sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==
+
+umap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/umap/-/umap-1.0.2.tgz#2d7d4b145ad8c9f957921460798f3e221347c981"
+ integrity sha512-bW127HgG4H4VAD6qlqO5vCC+7bnlYvZ6A6BdwyGblkWvlEG7VYpj1bcpf3iJpvyKmkPZWDIeZDmoULz67ec7NA==
+
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@@ -5383,11 +5991,33 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0"
tslib "^2.0.0"
+use-sync-external-store@^1.0.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
+ integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
+
util-deprecate@^1.0.1, util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+uuid-validate@^0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/uuid-validate/-/uuid-validate-0.0.3.tgz#e30617f75dc742a0e4f95012a11540faf9d39ab4"
+ integrity sha512-Fykw5U4eZESbq739BeLvEBFRuJODfrlmjx5eJux7W817LjRaq4b7/i4t2zxQmhcX+fAj4nMfRdTzO4tmwLKn0w==
+
+uuid@^9.0.0, uuid@^9.0.1:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
+uwire@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/uwire/-/uwire-1.1.0.tgz#ae72a770a140311a0d640aebb21131a7934988ca"
+ integrity sha512-XJPmJnySabt8D0/wnfFFywgUOBnXszDEW32nEVIfOx1n6gLTZSp+X+70+blSnHKNiIUVSFPmmRuxOal0I/aB5g==
+ dependencies:
+ uarray "^1.0.0"
+
vfile-message@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181"
@@ -5428,6 +6058,13 @@ webidl-conversions@^3.0.0:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+webrtc-adapter@^8.2.3:
+ version "8.2.3"
+ resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz#85e5e52ea68e808be8d6db85e338aa5c95e80022"
+ integrity sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==
+ dependencies:
+ sdp "^3.2.0"
+
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@@ -5503,6 +6140,16 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+ws@~8.17.1:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
+ integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
+
+xmlhttprequest-ssl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
+ integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
+
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"