From dc82f8bb3bdf3ab3d4088e592a30fd63907319e1 Mon Sep 17 00:00:00 2001
From: Sergey Mankovsky
Date: Thu, 4 Sep 2025 13:00:14 +0200
Subject: [PATCH 01/20] fix: source kind for file processing (#601)
---
server/reflector/views/transcripts.py | 3 +-
server/reflector/views/transcripts_process.py | 2 +-
www/app/(app)/transcripts/new/page.tsx | 13 +++++-
www/app/api/schemas.gen.ts | 43 ++++++++++++++++---
www/app/api/types.gen.ts | 10 ++---
5 files changed, 55 insertions(+), 16 deletions(-)
diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py
index b64ecf11..3f32a9bd 100644
--- a/server/reflector/views/transcripts.py
+++ b/server/reflector/views/transcripts.py
@@ -96,6 +96,7 @@ class CreateTranscript(BaseModel):
name: str
source_language: str = Field("en")
target_language: str = Field("en")
+ source_kind: SourceKind | None = None
class UpdateTranscript(BaseModel):
@@ -213,7 +214,7 @@ async def transcripts_create(
user_id = user["sub"] if user else None
return await transcripts_controller.add(
info.name,
- source_kind=SourceKind.LIVE,
+ source_kind=info.source_kind or SourceKind.LIVE,
source_language=info.source_language,
target_language=info.target_language,
user_id=user_id,
diff --git a/server/reflector/views/transcripts_process.py b/server/reflector/views/transcripts_process.py
index 0200e7f8..f9295765 100644
--- a/server/reflector/views/transcripts_process.py
+++ b/server/reflector/views/transcripts_process.py
@@ -34,7 +34,7 @@ async def transcript_process(
)
if task_is_scheduled_or_active(
- "reflector.pipelines.main_live_pipeline.task_pipeline_process",
+ "reflector.pipelines.main_file_pipeline.task_pipeline_file_process",
transcript_id=transcript_id,
):
return ProcessStatus(status="already running")
diff --git a/www/app/(app)/transcripts/new/page.tsx b/www/app/(app)/transcripts/new/page.tsx
index 698ac47b..2670fd39 100644
--- a/www/app/(app)/transcripts/new/page.tsx
+++ b/www/app/(app)/transcripts/new/page.tsx
@@ -7,6 +7,7 @@ import About from "../../../(aboutAndPrivacy)/about";
import Privacy from "../../../(aboutAndPrivacy)/privacy";
import { useRouter } from "next/navigation";
import useCreateTranscript from "../createTranscript";
+import { SourceKind } from "../../../api";
import SelectSearch from "react-select-search";
import { supportedLanguages } from "../../../supportedLanguages";
import useSessionStatus from "../../../lib/useSessionStatus";
@@ -61,13 +62,21 @@ const TranscriptCreate = () => {
const send = () => {
if (loadingRecord || createTranscript.loading || permissionDenied) return;
setLoadingRecord(true);
- createTranscript.create({ name, target_language: getTargetLanguage() });
+ createTranscript.create({
+ name,
+ target_language: getTargetLanguage(),
+ source_kind: "live" as SourceKind,
+ });
};
const uploadFile = () => {
if (loadingUpload || createTranscript.loading || permissionDenied) return;
setLoadingUpload(true);
- createTranscript.create({ name, target_language: getTargetLanguage() });
+ createTranscript.create({
+ name,
+ target_language: getTargetLanguage(),
+ source_kind: "file" as SourceKind,
+ });
};
useEffect(() => {
diff --git a/www/app/api/schemas.gen.ts b/www/app/api/schemas.gen.ts
index 919040a2..03091a5f 100644
--- a/www/app/api/schemas.gen.ts
+++ b/www/app/api/schemas.gen.ts
@@ -133,6 +133,16 @@ export const $CreateTranscript = {
title: "Target Language",
default: "en",
},
+ source_kind: {
+ anyOf: [
+ {
+ $ref: "#/components/schemas/SourceKind",
+ },
+ {
+ type: "null",
+ },
+ ],
+ },
},
type: "object",
required: ["name"],
@@ -1031,11 +1041,25 @@ export const $RoomDetails = {
title: "Is Shared",
},
webhook_url: {
- type: "string",
+ anyOf: [
+ {
+ type: "string",
+ },
+ {
+ type: "null",
+ },
+ ],
title: "Webhook Url",
},
webhook_secret: {
- type: "string",
+ anyOf: [
+ {
+ type: "string",
+ },
+ {
+ type: "null",
+ },
+ ],
title: "Webhook Secret",
},
},
@@ -1091,10 +1115,17 @@ export const $SearchResponse = {
description: "Total number of search results",
},
query: {
- type: "string",
- minLength: 0,
+ anyOf: [
+ {
+ type: "string",
+ minLength: 1,
+ description: "Search query text",
+ },
+ {
+ type: "null",
+ },
+ ],
title: "Query",
- description: "Search query text",
},
limit: {
type: "integer",
@@ -1111,7 +1142,7 @@ export const $SearchResponse = {
},
},
type: "object",
- required: ["results", "total", "query", "limit", "offset"],
+ required: ["results", "total", "limit", "offset"],
title: "SearchResponse",
} as const;
diff --git a/www/app/api/types.gen.ts b/www/app/api/types.gen.ts
index e2e7a020..d724fc98 100644
--- a/www/app/api/types.gen.ts
+++ b/www/app/api/types.gen.ts
@@ -32,6 +32,7 @@ export type CreateTranscript = {
name: string;
source_language?: string;
target_language?: string;
+ source_kind?: SourceKind | null;
};
export type DeletionStatus = {
@@ -191,8 +192,8 @@ export type RoomDetails = {
recording_type: string;
recording_trigger: string;
is_shared: boolean;
- webhook_url: string;
- webhook_secret: string;
+ webhook_url: string | null;
+ webhook_secret: string | null;
};
export type RtcOffer = {
@@ -206,10 +207,7 @@ export type SearchResponse = {
* Total number of search results
*/
total: number;
- /**
- * Search query text
- */
- query: string;
+ query?: string | null;
/**
* Results per page
*/
From 0663700a615a4af69a03c96c410f049e23ec9443 Mon Sep 17 00:00:00 2001
From: Sergey Mankovsky
Date: Fri, 5 Sep 2025 10:52:14 +0200
Subject: [PATCH 02/20] fix: align whisper transcriber api with parakeet (#602)
* Documents transcriber api
* Update whisper transcriber api to match parakeet
* Update api transcription spec
* Return 400 for unsupported file type
* Add params to api spec
* Update whisper transcriber implementation to match parakeet
---
server/docs/gpu/api-transcription.md | 194 ++++++
.../reflector_transcriber.py | 569 ++++++++++++++++--
server/tests/test_gpu_modal_transcript.py | 3 +
3 files changed, 705 insertions(+), 61 deletions(-)
create mode 100644 server/docs/gpu/api-transcription.md
diff --git a/server/docs/gpu/api-transcription.md b/server/docs/gpu/api-transcription.md
new file mode 100644
index 00000000..7a15d793
--- /dev/null
+++ b/server/docs/gpu/api-transcription.md
@@ -0,0 +1,194 @@
+## Reflector GPU Transcription API (Specification)
+
+This document defines the Reflector GPU transcription API that all implementations must adhere to. Current implementations include NVIDIA Parakeet (NeMo) and Whisper (faster-whisper), both deployed on Modal.com. The API surface and response shapes are OpenAI/Whisper-compatible, so clients can switch implementations by changing only the base URL.
+
+### Base URL and Authentication
+
+- Example base URLs (Modal web endpoints):
+
+ - Parakeet: `https://--reflector-transcriber-parakeet-web.modal.run`
+ - Whisper: `https://--reflector-transcriber-web.modal.run`
+
+- All endpoints are served under `/v1` and require a Bearer token:
+
+```
+Authorization: Bearer
+```
+
+Note: To switch implementations, deploy the desired variant and point `TRANSCRIPT_URL` to its base URL. The API is identical.
+
+### Supported file types
+
+`mp3, mp4, mpeg, mpga, m4a, wav, webm`
+
+### Models and languages
+
+- Parakeet (NVIDIA NeMo): default `nvidia/parakeet-tdt-0.6b-v2`
+ - Language support: only `en`. Other languages return HTTP 400.
+- Whisper (faster-whisper): default `large-v2` (or deployment-specific)
+ - Language support: multilingual (per Whisper model capabilities).
+
+Note: The `model` parameter is accepted by all implementations for interface parity. Some backends may treat it as informational.
+
+### Endpoints
+
+#### POST /v1/audio/transcriptions
+
+Transcribe one or more uploaded audio files.
+
+Request: multipart/form-data
+
+- `file` (File) — optional. Single file to transcribe.
+- `files` (File[]) — optional. One or more files to transcribe.
+- `model` (string) — optional. Defaults to the implementation-specific model (see above).
+- `language` (string) — optional, defaults to `en`.
+ - Parakeet: only `en` is accepted; other values return HTTP 400
+ - Whisper: model-dependent; typically multilingual
+- `batch` (boolean) — optional, defaults to `false`.
+
+Notes:
+
+- Provide either `file` or `files`, not both. If neither is provided, HTTP 400.
+- `batch` requires `files`; using `batch=true` without `files` returns HTTP 400.
+- Response shape for multiple files is the same regardless of `batch`.
+- Files sent to this endpoint are processed in a single pass (no VAD/chunking). This is intended for short clips (roughly ≤ 30s; depends on GPU memory/model). For longer audio, prefer `/v1/audio/transcriptions-from-url` which supports VAD-based chunking.
+
+Responses
+
+Single file response:
+
+```json
+{
+ "text": "transcribed text",
+ "words": [
+ { "word": "hello", "start": 0.0, "end": 0.5 },
+ { "word": "world", "start": 0.5, "end": 1.0 }
+ ],
+ "filename": "audio.mp3"
+}
+```
+
+Multiple files response:
+
+```json
+{
+ "results": [
+ {"filename": "a1.mp3", "text": "...", "words": [...]},
+ {"filename": "a2.mp3", "text": "...", "words": [...]}]
+}
+```
+
+Notes:
+
+- Word objects always include keys: `word`, `start`, `end`.
+- Some implementations may include a trailing space in `word` to match Whisper tokenization behavior; clients should trim if needed.
+
+Example curl (single file):
+
+```bash
+curl -X POST \
+ -H "Authorization: Bearer $REFLECTOR_GPU_APIKEY" \
+ -F "file=@/path/to/audio.mp3" \
+ -F "language=en" \
+ "$BASE_URL/v1/audio/transcriptions"
+```
+
+Example curl (multiple files, batch):
+
+```bash
+curl -X POST \
+ -H "Authorization: Bearer $REFLECTOR_GPU_APIKEY" \
+ -F "files=@/path/a1.mp3" -F "files=@/path/a2.mp3" \
+ -F "batch=true" -F "language=en" \
+ "$BASE_URL/v1/audio/transcriptions"
+```
+
+#### POST /v1/audio/transcriptions-from-url
+
+Transcribe a single remote audio file by URL.
+
+Request: application/json
+
+Body parameters:
+
+- `audio_file_url` (string) — required. URL of the audio file to transcribe.
+- `model` (string) — optional. Defaults to the implementation-specific model (see above).
+- `language` (string) — optional, defaults to `en`. Parakeet only accepts `en`.
+- `timestamp_offset` (number) — optional, defaults to `0.0`. Added to each word's `start`/`end` in the response.
+
+```json
+{
+ "audio_file_url": "https://example.com/audio.mp3",
+ "model": "nvidia/parakeet-tdt-0.6b-v2",
+ "language": "en",
+ "timestamp_offset": 0.0
+}
+```
+
+Response:
+
+```json
+{
+ "text": "transcribed text",
+ "words": [
+ { "word": "hello", "start": 10.0, "end": 10.5 },
+ { "word": "world", "start": 10.5, "end": 11.0 }
+ ]
+}
+```
+
+Notes:
+
+- `timestamp_offset` is added to each word’s `start`/`end` in the response.
+- Implementations may perform VAD-based chunking and batching for long-form audio; word timings are adjusted accordingly.
+
+Example curl:
+
+```bash
+curl -X POST \
+ -H "Authorization: Bearer $REFLECTOR_GPU_APIKEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "audio_file_url": "https://example.com/audio.mp3",
+ "language": "en",
+ "timestamp_offset": 0
+ }' \
+ "$BASE_URL/v1/audio/transcriptions-from-url"
+```
+
+### Error handling
+
+- 400 Bad Request
+ - Parakeet: `language` other than `en`
+ - Missing required parameters (`file`/`files` for upload; `audio_file_url` for URL endpoint)
+ - Unsupported file extension
+- 401 Unauthorized
+ - Missing or invalid Bearer token
+- 404 Not Found
+ - `audio_file_url` does not exist
+
+### Implementation details
+
+- GPUs: A10G for small-file/live, L40S for large-file URL transcription (subject to deployment)
+- VAD chunking and segment batching; word timings adjusted and overlapping ends constrained
+- Pads very short segments (< 0.5s) to avoid model crashes on some backends
+
+### Server configuration (Reflector API)
+
+Set the Reflector server to use the Modal backend and point `TRANSCRIPT_URL` to your chosen deployment:
+
+```
+TRANSCRIPT_BACKEND=modal
+TRANSCRIPT_URL=https://--reflector-transcriber-parakeet-web.modal.run
+TRANSCRIPT_MODAL_API_KEY=
+```
+
+### Conformance tests
+
+Use the pytest-based conformance tests to validate any new implementation (including self-hosted) against this spec:
+
+```
+TRANSCRIPT_URL=https:// \
+TRANSCRIPT_MODAL_API_KEY=your-api-key \
+uv run -m pytest -m gpu_modal --no-cov server/tests/test_gpu_modal_transcript.py
+```
diff --git a/server/gpu/modal_deployments/reflector_transcriber.py b/server/gpu/modal_deployments/reflector_transcriber.py
index 4bbbe512..3be25542 100644
--- a/server/gpu/modal_deployments/reflector_transcriber.py
+++ b/server/gpu/modal_deployments/reflector_transcriber.py
@@ -1,41 +1,78 @@
import os
-import tempfile
+import sys
import threading
+import uuid
+from typing import Generator, Mapping, NamedTuple, NewType, TypedDict
+from urllib.parse import urlparse
import modal
-from pydantic import BaseModel
-
-MODELS_DIR = "/models"
MODEL_NAME = "large-v2"
MODEL_COMPUTE_TYPE: str = "float16"
MODEL_NUM_WORKERS: int = 1
-
MINUTES = 60 # seconds
+SAMPLERATE = 16000
+UPLOADS_PATH = "/uploads"
+CACHE_PATH = "/models"
+SUPPORTED_FILE_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"]
+VAD_CONFIG = {
+ "batch_max_duration": 30.0,
+ "silence_padding": 0.5,
+ "window_size": 512,
+}
-volume = modal.Volume.from_name("models", create_if_missing=True)
+
+WhisperUniqFilename = NewType("WhisperUniqFilename", str)
+AudioFileExtension = NewType("AudioFileExtension", str)
app = modal.App("reflector-transcriber")
+model_cache = modal.Volume.from_name("models", create_if_missing=True)
+upload_volume = modal.Volume.from_name("whisper-uploads", create_if_missing=True)
+
+
+class TimeSegment(NamedTuple):
+ """Represents a time segment with start and end times."""
+
+ start: float
+ end: float
+
+
+class AudioSegment(NamedTuple):
+ """Represents an audio segment with timing and audio data."""
+
+ start: float
+ end: float
+ audio: any
+
+
+class TranscriptResult(NamedTuple):
+ """Represents a transcription result with text and word timings."""
+
+ text: str
+ words: list["WordTiming"]
+
+
+class WordTiming(TypedDict):
+ """Represents a word with its timing information."""
+
+ word: str
+ start: float
+ end: float
+
def download_model():
from faster_whisper import download_model
- volume.reload()
+ model_cache.reload()
- download_model(MODEL_NAME, cache_dir=MODELS_DIR)
+ download_model(MODEL_NAME, cache_dir=CACHE_PATH)
- volume.commit()
+ model_cache.commit()
image = (
modal.Image.debian_slim(python_version="3.12")
- .pip_install(
- "huggingface_hub==0.27.1",
- "hf-transfer==0.1.9",
- "torch==2.5.1",
- "faster-whisper==1.1.1",
- )
.env(
{
"HF_HUB_ENABLE_HF_TRANSFER": "1",
@@ -45,19 +82,98 @@ image = (
),
}
)
- .run_function(download_model, volumes={MODELS_DIR: volume})
+ .apt_install("ffmpeg")
+ .pip_install(
+ "huggingface_hub==0.27.1",
+ "hf-transfer==0.1.9",
+ "torch==2.5.1",
+ "faster-whisper==1.1.1",
+ "fastapi==0.115.12",
+ "requests",
+ "librosa==0.10.1",
+ "numpy<2",
+ "silero-vad==5.1.0",
+ )
+ .run_function(download_model, volumes={CACHE_PATH: model_cache})
)
+def detect_audio_format(url: str, headers: Mapping[str, str]) -> AudioFileExtension:
+ parsed_url = urlparse(url)
+ url_path = parsed_url.path
+
+ for ext in SUPPORTED_FILE_EXTENSIONS:
+ if url_path.lower().endswith(f".{ext}"):
+ return AudioFileExtension(ext)
+
+ content_type = headers.get("content-type", "").lower()
+ if "audio/mpeg" in content_type or "audio/mp3" in content_type:
+ return AudioFileExtension("mp3")
+ if "audio/wav" in content_type:
+ return AudioFileExtension("wav")
+ if "audio/mp4" in content_type:
+ return AudioFileExtension("mp4")
+
+ raise ValueError(
+ f"Unsupported audio format for URL: {url}. "
+ f"Supported extensions: {', '.join(SUPPORTED_FILE_EXTENSIONS)}"
+ )
+
+
+def download_audio_to_volume(
+ audio_file_url: str,
+) -> tuple[WhisperUniqFilename, AudioFileExtension]:
+ import requests
+ from fastapi import HTTPException
+
+ response = requests.head(audio_file_url, allow_redirects=True)
+ if response.status_code == 404:
+ raise HTTPException(status_code=404, detail="Audio file not found")
+
+ response = requests.get(audio_file_url, allow_redirects=True)
+ response.raise_for_status()
+
+ audio_suffix = detect_audio_format(audio_file_url, response.headers)
+ unique_filename = WhisperUniqFilename(f"{uuid.uuid4()}.{audio_suffix}")
+ file_path = f"{UPLOADS_PATH}/{unique_filename}"
+
+ with open(file_path, "wb") as f:
+ f.write(response.content)
+
+ upload_volume.commit()
+ return unique_filename, audio_suffix
+
+
+def pad_audio(audio_array, sample_rate: int = SAMPLERATE):
+ """Add 0.5s of silence if audio is shorter than the silence_padding window.
+
+ Whisper does not require this strictly, but aligning behavior with Parakeet
+ avoids edge-case crashes on extremely short inputs and makes comparisons easier.
+ """
+ import numpy as np
+
+ audio_duration = len(audio_array) / sample_rate
+ if audio_duration < VAD_CONFIG["silence_padding"]:
+ silence_samples = int(sample_rate * VAD_CONFIG["silence_padding"])
+ silence = np.zeros(silence_samples, dtype=np.float32)
+ return np.concatenate([audio_array, silence])
+ return audio_array
+
+
@app.cls(
gpu="A10G",
timeout=5 * MINUTES,
scaledown_window=5 * MINUTES,
- allow_concurrent_inputs=6,
image=image,
- volumes={MODELS_DIR: volume},
+ volumes={CACHE_PATH: model_cache, UPLOADS_PATH: upload_volume},
)
-class Transcriber:
+@modal.concurrent(max_inputs=10)
+class TranscriberWhisperLive:
+ """Live transcriber class for small audio segments (A10G).
+
+ Mirrors the Parakeet live class API but uses Faster-Whisper under the hood.
+ """
+
@modal.enter()
def enter(self):
import faster_whisper
@@ -71,23 +187,200 @@ class Transcriber:
device=self.device,
compute_type=MODEL_COMPUTE_TYPE,
num_workers=MODEL_NUM_WORKERS,
- download_root=MODELS_DIR,
+ download_root=CACHE_PATH,
local_files_only=True,
)
+ print(f"Model is on device: {self.device}")
@modal.method()
def transcribe_segment(
self,
- audio_data: str,
- audio_suffix: str,
- language: str,
+ filename: str,
+ language: str = "en",
):
- with tempfile.NamedTemporaryFile("wb+", suffix=f".{audio_suffix}") as fp:
- fp.write(audio_data)
+ """Transcribe a single uploaded audio file by filename."""
+ upload_volume.reload()
+
+ file_path = f"{UPLOADS_PATH}/{filename}"
+ if not os.path.exists(file_path):
+ raise FileNotFoundError(f"File not found: {file_path}")
+
+ with self.lock:
+ with NoStdStreams():
+ segments, _ = self.model.transcribe(
+ file_path,
+ language=language,
+ beam_size=5,
+ word_timestamps=True,
+ vad_filter=True,
+ vad_parameters={"min_silence_duration_ms": 500},
+ )
+
+ segments = list(segments)
+ text = "".join(segment.text for segment in segments).strip()
+ words = [
+ {
+ "word": word.word,
+ "start": round(float(word.start), 2),
+ "end": round(float(word.end), 2),
+ }
+ for segment in segments
+ for word in segment.words
+ ]
+
+ return {"text": text, "words": words}
+
+ @modal.method()
+ def transcribe_batch(
+ self,
+ filenames: list[str],
+ language: str = "en",
+ ):
+ """Transcribe multiple uploaded audio files and return per-file results."""
+ upload_volume.reload()
+
+ results = []
+ for filename in filenames:
+ file_path = f"{UPLOADS_PATH}/{filename}"
+ if not os.path.exists(file_path):
+ raise FileNotFoundError(f"Batch file not found: {file_path}")
+
+ with self.lock:
+ with NoStdStreams():
+ segments, _ = self.model.transcribe(
+ file_path,
+ language=language,
+ beam_size=5,
+ word_timestamps=True,
+ vad_filter=True,
+ vad_parameters={"min_silence_duration_ms": 500},
+ )
+
+ segments = list(segments)
+ text = "".join(seg.text for seg in segments).strip()
+ words = [
+ {
+ "word": w.word,
+ "start": round(float(w.start), 2),
+ "end": round(float(w.end), 2),
+ }
+ for seg in segments
+ for w in seg.words
+ ]
+
+ results.append(
+ {
+ "filename": filename,
+ "text": text,
+ "words": words,
+ }
+ )
+
+ return results
+
+
+@app.cls(
+ gpu="L40S",
+ timeout=15 * MINUTES,
+ image=image,
+ volumes={CACHE_PATH: model_cache, UPLOADS_PATH: upload_volume},
+)
+class TranscriberWhisperFile:
+ """File transcriber for larger/longer audio, using VAD-driven batching (L40S)."""
+
+ @modal.enter()
+ def enter(self):
+ import faster_whisper
+ import torch
+ from silero_vad import load_silero_vad
+
+ self.lock = threading.Lock()
+ self.use_gpu = torch.cuda.is_available()
+ self.device = "cuda" if self.use_gpu else "cpu"
+ self.model = faster_whisper.WhisperModel(
+ MODEL_NAME,
+ device=self.device,
+ compute_type=MODEL_COMPUTE_TYPE,
+ num_workers=MODEL_NUM_WORKERS,
+ download_root=CACHE_PATH,
+ local_files_only=True,
+ )
+ self.vad_model = load_silero_vad(onnx=False)
+
+ @modal.method()
+ def transcribe_segment(
+ self, filename: str, timestamp_offset: float = 0.0, language: str = "en"
+ ):
+ import librosa
+ import numpy as np
+ from silero_vad import VADIterator
+
+ def vad_segments(
+ audio_array,
+ sample_rate: int = SAMPLERATE,
+ window_size: int = VAD_CONFIG["window_size"],
+ ) -> Generator[TimeSegment, None, None]:
+ """Generate speech segments as TimeSegment using Silero VAD."""
+ iterator = VADIterator(self.vad_model, sampling_rate=sample_rate)
+ start = None
+ for i in range(0, len(audio_array), window_size):
+ chunk = audio_array[i : i + window_size]
+ if len(chunk) < window_size:
+ chunk = np.pad(
+ chunk, (0, window_size - len(chunk)), mode="constant"
+ )
+ speech = iterator(chunk)
+ if not speech:
+ continue
+ if "start" in speech:
+ start = speech["start"]
+ continue
+ if "end" in speech and start is not None:
+ end = speech["end"]
+ yield TimeSegment(
+ start / float(SAMPLERATE), end / float(SAMPLERATE)
+ )
+ start = None
+ iterator.reset_states()
+
+ upload_volume.reload()
+ file_path = f"{UPLOADS_PATH}/{filename}"
+ if not os.path.exists(file_path):
+ raise FileNotFoundError(f"File not found: {file_path}")
+
+ audio_array, _sr = librosa.load(file_path, sr=SAMPLERATE, mono=True)
+
+ # Batch segments up to ~30s windows by merging contiguous VAD segments
+ merged_batches: list[TimeSegment] = []
+ batch_start = None
+ batch_end = None
+ max_duration = VAD_CONFIG["batch_max_duration"]
+ for segment in vad_segments(audio_array):
+ seg_start, seg_end = segment.start, segment.end
+ if batch_start is None:
+ batch_start, batch_end = seg_start, seg_end
+ continue
+ if seg_end - batch_start <= max_duration:
+ batch_end = seg_end
+ else:
+ merged_batches.append(TimeSegment(batch_start, batch_end))
+ batch_start, batch_end = seg_start, seg_end
+ if batch_start is not None and batch_end is not None:
+ merged_batches.append(TimeSegment(batch_start, batch_end))
+
+ all_text = []
+ all_words = []
+
+ for segment in merged_batches:
+ start_time, end_time = segment.start, segment.end
+ s_idx = int(start_time * SAMPLERATE)
+ e_idx = int(end_time * SAMPLERATE)
+ segment = audio_array[s_idx:e_idx]
+ segment = pad_audio(segment, SAMPLERATE)
with self.lock:
segments, _ = self.model.transcribe(
- fp.name,
+ segment,
language=language,
beam_size=5,
word_timestamps=True,
@@ -96,66 +389,220 @@ class Transcriber:
)
segments = list(segments)
- text = "".join(segment.text for segment in segments)
+ text = "".join(seg.text for seg in segments).strip()
words = [
- {"word": word.word, "start": word.start, "end": word.end}
- for segment in segments
- for word in segment.words
+ {
+ "word": w.word,
+ "start": round(float(w.start) + start_time + timestamp_offset, 2),
+ "end": round(float(w.end) + start_time + timestamp_offset, 2),
+ }
+ for seg in segments
+ for w in seg.words
]
+ if text:
+ all_text.append(text)
+ all_words.extend(words)
- return {"text": text, "words": words}
+ return {"text": " ".join(all_text), "words": all_words}
+
+
+def detect_audio_format(url: str, headers: dict) -> str:
+ from urllib.parse import urlparse
+
+ from fastapi import HTTPException
+
+ url_path = urlparse(url).path
+ for ext in SUPPORTED_FILE_EXTENSIONS:
+ if url_path.lower().endswith(f".{ext}"):
+ return ext
+
+ content_type = headers.get("content-type", "").lower()
+ if "audio/mpeg" in content_type or "audio/mp3" in content_type:
+ return "mp3"
+ if "audio/wav" in content_type:
+ return "wav"
+ if "audio/mp4" in content_type:
+ return "mp4"
+
+ raise HTTPException(
+ status_code=400,
+ detail=(
+ f"Unsupported audio format for URL. Supported extensions: {', '.join(SUPPORTED_FILE_EXTENSIONS)}"
+ ),
+ )
+
+
+def download_audio_to_volume(audio_file_url: str) -> tuple[str, str]:
+ import requests
+ from fastapi import HTTPException
+
+ response = requests.head(audio_file_url, allow_redirects=True)
+ if response.status_code == 404:
+ raise HTTPException(status_code=404, detail="Audio file not found")
+
+ response = requests.get(audio_file_url, allow_redirects=True)
+ response.raise_for_status()
+
+ audio_suffix = detect_audio_format(audio_file_url, response.headers)
+ unique_filename = f"{uuid.uuid4()}.{audio_suffix}"
+ file_path = f"{UPLOADS_PATH}/{unique_filename}"
+
+ with open(file_path, "wb") as f:
+ f.write(response.content)
+
+ upload_volume.commit()
+ return unique_filename, audio_suffix
@app.function(
scaledown_window=60,
- timeout=60,
- allow_concurrent_inputs=40,
+ timeout=600,
secrets=[
modal.Secret.from_name("reflector-gpu"),
],
- volumes={MODELS_DIR: volume},
+ volumes={CACHE_PATH: model_cache, UPLOADS_PATH: upload_volume},
+ image=image,
)
+@modal.concurrent(max_inputs=40)
@modal.asgi_app()
def web():
- from fastapi import Body, Depends, FastAPI, HTTPException, UploadFile, status
+ from fastapi import (
+ Body,
+ Depends,
+ FastAPI,
+ Form,
+ HTTPException,
+ UploadFile,
+ status,
+ )
from fastapi.security import OAuth2PasswordBearer
- from typing_extensions import Annotated
- transcriber = Transcriber()
+ transcriber_live = TranscriberWhisperLive()
+ transcriber_file = TranscriberWhisperFile()
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
- supported_file_types = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"]
-
def apikey_auth(apikey: str = Depends(oauth2_scheme)):
- if apikey != os.environ["REFLECTOR_GPU_APIKEY"]:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Invalid API key",
- headers={"WWW-Authenticate": "Bearer"},
- )
+ if apikey == os.environ["REFLECTOR_GPU_APIKEY"]:
+ return
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Invalid API key",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
- class TranscriptResponse(BaseModel):
- result: dict
+ class TranscriptResponse(dict):
+ pass
@app.post("/v1/audio/transcriptions", dependencies=[Depends(apikey_auth)])
def transcribe(
- file: UploadFile,
- model: str = "whisper-1",
- language: Annotated[str, Body(...)] = "en",
- ) -> TranscriptResponse:
- audio_data = file.file.read()
- audio_suffix = file.filename.split(".")[-1]
- assert audio_suffix in supported_file_types
+ file: UploadFile = None,
+ files: list[UploadFile] | None = None,
+ model: str = Form(MODEL_NAME),
+ language: str = Form("en"),
+ batch: bool = Form(False),
+ ):
+ if not file and not files:
+ raise HTTPException(
+ status_code=400, detail="Either 'file' or 'files' parameter is required"
+ )
+ if batch and not files:
+ raise HTTPException(
+ status_code=400, detail="Batch transcription requires 'files'"
+ )
- func = transcriber.transcribe_segment.spawn(
- audio_data=audio_data,
- audio_suffix=audio_suffix,
- language=language,
- )
- result = func.get()
- return result
+ upload_files = [file] if file else files
+
+ uploaded_filenames: list[str] = []
+ for upload_file in upload_files:
+ audio_suffix = upload_file.filename.split(".")[-1]
+ if audio_suffix not in SUPPORTED_FILE_EXTENSIONS:
+ raise HTTPException(
+ status_code=400,
+ detail=(
+ f"Unsupported audio format. Supported extensions: {', '.join(SUPPORTED_FILE_EXTENSIONS)}"
+ ),
+ )
+
+ unique_filename = f"{uuid.uuid4()}.{audio_suffix}"
+ file_path = f"{UPLOADS_PATH}/{unique_filename}"
+ with open(file_path, "wb") as f:
+ content = upload_file.file.read()
+ f.write(content)
+ uploaded_filenames.append(unique_filename)
+
+ upload_volume.commit()
+
+ try:
+ if batch and len(upload_files) > 1:
+ func = transcriber_live.transcribe_batch.spawn(
+ filenames=uploaded_filenames,
+ language=language,
+ )
+ results = func.get()
+ return {"results": results}
+
+ results = []
+ for filename in uploaded_filenames:
+ func = transcriber_live.transcribe_segment.spawn(
+ filename=filename,
+ language=language,
+ )
+ result = func.get()
+ result["filename"] = filename
+ results.append(result)
+
+ return {"results": results} if len(results) > 1 else results[0]
+ finally:
+ for filename in uploaded_filenames:
+ try:
+ file_path = f"{UPLOADS_PATH}/{filename}"
+ os.remove(file_path)
+ except Exception:
+ pass
+ upload_volume.commit()
+
+ @app.post("/v1/audio/transcriptions-from-url", dependencies=[Depends(apikey_auth)])
+ def transcribe_from_url(
+ audio_file_url: str = Body(
+ ..., description="URL of the audio file to transcribe"
+ ),
+ model: str = Body(MODEL_NAME),
+ language: str = Body("en"),
+ timestamp_offset: float = Body(0.0),
+ ):
+ unique_filename, _audio_suffix = download_audio_to_volume(audio_file_url)
+ try:
+ func = transcriber_file.transcribe_segment.spawn(
+ filename=unique_filename,
+ timestamp_offset=timestamp_offset,
+ language=language,
+ )
+ result = func.get()
+ return result
+ finally:
+ try:
+ file_path = f"{UPLOADS_PATH}/{unique_filename}"
+ os.remove(file_path)
+ upload_volume.commit()
+ except Exception:
+ pass
return app
+
+
+class NoStdStreams:
+ def __init__(self):
+ self.devnull = open(os.devnull, "w")
+
+ def __enter__(self):
+ self._stdout, self._stderr = sys.stdout, sys.stderr
+ self._stdout.flush()
+ self._stderr.flush()
+ sys.stdout, sys.stderr = self.devnull, self.devnull
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ sys.stdout, sys.stderr = self._stdout, self._stderr
+ self.devnull.close()
diff --git a/server/tests/test_gpu_modal_transcript.py b/server/tests/test_gpu_modal_transcript.py
index 9b37fbe6..9a152185 100644
--- a/server/tests/test_gpu_modal_transcript.py
+++ b/server/tests/test_gpu_modal_transcript.py
@@ -272,6 +272,9 @@ class TestGPUModalTranscript:
for f in temp_files:
Path(f).unlink(missing_ok=True)
+ @pytest.mark.skipif(
+ not "parakeet" in get_model_name(), reason="Parakeet only supports English"
+ )
def test_transcriptions_error_handling(self):
"""Test error handling for invalid requests."""
url = get_modal_transcript_url()
From c4d2825c81f81ad8835629fbf6ea8c7383f8c31b Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Fri, 5 Sep 2025 18:01:31 -0400
Subject: [PATCH 03/20] feat: frontend openapi react query (#606)
* refactor: migrate from @hey-api/openapi-ts to openapi-react-query
- Replace @hey-api/openapi-ts with openapi-typescript and openapi-react-query
- Generate TypeScript types from OpenAPI spec
- Set up React Query infrastructure with QueryClientProvider
- Migrate all API hooks to use React Query patterns
- Maintain backward compatibility for existing components
- Remove old API infrastructure and dependencies
* fix: resolve import errors and add missing api hooks
- Create constants.ts for RECORD_A_MEETING_URL
- Add api-types.ts for backward compatible type exports
- Update all imports from deleted api folder to new locations
- Add missing React Query hooks for rooms and zulip operations
- Create useApi compatibility layer for unmigrated components
* feat: migrate components to React Query hooks
- Add comprehensive API hooks for all operations
- Migrate rooms page to use React Query mutations
- Update transcript title component to use mutation hook
- Refactor share/privacy component with proper error handling
- Remove direct API client usage in favor of hooks
* feat: complete migration from @hey-api/openapi-ts to openapi-react-query
- Migrated all components from useApi compatibility layer to direct React Query hooks
- Added new hooks for participant operations, room meetings, and speaker operations
- Updated all imports from old api module to api-types
- Fixed TypeScript types and API endpoint signatures
- Removed deprecated useApi.ts compatibility layer
- Fixed SourceKind enum values to match OpenAPI spec
- Added @ts-ignore for Zulip endpoints not in OpenAPI spec yet
- Fixed all compilation errors and type issues
* fix: authentication flow with React Query migration
- Fix middleware management in apiClient to properly handle auth tokens
- Update ApiAuthProvider to correctly configure base URL and auth
- Add missing NextAuth API route handler at app/api/auth/[...nextauth]/route.ts
- Remove middleware ejection attempts (not supported by openapi-fetch)
- Use global variables to store current auth token and API URL
- Setup middleware once on initialization instead of repeatedly adding
This fixes the login/logout flow that was broken after migrating from
the useApi compatibility layer to native React Query hooks.
* fix: prevent unauthorized API calls before authentication
- Add global AuthGuard component to handle authentication at layout level
- Make all API query hooks conditional on authentication status
- Define public routes (like /transcripts/new) that don't require auth
- Fix login flow to use NextAuth signIn instead of non-existent /login route
- Prevent 401 errors by waiting for auth token before making API calls
Previously, all routes under (app) were publicly accessible with each page
handling auth individually. Now authentication is enforced globally while
still allowing specific routes to remain public.
* refactor: remove redundant client-side AuthGuard
The authentication is already properly handled by Next.js middleware
in middleware.ts with LOGIN_REQUIRED_PAGES. The middleware approach is
superior as it:
- Provides server-side protection before page loads
- Prevents flash of unauthorized content
- Centralizes auth logic in one place
- Better performance (no client-side JS needed)
Keep the API hooks conditional to prevent 401 errors before token is ready.
* fix: use direct status check for API query authentication
Changed all query hooks to use direct `status === "authenticated"` check
instead of derived `isAuthenticated && !isLoading` to avoid race conditions
where queries might fire before the authentication token is properly set.
This prevents the brief 401 errors that occur on page refresh when the
session is being restored.
* fix: correct content-type header for FormData uploads
Previously, the API client was setting a default Content-Type of application/json
for all requests, which broke file uploads that need multipart/form-data.
Now the client only sets application/json when the body is not FormData,
allowing FormData to automatically set the correct multipart boundary.
* fix: resolve authentication race condition with React Query
Previously, API calls were being made before the auth token was configured,
causing initial 401 errors that would retry with 200 after token setup.
Changes:
- Add global auth readiness tracking in apiClient
- Create useAuthReady hook that checks both session and token state
- Update all API hooks to use isAuthReady instead of just session status
- Add AuthWrapper component at layout level for consistent loading UX
- Show spinner while authentication initializes across all pages
This ensures API calls only fire after authentication is fully configured,
eliminating the 401/retry pattern and improving user experience.
* refactor: clean up api-hooks.ts comments and improve search invalidation
- Remove redundant function category comments (exports are self-explanatory)
- Remove obvious inline comments for query invalidation
- Fix search endpoint invalidation to clear all queries regardless of parameters
* refactor: remove api-types.ts compatibility layer
- Migrated all 29 files from api-types.ts to use reflector-api.d.ts directly
- Removed $SourceKind manual enum in favor of OpenAPI-generated types
- Fixed unrelated Spinner component TypeScript error in AuthWrapper.tsx
- All imports now use: import type { components } from "path/to/reflector-api"
- Deleted api-types.ts file completely
* refactor: rename api-hooks.ts to apiHooks.ts for consistency
- Renamed api-hooks.ts to apiHooks.ts to follow camelCase convention
- Updated all 21 import statements across the codebase
- Maintains consistency with other non-component files (apiClient.tsx, useAuthReady.ts, etc.)
- Follows established naming pattern: PascalCase for components, camelCase for utilities/hooks
* chore: add .playwright-mcp to .gitignore
* refactor: remove SK helper object and use inline type casting in FilterSidebar
Replace the SK (SourceKind) helper object with direct inline type casting
to simplify the code and reduce unnecessary abstraction.
* chore: clean up migration comments from React Query refactoring
- Remove temporary "// Use new React Query hooks" comments
- Remove "// React Query hooks" comments from browse and rooms pages
- Update package.json script name from codegen to openapi for consistency
* refactor: remove Redis dependencies from frontend authentication
- Replace Redis/Redlock with in-memory cache for token management
- Remove @vercel/kv, ioredis, and redlock dependencies from package.json
- Implement simple lock mechanism for concurrent token refresh prevention
- Use Map-based cache with TTL for token storage
- Maintain same authentication flow without external dependencies
This simplifies the infrastructure requirements and removes the need for
Redis while maintaining the same functionality through in-memory caching.
* fix: add staleTime to prevent cross-tab staled data
* fix: remove infinite re-render loop in useSessionAccessToken
The hook was maintaining redundant local state that caused re-renders
on every update, which triggered NextAuth to continuously refetch the
session, resulting in hundreds of POST requests to /api/auth/session.
Simplified the hook to directly return session values without
unnecessary state duplication.
* fix: handle undefined access tokens in auth.ts
Added fallback to empty string for potentially undefined access_token
and refresh_token from NextAuth account object to satisfy
JWTWithAccessToken type requirements.
* Igor/mathieu/frontend openapi react query (#597)
* small typing
* typing fixes
---------
Co-authored-by: Igor Loskutov
* self-review-fix
* authReady callback simplify
* fix auth
* fix compose
* room detail page fix
* compile fix
* room edit fix
* normalize auth provider
* room edition state granular management
* cover TODOs + cross-tab cache
* session auto refresh blink
* schema generator error type doc
* protect from zombie auth
* clarify access token refresh logic a bit
* remove react-query tab sharing cache
* remove react-query tab sharing cache
* websocket dupe react devmode protection
* invalidate room on room update
* redis cache
* test ts server
* ci randomness
* less edgy config (ci)
* less edgy config (ci)
* less edgy config (ci)
* ci randomness
* ci randomness
* ci randomness
* ci randomness
* less edgy config (ci)
* added vs edited room state cleanup
* file upload real-time state management fix
* prettier auth state ternary
* prettier auth state ternary
* proper api address from env
* INTERVAL_REFRESH_MS
* node version 20 for tests
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* github debug
* CI debug
* CI debug
* nextjs magic
* nextjs magic
* doc
* client-side stale auth soft safety net
---------
Co-authored-by: Mathieu Virbel
Co-authored-by: Igor Loskutov
---
.github/workflows/test_next_server.yml | 45 +
.gitignore | 1 +
compose.yml | 3 +
server/reflector/views/rooms.py | 1 +
server/reflector/views/transcripts.py | 3 +-
server/runserver.sh | 2 +-
www/app/(app)/AuthWrapper.tsx | 30 +
.../browse/_components/FilterSidebar.tsx | 7 +-
.../browse/_components/TranscriptCards.tsx | 7 +-
www/app/(app)/browse/page.tsx | 149 +-
www/app/(app)/layout.tsx | 5 +-
www/app/(app)/rooms/_components/RoomCards.tsx | 6 +-
www/app/(app)/rooms/_components/RoomList.tsx | 6 +-
www/app/(app)/rooms/_components/RoomTable.tsx | 6 +-
www/app/(app)/rooms/page.tsx | 233 +-
www/app/(app)/rooms/useRoomList.tsx | 49 +-
.../[transcriptId]/correct/page.tsx | 28 +-
.../correct/participantList.tsx | 197 +-
.../[transcriptId]/correct/topicHeader.tsx | 3 +-
.../[transcriptId]/finalSummary.tsx | 37 +-
.../(app)/transcripts/[transcriptId]/page.tsx | 4 +-
.../[transcriptId]/upload/page.tsx | 13 +-
www/app/(app)/transcripts/createTranscript.ts | 46 +-
.../(app)/transcripts/fileUploadButton.tsx | 64 +-
www/app/(app)/transcripts/new/page.tsx | 39 +-
www/app/(app)/transcripts/player.tsx | 10 +-
www/app/(app)/transcripts/recorder.tsx | 1 -
www/app/(app)/transcripts/shareAndPrivacy.tsx | 47 +-
www/app/(app)/transcripts/shareCopy.tsx | 4 +-
www/app/(app)/transcripts/shareZulip.tsx | 145 +-
www/app/(app)/transcripts/transcriptTitle.tsx | 21 +-
www/app/(app)/transcripts/useMp3.ts | 108 +-
www/app/(app)/transcripts/useParticipants.ts | 74 +-
.../(app)/transcripts/useSearchTranscripts.ts | 123 -
.../(app)/transcripts/useTopicWithWords.ts | 78 +-
www/app/(app)/transcripts/useTopics.ts | 42 +-
www/app/(app)/transcripts/useTranscript.ts | 75 +-
www/app/(app)/transcripts/useWaveform.ts | 47 +-
www/app/(app)/transcripts/useWebRTC.ts | 44 +-
www/app/(app)/transcripts/useWebSockets.ts | 62 +-
www/app/(app)/transcripts/webSocketTypes.ts | 4 +-
www/app/(auth)/userInfo.tsx | 17 +-
www/app/[roomName]/page.tsx | 42 +-
www/app/[roomName]/useRoomMeeting.tsx | 43 +-
www/app/api/OpenApi.ts | 37 -
www/app/api/auth/[...nextauth]/route.ts | 5 +-
www/app/api/core/ApiError.ts | 25 -
www/app/api/core/ApiRequestOptions.ts | 21 -
www/app/api/core/ApiResult.ts | 7 -
www/app/api/core/AxiosHttpRequest.ts | 23 -
www/app/api/core/BaseHttpRequest.ts | 11 -
www/app/api/core/CancelablePromise.ts | 126 -
www/app/api/core/OpenAPI.ts | 57 -
www/app/api/core/request.ts | 387 ---
www/app/api/index.ts | 9 -
www/app/api/schemas.gen.ts | 1776 ----------
www/app/api/services.gen.ts | 942 ------
www/app/api/types.gen.ts | 1143 -------
www/app/api/urls.ts | 1 -
www/app/layout.tsx | 23 +-
www/app/lib/AuthProvider.tsx | 104 +
www/app/lib/SessionAutoRefresh.tsx | 38 +-
www/app/lib/SessionProvider.tsx | 11 -
www/app/lib/__tests__/redisTokenCache.test.ts | 85 +
www/app/lib/apiClient.tsx | 50 +
www/app/lib/apiHooks.ts | 618 ++++
www/app/lib/auth.ts | 166 +-
www/app/lib/authBackend.ts | 178 +
www/app/lib/edgeConfig.ts | 14 +-
www/app/lib/next.ts | 2 +
www/app/lib/queryClient.tsx | 17 +
www/app/lib/redisClient.ts | 46 +
www/app/lib/redisTokenCache.ts | 61 +
www/app/lib/types.ts | 68 +-
www/app/lib/useApi.ts | 37 -
www/app/lib/useLoginRequiredPages.ts | 26 +
www/app/lib/useSessionAccessToken.ts | 42 -
www/app/lib/useSessionStatus.ts | 22 -
www/app/lib/useSessionUser.ts | 33 -
www/app/lib/useUserName.ts | 7 +
www/app/lib/utils.ts | 23 +-
www/app/providers.tsx | 22 +-
www/app/reflector-api.d.ts | 2330 +++++++++++++
www/jest.config.js | 8 +
www/middleware.ts | 11 +-
www/next.config.js | 3 +
www/openapi-ts.config.ts | 14 -
www/package.json | 19 +-
www/pnpm-lock.yaml | 2900 +++++++++++++++--
www/public/service-worker.js | 2 +-
90 files changed, 7253 insertions(+), 6268 deletions(-)
create mode 100644 .github/workflows/test_next_server.yml
create mode 100644 www/app/(app)/AuthWrapper.tsx
delete mode 100644 www/app/(app)/transcripts/useSearchTranscripts.ts
delete mode 100644 www/app/api/OpenApi.ts
delete mode 100644 www/app/api/core/ApiError.ts
delete mode 100644 www/app/api/core/ApiRequestOptions.ts
delete mode 100644 www/app/api/core/ApiResult.ts
delete mode 100644 www/app/api/core/AxiosHttpRequest.ts
delete mode 100644 www/app/api/core/BaseHttpRequest.ts
delete mode 100644 www/app/api/core/CancelablePromise.ts
delete mode 100644 www/app/api/core/OpenAPI.ts
delete mode 100644 www/app/api/core/request.ts
delete mode 100644 www/app/api/index.ts
create mode 100644 www/app/lib/AuthProvider.tsx
delete mode 100644 www/app/lib/SessionProvider.tsx
create mode 100644 www/app/lib/__tests__/redisTokenCache.test.ts
create mode 100644 www/app/lib/apiClient.tsx
create mode 100644 www/app/lib/apiHooks.ts
create mode 100644 www/app/lib/authBackend.ts
create mode 100644 www/app/lib/next.ts
create mode 100644 www/app/lib/queryClient.tsx
create mode 100644 www/app/lib/redisClient.ts
create mode 100644 www/app/lib/redisTokenCache.ts
delete mode 100644 www/app/lib/useApi.ts
create mode 100644 www/app/lib/useLoginRequiredPages.ts
delete mode 100644 www/app/lib/useSessionAccessToken.ts
delete mode 100644 www/app/lib/useSessionStatus.ts
delete mode 100644 www/app/lib/useSessionUser.ts
create mode 100644 www/app/lib/useUserName.ts
create mode 100644 www/app/reflector-api.d.ts
create mode 100644 www/jest.config.js
delete mode 100644 www/openapi-ts.config.ts
diff --git a/.github/workflows/test_next_server.yml b/.github/workflows/test_next_server.yml
new file mode 100644
index 00000000..892566d6
--- /dev/null
+++ b/.github/workflows/test_next_server.yml
@@ -0,0 +1,45 @@
+name: Test Next Server
+
+on:
+ pull_request:
+ paths:
+ - "www/**"
+ push:
+ branches:
+ - main
+ paths:
+ - "www/**"
+
+jobs:
+ test-next-server:
+ runs-on: ubuntu-latest
+
+ defaults:
+ run:
+ working-directory: ./www
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 8
+
+ - name: Setup Node.js cache
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'pnpm'
+ cache-dependency-path: './www/pnpm-lock.yaml'
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Run tests
+ run: pnpm test
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 29d56f25..f3249991 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ server/test.sqlite
CLAUDE.local.md
www/.env.development
www/.env.production
+.playwright-mcp
diff --git a/compose.yml b/compose.yml
index 492c7b8c..acbfd3b5 100644
--- a/compose.yml
+++ b/compose.yml
@@ -6,6 +6,7 @@ services:
- 1250:1250
volumes:
- ./server/:/app/
+ - /app/.venv
env_file:
- ./server/.env
environment:
@@ -16,6 +17,7 @@ services:
context: server
volumes:
- ./server/:/app/
+ - /app/.venv
env_file:
- ./server/.env
environment:
@@ -26,6 +28,7 @@ services:
context: server
volumes:
- ./server/:/app/
+ - /app/.venv
env_file:
- ./server/.env
environment:
diff --git a/server/reflector/views/rooms.py b/server/reflector/views/rooms.py
index 40e81aeb..cc00f3c0 100644
--- a/server/reflector/views/rooms.py
+++ b/server/reflector/views/rooms.py
@@ -197,6 +197,7 @@ async def rooms_create_meeting(
end_date = current_time + timedelta(hours=8)
whereby_meeting = await create_meeting("", end_date=end_date, room=room)
+
await upload_logo(whereby_meeting["roomName"], "./images/logo.png")
# Now try to save to database
diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py
index 3f32a9bd..9acfcbf8 100644
--- a/server/reflector/views/transcripts.py
+++ b/server/reflector/views/transcripts.py
@@ -27,6 +27,7 @@ from reflector.db.search import (
from reflector.db.transcripts import (
SourceKind,
TranscriptParticipant,
+ TranscriptStatus,
TranscriptTopic,
transcripts_controller,
)
@@ -63,7 +64,7 @@ class GetTranscriptMinimal(BaseModel):
id: str
user_id: str | None
name: str
- status: str
+ status: TranscriptStatus
locked: bool
duration: float
title: str | None
diff --git a/server/runserver.sh b/server/runserver.sh
index a4fb6869..9cccaacb 100755
--- a/server/runserver.sh
+++ b/server/runserver.sh
@@ -2,7 +2,7 @@
if [ "${ENTRYPOINT}" = "server" ]; then
uv run alembic upgrade head
- uv run -m reflector.app
+ uv run uvicorn reflector.app:app --host 0.0.0.0 --port 1250
elif [ "${ENTRYPOINT}" = "worker" ]; then
uv run celery -A reflector.worker.app worker --loglevel=info
elif [ "${ENTRYPOINT}" = "beat" ]; then
diff --git a/www/app/(app)/AuthWrapper.tsx b/www/app/(app)/AuthWrapper.tsx
new file mode 100644
index 00000000..57038b7b
--- /dev/null
+++ b/www/app/(app)/AuthWrapper.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import { Flex, Spinner } from "@chakra-ui/react";
+import { useAuth } from "../lib/AuthProvider";
+import { useLoginRequiredPages } from "../lib/useLoginRequiredPages";
+
+export default function AuthWrapper({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const auth = useAuth();
+ const redirectPath = useLoginRequiredPages();
+ const redirectHappens = !!redirectPath;
+
+ if (auth.status === "loading" || redirectHappens) {
+ return (
+
+
+
+ );
+ }
+
+ return <>{children}>;
+}
diff --git a/www/app/(app)/browse/_components/FilterSidebar.tsx b/www/app/(app)/browse/_components/FilterSidebar.tsx
index b2abe481..6eef61b8 100644
--- a/www/app/(app)/browse/_components/FilterSidebar.tsx
+++ b/www/app/(app)/browse/_components/FilterSidebar.tsx
@@ -1,7 +1,10 @@
import React from "react";
import { Box, Stack, Link, Heading } from "@chakra-ui/react";
import NextLink from "next/link";
-import { Room, SourceKind } from "../../../api";
+import type { components } from "../../../reflector-api";
+
+type Room = components["schemas"]["Room"];
+type SourceKind = components["schemas"]["SourceKind"];
interface FilterSidebarProps {
rooms: Room[];
@@ -72,7 +75,7 @@ export default function FilterSidebar({
key={room.id}
as={NextLink}
href="#"
- onClick={() => onFilterChange("room", room.id)}
+ onClick={() => onFilterChange("room" as SourceKind, room.id)}
color={
selectedSourceKind === "room" && selectedRoomId === room.id
? "blue.500"
diff --git a/www/app/(app)/browse/_components/TranscriptCards.tsx b/www/app/(app)/browse/_components/TranscriptCards.tsx
index b67e71e7..8dbc3568 100644
--- a/www/app/(app)/browse/_components/TranscriptCards.tsx
+++ b/www/app/(app)/browse/_components/TranscriptCards.tsx
@@ -18,7 +18,10 @@ import {
highlightMatches,
generateTextFragment,
} from "../../../lib/textHighlight";
-import { SearchResult } from "../../../api";
+import type { components } from "../../../reflector-api";
+
+type SearchResult = components["schemas"]["SearchResult"];
+type SourceKind = components["schemas"]["SourceKind"];
interface TranscriptCardsProps {
results: SearchResult[];
@@ -120,7 +123,7 @@ function TranscriptCard({
: "N/A";
const formattedDate = formatLocalDate(result.created_at);
const source =
- result.source_kind === "room"
+ result.source_kind === ("room" as SourceKind)
? result.room_name || result.room_id
: result.source_kind;
diff --git a/www/app/(app)/browse/page.tsx b/www/app/(app)/browse/page.tsx
index e7522e14..8523650e 100644
--- a/www/app/(app)/browse/page.tsx
+++ b/www/app/(app)/browse/page.tsx
@@ -19,37 +19,33 @@ import {
parseAsStringLiteral,
} from "nuqs";
import { LuX } from "react-icons/lu";
-import { useSearchTranscripts } from "../transcripts/useSearchTranscripts";
-import useSessionUser from "../../lib/useSessionUser";
-import { Room, SourceKind, SearchResult, $SourceKind } from "../../api";
-import useApi from "../../lib/useApi";
-import { useError } from "../../(errors)/errorContext";
+import type { components } from "../../reflector-api";
+
+type Room = components["schemas"]["Room"];
+type SourceKind = components["schemas"]["SourceKind"];
+type SearchResult = components["schemas"]["SearchResult"];
+import {
+ useRoomsList,
+ useTranscriptsSearch,
+ useTranscriptDelete,
+ useTranscriptProcess,
+} from "../../lib/apiHooks";
import FilterSidebar from "./_components/FilterSidebar";
import Pagination, {
FIRST_PAGE,
PaginationPage,
parsePaginationPage,
totalPages as getTotalPages,
+ paginationPageTo0Based,
} from "./_components/Pagination";
import TranscriptCards from "./_components/TranscriptCards";
import DeleteTranscriptDialog from "./_components/DeleteTranscriptDialog";
import { formatLocalDate } from "../../lib/time";
import { RECORD_A_MEETING_URL } from "../../api/urls";
+import { useUserName } from "../../lib/useUserName";
const SEARCH_FORM_QUERY_INPUT_NAME = "query" as const;
-const usePrefetchRooms = (setRooms: (rooms: Room[]) => void): void => {
- const { setError } = useError();
- const api = useApi();
- useEffect(() => {
- if (!api) return;
- api
- .v1RoomsList({ page: 1 })
- .then((rooms) => setRooms(rooms.items))
- .catch((err) => setError(err, "There was an error fetching the rooms"));
- }, [api, setError]);
-};
-
const SearchForm: React.FC<{
setPage: (page: PaginationPage) => void;
sourceKind: SourceKind | null;
@@ -69,7 +65,6 @@ const SearchForm: React.FC<{
searchQuery,
setSearchQuery,
}) => {
- // to keep the search input controllable + more fine grained control (urlSearchQuery is updated on submits)
const [searchInputValue, setSearchInputValue] = useState(searchQuery || "");
const handleSearchQuerySubmit = async (d: FormData) => {
await setSearchQuery((d.get(SEARCH_FORM_QUERY_INPUT_NAME) as string) || "");
@@ -163,7 +158,6 @@ const UnderSearchFormFilterIndicators: React.FC<{
p="1px"
onClick={() => {
setSourceKind(null);
- // TODO questionable
setRoomId(null);
}}
_hover={{ bg: "blue.200" }}
@@ -209,7 +203,11 @@ export default function TranscriptBrowser() {
const [urlSourceKind, setUrlSourceKind] = useQueryState(
"source",
- parseAsStringLiteral($SourceKind.enum).withOptions({
+ parseAsStringLiteral([
+ "room",
+ "live",
+ "file",
+ ] as const satisfies SourceKind[]).withOptions({
shallow: false,
}),
);
@@ -229,46 +227,40 @@ export default function TranscriptBrowser() {
useEffect(() => {
const maybePage = parsePaginationPage(urlPage);
if ("error" in maybePage) {
- setPage(FIRST_PAGE).then(() => {
- /*may be called n times we dont care*/
- });
+ setPage(FIRST_PAGE).then(() => {});
return;
}
_setSafePage(maybePage.value);
}, [urlPage]);
- const [rooms, setRooms] = useState([]);
-
const pageSize = 20;
+
const {
- results,
- totalCount: totalResults,
- isLoading,
- reload,
- } = useSearchTranscripts(
- urlSearchQuery,
- {
- roomIds: urlRoomId ? [urlRoomId] : null,
- sourceKind: urlSourceKind,
- },
- {
- pageSize,
- page,
- },
- );
+ data: searchData,
+ isLoading: searchLoading,
+ refetch: reloadSearch,
+ } = useTranscriptsSearch(urlSearchQuery, {
+ limit: pageSize,
+ offset: paginationPageTo0Based(page) * pageSize,
+ room_id: urlRoomId || undefined,
+ source_kind: urlSourceKind || undefined,
+ });
+
+ const results = searchData?.results || [];
+ const totalResults = searchData?.total || 0;
+
+ // Fetch rooms
+ const { data: roomsData } = useRoomsList(1);
+ const rooms = roomsData?.items || [];
const totalPages = getTotalPages(totalResults, pageSize);
- const userName = useSessionUser().name;
+ const userName = useUserName();
const [deletionLoading, setDeletionLoading] = useState(false);
- const api = useApi();
- const { setError } = useError();
const cancelRef = React.useRef(null);
const [transcriptToDeleteId, setTranscriptToDeleteId] =
React.useState();
- usePrefetchRooms(setRooms);
-
const handleFilterTranscripts = (
sourceKind: SourceKind | null,
roomId: string,
@@ -280,44 +272,37 @@ export default function TranscriptBrowser() {
const onCloseDeletion = () => setTranscriptToDeleteId(undefined);
+ const deleteTranscript = useTranscriptDelete();
+ const processTranscript = useTranscriptProcess();
+
const confirmDeleteTranscript = (transcriptId: string) => {
- if (!api || deletionLoading) return;
+ if (deletionLoading) return;
setDeletionLoading(true);
- api
- .v1TranscriptDelete({ transcriptId })
- .then(() => {
- setDeletionLoading(false);
- onCloseDeletion();
- reload();
- })
- .catch((err) => {
- setDeletionLoading(false);
- setError(err, "There was an error deleting the transcript");
- });
+ deleteTranscript.mutate(
+ {
+ params: {
+ path: { transcript_id: transcriptId },
+ },
+ },
+ {
+ onSuccess: () => {
+ setDeletionLoading(false);
+ onCloseDeletion();
+ reloadSearch();
+ },
+ onError: () => {
+ setDeletionLoading(false);
+ },
+ },
+ );
};
const handleProcessTranscript = (transcriptId: string) => {
- if (!api) {
- console.error("API not available on handleProcessTranscript");
- return;
- }
- api
- .v1TranscriptProcess({ transcriptId })
- .then((result) => {
- const status =
- result && typeof result === "object" && "status" in result
- ? (result as { status: string }).status
- : undefined;
- if (status === "already running") {
- setError(
- new Error("Processing is already running, please wait"),
- "Processing is already running, please wait",
- );
- }
- })
- .catch((err) => {
- setError(err, "There was an error processing the transcript");
- });
+ processTranscript.mutate({
+ params: {
+ path: { transcript_id: transcriptId },
+ },
+ });
};
const transcriptToDelete = results?.find(
@@ -332,7 +317,7 @@ export default function TranscriptBrowser() {
? transcriptToDelete.room_name || transcriptToDelete.room_id
: transcriptToDelete?.source_kind;
- if (isLoading && results.length === 0) {
+ if (searchLoading && results.length === 0) {
return (
{userName ? `${userName}'s Transcriptions` : "Your Transcriptions"}{" "}
- {(isLoading || deletionLoading) && }
+ {(searchLoading || deletionLoading) && }
@@ -403,12 +388,12 @@ export default function TranscriptBrowser() {
- {!isLoading && results.length === 0 && (
+ {!searchLoading && results.length === 0 && (
)}
diff --git a/www/app/(app)/layout.tsx b/www/app/(app)/layout.tsx
index 5760e19d..801be28f 100644
--- a/www/app/(app)/layout.tsx
+++ b/www/app/(app)/layout.tsx
@@ -2,9 +2,8 @@ import { Container, Flex, Link } from "@chakra-ui/react";
import { getConfig } from "../lib/edgeConfig";
import NextLink from "next/link";
import Image from "next/image";
-import About from "../(aboutAndPrivacy)/about";
-import Privacy from "../(aboutAndPrivacy)/privacy";
import UserInfo from "../(auth)/userInfo";
+import AuthWrapper from "./AuthWrapper";
import { RECORD_A_MEETING_URL } from "../api/urls";
export default async function AppLayout({
@@ -90,7 +89,7 @@ export default async function AppLayout({
- {children}
+ {children}
);
}
diff --git a/www/app/(app)/rooms/_components/RoomCards.tsx b/www/app/(app)/rooms/_components/RoomCards.tsx
index 16748d90..8b22ad72 100644
--- a/www/app/(app)/rooms/_components/RoomCards.tsx
+++ b/www/app/(app)/rooms/_components/RoomCards.tsx
@@ -12,11 +12,13 @@ import {
HStack,
} from "@chakra-ui/react";
import { LuLink } from "react-icons/lu";
-import { RoomDetails } from "../../../api";
+import type { components } from "../../../reflector-api";
+
+type Room = components["schemas"]["Room"];
import { RoomActionsMenu } from "./RoomActionsMenu";
interface RoomCardsProps {
- rooms: RoomDetails[];
+ rooms: Room[];
linkCopied: string;
onCopyUrl: (roomName: string) => void;
onEdit: (roomId: string, roomData: any) => void;
diff --git a/www/app/(app)/rooms/_components/RoomList.tsx b/www/app/(app)/rooms/_components/RoomList.tsx
index 73fe8a5c..218c890c 100644
--- a/www/app/(app)/rooms/_components/RoomList.tsx
+++ b/www/app/(app)/rooms/_components/RoomList.tsx
@@ -1,11 +1,13 @@
import { Box, Heading, Text, VStack } from "@chakra-ui/react";
-import { RoomDetails } from "../../../api";
+import type { components } from "../../../reflector-api";
+
+type Room = components["schemas"]["Room"];
import { RoomTable } from "./RoomTable";
import { RoomCards } from "./RoomCards";
interface RoomListProps {
title: string;
- rooms: RoomDetails[];
+ rooms: Room[];
linkCopied: string;
onCopyUrl: (roomName: string) => void;
onEdit: (roomId: string, roomData: any) => void;
diff --git a/www/app/(app)/rooms/_components/RoomTable.tsx b/www/app/(app)/rooms/_components/RoomTable.tsx
index 93d05b61..113eca7f 100644
--- a/www/app/(app)/rooms/_components/RoomTable.tsx
+++ b/www/app/(app)/rooms/_components/RoomTable.tsx
@@ -9,11 +9,13 @@ import {
Spinner,
} from "@chakra-ui/react";
import { LuLink } from "react-icons/lu";
-import { RoomDetails } from "../../../api";
+import type { components } from "../../../reflector-api";
+
+type Room = components["schemas"]["Room"];
import { RoomActionsMenu } from "./RoomActionsMenu";
interface RoomTableProps {
- rooms: RoomDetails[];
+ rooms: Room[];
linkCopied: string;
onCopyUrl: (roomName: string) => void;
onEdit: (roomId: string, roomData: any) => void;
diff --git a/www/app/(app)/rooms/page.tsx b/www/app/(app)/rooms/page.tsx
index 33cfa6b3..8b1378df 100644
--- a/www/app/(app)/rooms/page.tsx
+++ b/www/app/(app)/rooms/page.tsx
@@ -15,13 +15,24 @@ import {
createListCollection,
useDisclosure,
} from "@chakra-ui/react";
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import { LuEye, LuEyeOff } from "react-icons/lu";
-import useApi from "../../lib/useApi";
import useRoomList from "./useRoomList";
-import { ApiError, RoomDetails } from "../../api";
+import type { components } from "../../reflector-api";
+import {
+ useRoomCreate,
+ useRoomUpdate,
+ useRoomDelete,
+ useZulipStreams,
+ useZulipTopics,
+ useRoomGet,
+ useRoomTestWebhook,
+} from "../../lib/apiHooks";
import { RoomList } from "./_components/RoomList";
import { PaginationPage } from "../browse/_components/Pagination";
+import { assertExists } from "../../lib/utils";
+
+type Room = components["schemas"]["Room"];
interface SelectOption {
label: string;
@@ -76,66 +87,77 @@ export default function RoomsList() {
const recordingTypeCollection = createListCollection({
items: recordingTypeOptions,
});
- const [room, setRoom] = useState(roomInitialState);
+ const [roomInput, setRoomInput] = useState(
+ null,
+ );
const [isEditing, setIsEditing] = useState(false);
- const [editRoomId, setEditRoomId] = useState("");
- const api = useApi();
- // TODO seems to be no setPage calls
- const [page, setPage] = useState(1);
- const { loading, response, refetch } = useRoomList(PaginationPage(page));
- const [streams, setStreams] = useState([]);
- const [topics, setTopics] = useState([]);
+ const [editRoomId, setEditRoomId] = useState(null);
+ const {
+ loading,
+ response,
+ refetch,
+ error: roomListError,
+ } = useRoomList(PaginationPage(1));
const [nameError, setNameError] = useState("");
const [linkCopied, setLinkCopied] = useState("");
+ const [selectedStreamId, setSelectedStreamId] = useState(null);
const [testingWebhook, setTestingWebhook] = useState(false);
const [webhookTestResult, setWebhookTestResult] = useState(
null,
);
const [showWebhookSecret, setShowWebhookSecret] = useState(false);
- interface Stream {
- stream_id: number;
- name: string;
- }
- interface Topic {
- name: string;
- }
+ const createRoomMutation = useRoomCreate();
+ const updateRoomMutation = useRoomUpdate();
+ const deleteRoomMutation = useRoomDelete();
+ const { data: streams = [] } = useZulipStreams();
+ const { data: topics = [] } = useZulipTopics(selectedStreamId);
+ const {
+ data: detailedEditedRoom,
+ isLoading: isDetailedEditedRoomLoading,
+ error: detailedEditedRoomError,
+ } = useRoomGet(editRoomId);
+
+ const error = roomListError || detailedEditedRoomError;
+
+ // room being edited, as fetched from the server
+ const editedRoom: typeof roomInitialState | null = useMemo(
+ () =>
+ detailedEditedRoom
+ ? {
+ name: detailedEditedRoom.name,
+ zulipAutoPost: detailedEditedRoom.zulip_auto_post,
+ zulipStream: detailedEditedRoom.zulip_stream,
+ zulipTopic: detailedEditedRoom.zulip_topic,
+ isLocked: detailedEditedRoom.is_locked,
+ roomMode: detailedEditedRoom.room_mode,
+ recordingType: detailedEditedRoom.recording_type,
+ recordingTrigger: detailedEditedRoom.recording_trigger,
+ isShared: detailedEditedRoom.is_shared,
+ webhookUrl: detailedEditedRoom.webhook_url || "",
+ webhookSecret: detailedEditedRoom.webhook_secret || "",
+ }
+ : null,
+ [detailedEditedRoom],
+ );
+
+ // a room input value or a last api room state
+ const room = roomInput || editedRoom || roomInitialState;
+
+ const roomTestWebhookMutation = useRoomTestWebhook();
+
+ // Update selected stream ID when zulip stream changes
useEffect(() => {
- const fetchZulipStreams = async () => {
- if (!api) return;
-
- try {
- const response = await api.v1ZulipGetStreams();
- setStreams(response);
- } catch (error) {
- console.error("Error fetching Zulip streams:", error);
+ if (room.zulipStream && streams.length > 0) {
+ const selectedStream = streams.find((s) => s.name === room.zulipStream);
+ if (selectedStream !== undefined) {
+ setSelectedStreamId(selectedStream.stream_id);
}
- };
-
- if (room.zulipAutoPost) {
- fetchZulipStreams();
+ } else {
+ setSelectedStreamId(null);
}
- }, [room.zulipAutoPost, !api]);
-
- useEffect(() => {
- const fetchZulipTopics = async () => {
- if (!api || !room.zulipStream) return;
- try {
- const selectedStream = streams.find((s) => s.name === room.zulipStream);
- if (selectedStream) {
- const response = await api.v1ZulipGetTopics({
- streamId: selectedStream.stream_id,
- });
- setTopics(response);
- }
- } catch (error) {
- console.error("Error fetching Zulip topics:", error);
- }
- };
-
- fetchZulipTopics();
- }, [room.zulipStream, streams, api]);
+ }, [room.zulipStream, streams]);
const streamOptions: SelectOption[] = streams.map((stream) => {
return { label: stream.name, value: stream.name };
@@ -167,35 +189,42 @@ export default function RoomsList() {
const handleCloseDialog = () => {
setShowWebhookSecret(false);
setWebhookTestResult(null);
+ setEditRoomId(null);
onClose();
};
const handleTestWebhook = async () => {
- if (!room.webhookUrl || !editRoomId) {
+ if (!room.webhookUrl) {
setWebhookTestResult("Please enter a webhook URL first");
return;
}
+ if (!editRoomId) {
+ console.error("No room ID to test webhook");
+ return;
+ }
setTestingWebhook(true);
setWebhookTestResult(null);
try {
- const response = await api?.v1RoomsTestWebhook({
- roomId: editRoomId,
+ const response = await roomTestWebhookMutation.mutateAsync({
+ params: {
+ path: {
+ room_id: editRoomId,
+ },
+ },
});
- if (response?.success) {
+ if (response.success) {
setWebhookTestResult(
`✅ Webhook test successful! Status: ${response.status_code}`,
);
} else {
let errorMsg = `❌ Webhook test failed`;
- if (response?.status_code) {
- errorMsg += ` (Status: ${response.status_code})`;
- }
- if (response?.error) {
+ errorMsg += ` (Status: ${response.status_code})`;
+ if (response.error) {
errorMsg += `: ${response.error}`;
- } else if (response?.response_preview) {
+ } else if (response.response_preview) {
// Try to parse and extract meaningful error from response
// Specific to N8N at the moment, as there is no specification for that
// We could just display as is, but decided here to dig a little bit more.
@@ -249,27 +278,29 @@ export default function RoomsList() {
};
if (isEditing) {
- await api?.v1RoomsUpdate({
- roomId: editRoomId,
- requestBody: roomData,
+ await updateRoomMutation.mutateAsync({
+ params: {
+ path: { room_id: assertExists(editRoomId) },
+ },
+ body: roomData,
});
} else {
- await api?.v1RoomsCreate({
- requestBody: roomData,
+ await createRoomMutation.mutateAsync({
+ body: roomData,
});
}
- setRoom(roomInitialState);
+ setRoomInput(null);
setIsEditing(false);
setEditRoomId("");
setNameError("");
refetch();
+ onClose();
handleCloseDialog();
- } catch (err) {
+ } catch (err: any) {
if (
- err instanceof ApiError &&
- err.status === 400 &&
- (err.body as any).detail == "Room name is not unique"
+ err?.status === 400 &&
+ err?.body?.detail == "Room name is not unique"
) {
setNameError(
"This room name is already taken. Please choose a different name.",
@@ -280,46 +311,11 @@ export default function RoomsList() {
}
};
- const handleEditRoom = async (roomId, roomData) => {
+ const handleEditRoom = async (roomId: string, roomData) => {
// Reset states
setShowWebhookSecret(false);
setWebhookTestResult(null);
- // Fetch full room details to get webhook fields
- try {
- const detailedRoom = await api?.v1RoomsGet({ roomId });
- if (detailedRoom) {
- setRoom({
- name: detailedRoom.name,
- zulipAutoPost: detailedRoom.zulip_auto_post,
- zulipStream: detailedRoom.zulip_stream,
- zulipTopic: detailedRoom.zulip_topic,
- isLocked: detailedRoom.is_locked,
- roomMode: detailedRoom.room_mode,
- recordingType: detailedRoom.recording_type,
- recordingTrigger: detailedRoom.recording_trigger,
- isShared: detailedRoom.is_shared,
- webhookUrl: detailedRoom.webhook_url || "",
- webhookSecret: detailedRoom.webhook_secret || "",
- });
- }
- } catch (error) {
- console.error("Failed to fetch room details, using list data:", error);
- // Fallback to using the data from the list
- setRoom({
- name: roomData.name,
- zulipAutoPost: roomData.zulip_auto_post,
- zulipStream: roomData.zulip_stream,
- zulipTopic: roomData.zulip_topic,
- isLocked: roomData.is_locked,
- roomMode: roomData.room_mode,
- recordingType: roomData.recording_type,
- recordingTrigger: roomData.recording_trigger,
- isShared: roomData.is_shared,
- webhookUrl: roomData.webhook_url || "",
- webhookSecret: roomData.webhook_secret || "",
- });
- }
setEditRoomId(roomId);
setIsEditing(true);
setNameError("");
@@ -328,8 +324,10 @@ export default function RoomsList() {
const handleDeleteRoom = async (roomId: string) => {
try {
- await api?.v1RoomsDelete({
- roomId,
+ await deleteRoomMutation.mutateAsync({
+ params: {
+ path: { room_id: roomId },
+ },
});
refetch();
} catch (err) {
@@ -346,15 +344,15 @@ export default function RoomsList() {
.toLowerCase();
setNameError("");
}
- setRoom({
+ setRoomInput({
...room,
[name]: type === "checkbox" ? checked : value,
});
};
- const myRooms: RoomDetails[] =
+ const myRooms: Room[] =
response?.items.filter((roomData) => !roomData.is_shared) || [];
- const sharedRooms: RoomDetails[] =
+ const sharedRooms: Room[] =
response?.items.filter((roomData) => roomData.is_shared) || [];
if (loading && !response)
@@ -369,6 +367,9 @@ export default function RoomsList() {
);
+ if (roomListError)
+ return {`${roomListError.name}: ${roomListError.message}`}
;
+
return (
{
setIsEditing(false);
- setRoom(roomInitialState);
+ setRoomInput(null);
setNameError("");
setShowWebhookSecret(false);
setWebhookTestResult(null);
@@ -456,7 +457,7 @@ export default function RoomsList() {
- setRoom({ ...room, roomMode: e.value[0] })
+ setRoomInput({ ...room, roomMode: e.value[0] })
}
collection={roomModeCollection}
>
@@ -486,7 +487,7 @@ export default function RoomsList() {
- setRoom({
+ setRoomInput({
...room,
recordingType: e.value[0],
recordingTrigger:
@@ -521,7 +522,7 @@ export default function RoomsList() {
- setRoom({ ...room, recordingTrigger: e.value[0] })
+ setRoomInput({ ...room, recordingTrigger: e.value[0] })
}
collection={recordingTriggerCollection}
disabled={room.recordingType !== "cloud"}
@@ -576,7 +577,7 @@ export default function RoomsList() {
- setRoom({
+ setRoomInput({
...room,
zulipStream: e.value[0],
zulipTopic: "",
@@ -611,7 +612,7 @@ export default function RoomsList() {
- setRoom({ ...room, zulipTopic: e.value[0] })
+ setRoomInput({ ...room, zulipTopic: e.value[0] })
}
collection={topicCollection}
disabled={!room.zulipAutoPost}
diff --git a/www/app/(app)/rooms/useRoomList.tsx b/www/app/(app)/rooms/useRoomList.tsx
index c1021ade..e8d11250 100644
--- a/www/app/(app)/rooms/useRoomList.tsx
+++ b/www/app/(app)/rooms/useRoomList.tsx
@@ -1,48 +1,27 @@
-import { useEffect, useState } from "react";
-import { useError } from "../../(errors)/errorContext";
-import useApi from "../../lib/useApi";
-import { Page_RoomDetails_ } from "../../api";
+import { useRoomsList } from "../../lib/apiHooks";
+import type { components } from "../../reflector-api";
+
+type Page_Room_ = components["schemas"]["Page_RoomDetails_"];
import { PaginationPage } from "../browse/_components/Pagination";
type RoomList = {
- response: Page_RoomDetails_ | null;
+ response: Page_Room_ | null;
loading: boolean;
error: Error | null;
refetch: () => void;
};
-//always protected
+// Wrapper to maintain backward compatibility
const useRoomList = (page: PaginationPage): 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);
+ const { data, isLoading, error, refetch } = useRoomsList(page);
+ return {
+ response: data || null,
+ loading: isLoading,
+ error: error
+ ? new Error(error.detail ? JSON.stringify(error.detail) : undefined)
+ : null,
+ refetch,
};
-
- 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/(app)/transcripts/[transcriptId]/correct/page.tsx b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
index 9eff7b60..c885ca6e 100644
--- a/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
@@ -6,9 +6,10 @@ import TopicPlayer from "./topicPlayer";
import useParticipants from "../../useParticipants";
import useTopicWithWords from "../../useTopicWithWords";
import ParticipantList from "./participantList";
-import { GetTranscriptTopic } from "../../../../api";
+import type { components } from "../../../../reflector-api";
+type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
import { SelectedText, selectedTextIsTimeSlice } from "./types";
-import useApi from "../../../../lib/useApi";
+import { useTranscriptUpdate } from "../../../../lib/apiHooks";
import useTranscript from "../../useTranscript";
import { useError } from "../../../../(errors)/errorContext";
import { useRouter } from "next/navigation";
@@ -23,7 +24,7 @@ export type TranscriptCorrect = {
export default function TranscriptCorrect({
params: { transcriptId },
}: TranscriptCorrect) {
- const api = useApi();
+ const updateTranscriptMutation = useTranscriptUpdate();
const transcript = useTranscript(transcriptId);
const stateCurrentTopic = useState();
const [currentTopic, _sct] = stateCurrentTopic;
@@ -34,16 +35,21 @@ export default function TranscriptCorrect({
const { setError } = useError();
const router = useRouter();
- const markAsDone = () => {
+ const markAsDone = async () => {
if (transcript.response && !transcript.response.reviewed) {
- api
- ?.v1TranscriptUpdate({ transcriptId, requestBody: { reviewed: true } })
- .then(() => {
- router.push(`/transcripts/${transcriptId}`);
- })
- .catch((e) => {
- setError(e, "Error marking as done");
+ try {
+ await updateTranscriptMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: { reviewed: true },
});
+ router.push(`/transcripts/${transcriptId}`);
+ } catch (e) {
+ setError(e as Error, "Error marking as done");
+ }
}
};
diff --git a/www/app/(app)/transcripts/[transcriptId]/correct/participantList.tsx b/www/app/(app)/transcripts/[transcriptId]/correct/participantList.tsx
index e9297c4b..7c60ea54 100644
--- a/www/app/(app)/transcripts/[transcriptId]/correct/participantList.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/correct/participantList.tsx
@@ -1,8 +1,15 @@
import { faArrowTurnDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ChangeEvent, useEffect, useRef, useState } from "react";
-import { Participant } from "../../../../api";
-import useApi from "../../../../lib/useApi";
+import type { components } from "../../../../reflector-api";
+type Participant = components["schemas"]["Participant"];
+import {
+ useTranscriptSpeakerAssign,
+ useTranscriptSpeakerMerge,
+ useTranscriptParticipantUpdate,
+ useTranscriptParticipantCreate,
+ useTranscriptParticipantDelete,
+} from "../../../../lib/apiHooks";
import { UseParticipants } from "../../useParticipants";
import { selectedTextIsSpeaker, selectedTextIsTimeSlice } from "./types";
import { useError } from "../../../../(errors)/errorContext";
@@ -30,9 +37,19 @@ const ParticipantList = ({
topicWithWords,
stateSelectedText,
}: ParticipantList) => {
- const api = useApi();
const { setError } = useError();
- const [loading, setLoading] = useState(false);
+ const speakerAssignMutation = useTranscriptSpeakerAssign();
+ const speakerMergeMutation = useTranscriptSpeakerMerge();
+ const participantUpdateMutation = useTranscriptParticipantUpdate();
+ const participantCreateMutation = useTranscriptParticipantCreate();
+ const participantDeleteMutation = useTranscriptParticipantDelete();
+
+ const loading =
+ speakerAssignMutation.isPending ||
+ speakerMergeMutation.isPending ||
+ participantUpdateMutation.isPending ||
+ participantCreateMutation.isPending ||
+ participantDeleteMutation.isPending;
const [participantInput, setParticipantInput] = useState("");
const inputRef = useRef(null);
const [selectedText, setSelectedText] = stateSelectedText;
@@ -103,7 +120,6 @@ const ParticipantList = ({
const onSuccess = () => {
topicWithWords.refetch();
participants.refetch();
- setLoading(false);
setAction(null);
setSelectedText(undefined);
setSelectedParticipant(undefined);
@@ -120,11 +136,14 @@ const ParticipantList = ({
if (loading || participants.loading || topicWithWords.loading) return;
if (!selectedTextIsTimeSlice(selectedText)) return;
- setLoading(true);
try {
- await api?.v1TranscriptAssignSpeaker({
- transcriptId,
- requestBody: {
+ await speakerAssignMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
participant: participant.id,
timestamp_from: selectedText.start,
timestamp_to: selectedText.end,
@@ -132,8 +151,7 @@ const ParticipantList = ({
});
onSuccess();
} catch (error) {
- setError(error, "There was an error assigning");
- setLoading(false);
+ setError(error as Error, "There was an error assigning");
throw error;
}
};
@@ -141,32 +159,38 @@ const ParticipantList = ({
const mergeSpeaker =
(speakerFrom, participantTo: Participant) => async () => {
if (loading || participants.loading || topicWithWords.loading) return;
- setLoading(true);
+
if (participantTo.speaker) {
try {
- await api?.v1TranscriptMergeSpeaker({
- transcriptId,
- requestBody: {
+ await speakerMergeMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
speaker_from: speakerFrom,
speaker_to: participantTo.speaker,
},
});
onSuccess();
} catch (error) {
- setError(error, "There was an error merging");
- setLoading(false);
+ setError(error as Error, "There was an error merging");
}
} else {
try {
- await api?.v1TranscriptUpdateParticipant({
- transcriptId,
- participantId: participantTo.id,
- requestBody: { speaker: speakerFrom },
+ await participantUpdateMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ participant_id: participantTo.id,
+ },
+ },
+ body: { speaker: speakerFrom },
});
onSuccess();
} catch (error) {
- setError(error, "There was an error merging (update)");
- setLoading(false);
+ setError(error as Error, "There was an error merging (update)");
}
}
};
@@ -186,105 +210,106 @@ const ParticipantList = ({
(p) => p.speaker == selectedText,
);
if (participant && participant.name !== participantInput) {
- setLoading(true);
- api
- ?.v1TranscriptUpdateParticipant({
- transcriptId,
- participantId: participant.id,
- requestBody: {
+ try {
+ await participantUpdateMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ participant_id: participant.id,
+ },
+ },
+ body: {
name: participantInput,
},
- })
- .then(() => {
- participants.refetch();
- setLoading(false);
- setAction(null);
- })
- .catch((e) => {
- setError(e, "There was an error renaming");
- setLoading(false);
});
+ participants.refetch();
+ setAction(null);
+ } catch (e) {
+ setError(e as Error, "There was an error renaming");
+ }
}
} else if (
action == "Create to rename" &&
selectedTextIsSpeaker(selectedText)
) {
- setLoading(true);
- api
- ?.v1TranscriptAddParticipant({
- transcriptId,
- requestBody: {
+ try {
+ await participantCreateMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
name: participantInput,
speaker: selectedText,
},
- })
- .then(() => {
- participants.refetch();
- setParticipantInput("");
- setOneMatch(undefined);
- setLoading(false);
- })
- .catch((e) => {
- setError(e, "There was an error creating");
- setLoading(false);
});
+ participants.refetch();
+ setParticipantInput("");
+ setOneMatch(undefined);
+ } catch (e) {
+ setError(e as Error, "There was an error creating");
+ }
} else if (
action == "Create and assign" &&
selectedTextIsTimeSlice(selectedText)
) {
- setLoading(true);
try {
- const participant = await api?.v1TranscriptAddParticipant({
- transcriptId,
- requestBody: {
+ const participant = await participantCreateMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
name: participantInput,
},
});
- setLoading(false);
assignTo(participant)().catch(() => {
// error and loading are handled by assignTo catch
participants.refetch();
});
} catch (error) {
- setError(e, "There was an error creating");
- setLoading(false);
+ setError(error as Error, "There was an error creating");
}
} else if (action == "Create") {
- setLoading(true);
- api
- ?.v1TranscriptAddParticipant({
- transcriptId,
- requestBody: {
+ try {
+ await participantCreateMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
name: participantInput,
},
- })
- .then(() => {
- participants.refetch();
- setParticipantInput("");
- setLoading(false);
- inputRef.current?.focus();
- })
- .catch((e) => {
- setError(e, "There was an error creating");
- setLoading(false);
});
+ participants.refetch();
+ setParticipantInput("");
+ inputRef.current?.focus();
+ } catch (e) {
+ setError(e as Error, "There was an error creating");
+ }
}
};
- const deleteParticipant = (participantId) => (e) => {
+ const deleteParticipant = (participantId) => async (e) => {
e.stopPropagation();
if (loading || participants.loading || topicWithWords.loading) return;
- setLoading(true);
- api
- ?.v1TranscriptDeleteParticipant({ transcriptId, participantId })
- .then(() => {
- participants.refetch();
- setLoading(false);
- })
- .catch((e) => {
- setError(e, "There was an error deleting");
- setLoading(false);
+
+ try {
+ await participantDeleteMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ participant_id: participantId,
+ },
+ },
});
+ participants.refetch();
+ } catch (e) {
+ setError(e as Error, "There was an error deleting");
+ }
};
const selectParticipant = (participant) => (e) => {
diff --git a/www/app/(app)/transcripts/[transcriptId]/correct/topicHeader.tsx b/www/app/(app)/transcripts/[transcriptId]/correct/topicHeader.tsx
index 1448de80..494d2929 100644
--- a/www/app/(app)/transcripts/[transcriptId]/correct/topicHeader.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/correct/topicHeader.tsx
@@ -1,6 +1,7 @@
import useTopics from "../../useTopics";
import { Dispatch, SetStateAction, useEffect } from "react";
-import { GetTranscriptTopic } from "../../../../api";
+import type { components } from "../../../../reflector-api";
+type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
import {
BoxProps,
Box,
diff --git a/www/app/(app)/transcripts/[transcriptId]/finalSummary.tsx b/www/app/(app)/transcripts/[transcriptId]/finalSummary.tsx
index 4ce4a9e1..b1f61d43 100644
--- a/www/app/(app)/transcripts/[transcriptId]/finalSummary.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/finalSummary.tsx
@@ -2,12 +2,10 @@ import { useEffect, useRef, useState } from "react";
import React from "react";
import Markdown from "react-markdown";
import "../../../styles/markdown.css";
-import {
- GetTranscript,
- GetTranscriptTopic,
- UpdateTranscript,
-} from "../../../api";
-import useApi from "../../../lib/useApi";
+import type { components } from "../../../reflector-api";
+type GetTranscript = components["schemas"]["GetTranscript"];
+type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
+import { useTranscriptUpdate } from "../../../lib/apiHooks";
import {
Flex,
Heading,
@@ -33,9 +31,8 @@ export default function FinalSummary(props: FinalSummaryProps) {
const [preEditSummary, setPreEditSummary] = useState("");
const [editedSummary, setEditedSummary] = useState("");
- const api = useApi();
-
const { setError } = useError();
+ const updateTranscriptMutation = useTranscriptUpdate();
useEffect(() => {
setEditedSummary(props.transcriptResponse?.long_summary || "");
@@ -47,12 +44,15 @@ export default function FinalSummary(props: FinalSummaryProps) {
const updateSummary = async (newSummary: string, transcriptId: string) => {
try {
- const requestBody: UpdateTranscript = {
- long_summary: newSummary,
- };
- const updatedTranscript = await api?.v1TranscriptUpdate({
- transcriptId,
- requestBody,
+ const updatedTranscript = await updateTranscriptMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: {
+ long_summary: newSummary,
+ },
});
if (props.onUpdate) {
props.onUpdate(newSummary);
@@ -60,7 +60,7 @@ export default function FinalSummary(props: FinalSummaryProps) {
console.log("Updated long summary:", updatedTranscript);
} catch (err) {
console.error("Failed to update long summary:", err);
- setError(err, "Failed to update long summary.");
+ setError(err as Error, "Failed to update long summary.");
}
};
@@ -114,7 +114,12 @@ export default function FinalSummary(props: FinalSummaryProps) {
-
+
)}
{!isEditMode && (
diff --git a/www/app/(app)/transcripts/[transcriptId]/page.tsx b/www/app/(app)/transcripts/[transcriptId]/page.tsx
index 0a2dba47..ce48e951 100644
--- a/www/app/(app)/transcripts/[transcriptId]/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/page.tsx
@@ -86,7 +86,7 @@ export default function TranscriptDetails(details: TranscriptDetails) {
useActiveTopic={useActiveTopic}
waveform={waveform.waveform}
media={mp3.media}
- mediaDuration={transcript.response.duration}
+ mediaDuration={transcript.response?.duration || null}
/>
) : !mp3.loading && (waveform.error || mp3.error) ? (
@@ -116,7 +116,7 @@ export default function TranscriptDetails(details: TranscriptDetails) {
{
transcript.reload();
diff --git a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
index 3a13052e..567272ff 100644
--- a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
@@ -24,10 +24,16 @@ const TranscriptUpload = (details: TranscriptUpload) => {
const router = useRouter();
- const [status, setStatus] = useState(
+ const [status_, setStatus] = useState(
webSockets.status.value || transcript.response?.status || "idle",
);
+ // status is obviously done if we have transcript
+ const status =
+ !transcript.loading && transcript.response?.status === "ended"
+ ? transcript.response?.status
+ : status_;
+
useEffect(() => {
if (!transcriptStarted && webSockets.transcriptTextLive.length !== 0)
setTranscriptStarted(true);
@@ -35,8 +41,11 @@ const TranscriptUpload = (details: TranscriptUpload) => {
useEffect(() => {
//TODO HANDLE ERROR STATUS BETTER
+ // TODO deprecate webSockets.status.value / depend on transcript.response?.status from query lib
const newStatus =
- webSockets.status.value || transcript.response?.status || "idle";
+ transcript.response?.status === "ended"
+ ? "ended"
+ : webSockets.status.value || transcript.response?.status || "idle";
setStatus(newStatus);
if (newStatus && (newStatus == "ended" || newStatus == "error")) {
console.log(newStatus, "redirecting");
diff --git a/www/app/(app)/transcripts/createTranscript.ts b/www/app/(app)/transcripts/createTranscript.ts
index 015c82de..8a235161 100644
--- a/www/app/(app)/transcripts/createTranscript.ts
+++ b/www/app/(app)/transcripts/createTranscript.ts
@@ -1,45 +1,33 @@
-import { useEffect, useState } from "react";
+import type { components } from "../../reflector-api";
+import { useTranscriptCreate } from "../../lib/apiHooks";
-import { useError } from "../../(errors)/errorContext";
-import { CreateTranscript, GetTranscript } from "../../api";
-import useApi from "../../lib/useApi";
+type CreateTranscript = components["schemas"]["CreateTranscript"];
+type GetTranscript = components["schemas"]["GetTranscript"];
type UseCreateTranscript = {
transcript: GetTranscript | null;
loading: boolean;
error: Error | null;
- create: (transcriptCreationDetails: CreateTranscript) => void;
+ create: (transcriptCreationDetails: CreateTranscript) => Promise;
};
const useCreateTranscript = (): UseCreateTranscript => {
- const [transcript, setTranscript] = useState(null);
- const [loading, setLoading] = useState(false);
- const [error, setErrorState] = useState(null);
- const { setError } = useError();
- const api = useApi();
+ const createMutation = useTranscriptCreate();
- const create = (transcriptCreationDetails: CreateTranscript) => {
- if (loading || !api) return;
+ const create = async (transcriptCreationDetails: CreateTranscript) => {
+ if (createMutation.isPending) return;
- setLoading(true);
-
- api
- .v1TranscriptsCreate({ 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);
- });
+ await createMutation.mutateAsync({
+ body: transcriptCreationDetails,
+ });
};
- return { transcript, loading, error, create };
+ return {
+ transcript: createMutation.data || null,
+ loading: createMutation.isPending,
+ error: createMutation.error as Error | null,
+ create,
+ };
};
export default useCreateTranscript;
diff --git a/www/app/(app)/transcripts/fileUploadButton.tsx b/www/app/(app)/transcripts/fileUploadButton.tsx
index 1b4101e8..1f5d72eb 100644
--- a/www/app/(app)/transcripts/fileUploadButton.tsx
+++ b/www/app/(app)/transcripts/fileUploadButton.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
-import useApi from "../../lib/useApi";
+import { useTranscriptUploadAudio } from "../../lib/apiHooks";
import { Button, Spinner } from "@chakra-ui/react";
+import { useError } from "../../(errors)/errorContext";
type FileUploadButton = {
transcriptId: string;
@@ -8,13 +9,16 @@ type FileUploadButton = {
export default function FileUploadButton(props: FileUploadButton) {
const fileInputRef = React.useRef(null);
- const api = useApi();
+ const uploadMutation = useTranscriptUploadAudio();
+ const { setError } = useError();
const [progress, setProgress] = useState(0);
const triggerFileUpload = () => {
fileInputRef.current?.click();
};
- const handleFileUpload = (event: React.ChangeEvent) => {
+ const handleFileUpload = async (
+ event: React.ChangeEvent,
+ ) => {
const file = event.target.files?.[0];
if (file) {
@@ -24,37 +28,45 @@ export default function FileUploadButton(props: FileUploadButton) {
let start = 0;
let uploadedSize = 0;
- api?.httpRequest.config.interceptors.request.use((request) => {
- request.onUploadProgress = (progressEvent) => {
- const currentProgress = Math.floor(
- ((uploadedSize + progressEvent.loaded) / file.size) * 100,
- );
- setProgress(currentProgress);
- };
- return request;
- });
-
const uploadNextChunk = async () => {
- if (chunkNumber == totalChunks) return;
+ if (chunkNumber == totalChunks) {
+ setProgress(0);
+ return;
+ }
const chunkSize = Math.min(maxChunkSize, file.size - start);
const end = start + chunkSize;
const chunk = file.slice(start, end);
- await api?.v1TranscriptRecordUpload({
- transcriptId: props.transcriptId,
- formData: {
- chunk,
- },
- chunkNumber,
- totalChunks,
- });
+ try {
+ const formData = new FormData();
+ formData.append("chunk", chunk);
- uploadedSize += chunkSize;
- chunkNumber++;
- start = end;
+ await uploadMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: props.transcriptId,
+ },
+ query: {
+ chunk_number: chunkNumber,
+ total_chunks: totalChunks,
+ },
+ },
+ body: formData as any,
+ });
- uploadNextChunk();
+ uploadedSize += chunkSize;
+ const currentProgress = Math.floor((uploadedSize / file.size) * 100);
+ setProgress(currentProgress);
+
+ chunkNumber++;
+ start = end;
+
+ await uploadNextChunk();
+ } catch (error) {
+ setError(error as Error, "Failed to upload file");
+ setProgress(0);
+ }
};
uploadNextChunk();
diff --git a/www/app/(app)/transcripts/new/page.tsx b/www/app/(app)/transcripts/new/page.tsx
index 2670fd39..0410bd97 100644
--- a/www/app/(app)/transcripts/new/page.tsx
+++ b/www/app/(app)/transcripts/new/page.tsx
@@ -7,36 +7,29 @@ import About from "../../../(aboutAndPrivacy)/about";
import Privacy from "../../../(aboutAndPrivacy)/privacy";
import { useRouter } from "next/navigation";
import useCreateTranscript from "../createTranscript";
-import { SourceKind } from "../../../api";
import SelectSearch from "react-select-search";
import { supportedLanguages } from "../../../supportedLanguages";
-import useSessionStatus from "../../../lib/useSessionStatus";
import { featureEnabled } from "../../../domainContext";
-import { signIn } from "next-auth/react";
import {
Flex,
Box,
Spinner,
Heading,
Button,
- Card,
Center,
- Link,
- CardBody,
- Stack,
Text,
- Icon,
- Grid,
- IconButton,
Spacer,
- Menu,
- Tooltip,
- Input,
} from "@chakra-ui/react";
+import { useAuth } from "../../../lib/AuthProvider";
+import type { components } from "../../../reflector-api";
+
const TranscriptCreate = () => {
const isClient = typeof window !== "undefined";
const router = useRouter();
- const { isLoading, isAuthenticated } = useSessionStatus();
+ const auth = useAuth();
+ const isAuthenticated = auth.status === "authenticated";
+ const isAuthRefreshing = auth.status === "refreshing";
+ const isLoading = auth.status === "loading";
const requireLogin = featureEnabled("requireLogin");
const [name, setName] = useState("");
@@ -55,27 +48,31 @@ const TranscriptCreate = () => {
const [loadingUpload, setLoadingUpload] = useState(false);
const getTargetLanguage = () => {
- if (targetLanguage === "NOTRANSLATION") return;
+ if (targetLanguage === "NOTRANSLATION") return undefined;
return targetLanguage;
};
const send = () => {
if (loadingRecord || createTranscript.loading || permissionDenied) return;
setLoadingRecord(true);
+ const targetLang = getTargetLanguage();
createTranscript.create({
name,
- target_language: getTargetLanguage(),
- source_kind: "live" as SourceKind,
+ source_language: "en",
+ target_language: targetLang || "en",
+ source_kind: "live",
});
};
const uploadFile = () => {
if (loadingUpload || createTranscript.loading || permissionDenied) return;
setLoadingUpload(true);
+ const targetLang = getTargetLanguage();
createTranscript.create({
name,
- target_language: getTargetLanguage(),
- source_kind: "file" as SourceKind,
+ source_language: "en",
+ target_language: targetLang || "en",
+ source_kind: "file",
});
};
@@ -141,8 +138,8 @@ const TranscriptCreate = () => {
{isLoading ? (
- ) : requireLogin && !isAuthenticated ? (
-
+ ) : requireLogin && !isAuthenticated && !isAuthRefreshing ? (
+
) : (
{
- if (!api)
- throw new Error("ShareLink's API should always be ready at this point");
-
const selectedOption = shareOptionsData.find(
(option) => option.value === selectedValue,
);
@@ -67,19 +66,27 @@ export default function ShareAndPrivacy(props: ShareAndPrivacyProps) {
share_mode: selectedValue as "public" | "semi-private" | "private",
};
- const updatedTranscript = await api.v1TranscriptUpdate({
- transcriptId: props.transcriptResponse.id,
- requestBody,
- });
- setShareMode(
- shareOptionsData.find(
- (option) => option.value === updatedTranscript.share_mode,
- ) || shareOptionsData[0],
- );
- setShareLoading(false);
+ try {
+ const updatedTranscript = await updateTranscriptMutation.mutateAsync({
+ params: {
+ path: { transcript_id: props.transcriptResponse.id },
+ },
+ body: requestBody,
+ });
+ setShareMode(
+ shareOptionsData.find(
+ (option) => option.value === updatedTranscript.share_mode,
+ ) || shareOptionsData[0],
+ );
+ } catch (err) {
+ console.error("Failed to update share mode:", err);
+ } finally {
+ setShareLoading(false);
+ }
};
- const userId = useSessionUser().id;
+ const auth = useAuth();
+ const userId = auth.status === "authenticated" ? auth.user?.id : null;
useEffect(() => {
setIsOwner(!!(requireLogin && userId === props.transcriptResponse.user_id));
@@ -124,7 +131,7 @@ export default function ShareAndPrivacy(props: ShareAndPrivacyProps) {
"This transcript is public. Everyone can access it."}
- {isOwner && api && (
+ {isOwner && (
(undefined);
+ const [selectedStreamId, setSelectedStreamId] = useState(null);
const [topic, setTopic] = useState(undefined);
const [includeTopics, setIncludeTopics] = useState(false);
- const [isLoading, setIsLoading] = useState(true);
- const [streams, setStreams] = useState([]);
- const [topics, setTopics] = useState([]);
- const api = useApi();
+
+ const { data: streams = [], isLoading: isLoadingStreams } = useZulipStreams();
+ const { data: topics = [] } = useZulipTopics(selectedStreamId);
+ const postToZulipMutation = useTranscriptPostToZulip();
+
const { contains } = useFilter({ sensitivity: "base" });
- const {
- collection: streamItemsCollection,
- filter: streamItemsFilter,
- set: streamItemsSet,
- } = useListCollection({
- initialItems: [] as { label: string; value: string }[],
- filter: contains,
- });
+ const streamItems = useMemo(() => {
+ return streams.map((stream: Stream) => ({
+ label: stream.name,
+ value: stream.name,
+ }));
+ }, [streams]);
- const {
- collection: topicItemsCollection,
- filter: topicItemsFilter,
- set: topicItemsSet,
- } = useListCollection({
- initialItems: [] as { label: string; value: string }[],
- filter: contains,
- });
+ const topicItems = useMemo(() => {
+ return topics.map(({ name }) => ({
+ label: name,
+ value: name,
+ }));
+ }, [topics]);
+ const { collection: streamItemsCollection, filter: streamItemsFilter } =
+ useListCollection({
+ initialItems: streamItems,
+ filter: contains,
+ });
+
+ const { collection: topicItemsCollection, filter: topicItemsFilter } =
+ useListCollection({
+ initialItems: topicItems,
+ filter: contains,
+ });
+
+ // Update selected stream ID when stream changes
useEffect(() => {
- const fetchZulipStreams = async () => {
- if (!api) return;
-
- try {
- const response = await api.v1ZulipGetStreams();
- setStreams(response);
-
- streamItemsSet(
- response.map((stream) => ({
- label: stream.name,
- value: stream.name,
- })),
- );
-
- setIsLoading(false);
- } catch (error) {
- console.error("Error fetching Zulip streams:", error);
- }
- };
-
- fetchZulipStreams();
- }, [!api]);
-
- useEffect(() => {
- const fetchZulipTopics = async () => {
- if (!api || !stream) return;
- try {
- const selectedStream = streams.find((s) => s.name === stream);
- if (selectedStream) {
- const response = await api.v1ZulipGetTopics({
- streamId: selectedStream.stream_id,
- });
- setTopics(response);
- topicItemsSet(
- response.map((topic) => ({
- label: topic.name,
- value: topic.name,
- })),
- );
- } else {
- topicItemsSet([]);
- }
- } catch (error) {
- console.error("Error fetching Zulip topics:", error);
- }
- };
-
- fetchZulipTopics();
- }, [stream, streams, api]);
+ if (stream && streams) {
+ const selectedStream = streams.find((s: Stream) => s.name === stream);
+ setSelectedStreamId(selectedStream ? selectedStream.stream_id : null);
+ } else {
+ setSelectedStreamId(null);
+ }
+ }, [stream, streams]);
const handleSendToZulip = async () => {
- if (!api || !props.transcriptResponse) return;
+ if (!props.transcriptResponse) return;
if (stream && topic) {
try {
- await api.v1TranscriptPostToZulip({
- transcriptId: props.transcriptResponse.id,
- stream,
- topic,
- includeTopics,
+ await postToZulipMutation.mutateAsync({
+ params: {
+ path: {
+ transcript_id: props.transcriptResponse.id,
+ },
+ query: {
+ stream,
+ topic,
+ include_topics: includeTopics,
+ },
+ },
});
setShowModal(false);
} catch (error) {
- console.log(error);
+ console.error("Error posting to Zulip:", error);
}
}
};
@@ -155,7 +132,7 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
- {isLoading ? (
+ {isLoadingStreams ? (
diff --git a/www/app/(app)/transcripts/transcriptTitle.tsx b/www/app/(app)/transcripts/transcriptTitle.tsx
index 4678818f..72421f48 100644
--- a/www/app/(app)/transcripts/transcriptTitle.tsx
+++ b/www/app/(app)/transcripts/transcriptTitle.tsx
@@ -1,6 +1,8 @@
import { useState } from "react";
-import { UpdateTranscript } from "../../api";
-import useApi from "../../lib/useApi";
+import type { components } from "../../reflector-api";
+
+type UpdateTranscript = components["schemas"]["UpdateTranscript"];
+import { useTranscriptUpdate } from "../../lib/apiHooks";
import { Heading, IconButton, Input, Flex, Spacer } from "@chakra-ui/react";
import { LuPen } from "react-icons/lu";
@@ -14,24 +16,27 @@ const TranscriptTitle = (props: TranscriptTitle) => {
const [displayedTitle, setDisplayedTitle] = useState(props.title);
const [preEditTitle, setPreEditTitle] = useState(props.title);
const [isEditing, setIsEditing] = useState(false);
- const api = useApi();
+ const updateTranscriptMutation = useTranscriptUpdate();
const updateTitle = async (newTitle: string, transcriptId: string) => {
- if (!api) return;
try {
const requestBody: UpdateTranscript = {
title: newTitle,
};
- const updatedTranscript = await api?.v1TranscriptUpdate({
- transcriptId,
- requestBody,
+ await updateTranscriptMutation.mutateAsync({
+ params: {
+ path: { transcript_id: transcriptId },
+ },
+ body: requestBody,
});
if (props.onUpdate) {
props.onUpdate(newTitle);
}
- console.log("Updated transcript:", updatedTranscript);
+ console.log("Updated transcript title:", newTitle);
} catch (err) {
console.error("Failed to update transcript:", err);
+ // Revert title on error
+ setDisplayedTitle(preEditTitle);
}
};
diff --git a/www/app/(app)/transcripts/useMp3.ts b/www/app/(app)/transcripts/useMp3.ts
index 3e8344ad..223a9a4a 100644
--- a/www/app/(app)/transcripts/useMp3.ts
+++ b/www/app/(app)/transcripts/useMp3.ts
@@ -1,6 +1,7 @@
import { useContext, useEffect, useState } from "react";
import { DomainContext } from "../../domainContext";
-import getApi from "../../lib/useApi";
+import { useTranscriptGet } from "../../lib/apiHooks";
+import { useAuth } from "../../lib/AuthProvider";
export type Mp3Response = {
media: HTMLMediaElement | null;
@@ -17,14 +18,17 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
const [audioLoadingError, setAudioLoadingError] = useState(
null,
);
- const [transcriptMetadataLoading, setTranscriptMetadataLoading] =
- useState(true);
- const [transcriptMetadataLoadingError, setTranscriptMetadataLoadingError] =
- useState(null);
const [audioDeleted, setAudioDeleted] = useState(null);
- const api = getApi();
const { api_url } = useContext(DomainContext);
- const accessTokenInfo = api?.httpRequest?.config?.TOKEN;
+ const auth = useAuth();
+ const accessTokenInfo =
+ auth.status === "authenticated" ? auth.accessToken : null;
+
+ const {
+ data: transcript,
+ isLoading: transcriptMetadataLoading,
+ error: transcriptError,
+ } = useTranscriptGet(later ? null : transcriptId);
const [serviceWorker, setServiceWorker] =
useState(null);
@@ -52,72 +56,50 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
}, [navigator.serviceWorker, !serviceWorker, accessTokenInfo]);
useEffect(() => {
- if (!transcriptId || !api || later) return;
+ if (!transcriptId || later || !transcript) return;
let stopped = false;
let audioElement: HTMLAudioElement | null = null;
let handleCanPlay: (() => void) | null = null;
let handleError: (() => void) | null = null;
- setTranscriptMetadataLoading(true);
setAudioLoading(true);
- // First fetch transcript info to check if audio is deleted
- api
- .v1TranscriptGet({ transcriptId })
- .then((transcript) => {
- if (stopped) {
- return;
- }
+ const deleted = transcript.audio_deleted || false;
+ setAudioDeleted(deleted);
- const deleted = transcript.audio_deleted || false;
- setAudioDeleted(deleted);
- setTranscriptMetadataLoadingError(null);
+ if (deleted) {
+ // Audio is deleted, don't attempt to load it
+ setMedia(null);
+ setAudioLoadingError(null);
+ setAudioLoading(false);
+ return;
+ }
- if (deleted) {
- // Audio is deleted, don't attempt to load it
- setMedia(null);
- setAudioLoadingError(null);
- setAudioLoading(false);
- return;
- }
+ // Audio is not deleted, proceed to load it
+ audioElement = document.createElement("audio");
+ audioElement.src = `${api_url}/v1/transcripts/${transcriptId}/audio/mp3`;
+ audioElement.crossOrigin = "anonymous";
+ audioElement.preload = "auto";
- // Audio is not deleted, proceed to load it
- audioElement = document.createElement("audio");
- audioElement.src = `${api_url}/v1/transcripts/${transcriptId}/audio/mp3`;
- audioElement.crossOrigin = "anonymous";
- audioElement.preload = "auto";
+ handleCanPlay = () => {
+ if (stopped) return;
+ setAudioLoading(false);
+ setAudioLoadingError(null);
+ };
- handleCanPlay = () => {
- if (stopped) return;
- setAudioLoading(false);
- setAudioLoadingError(null);
- };
+ handleError = () => {
+ if (stopped) return;
+ setAudioLoading(false);
+ setAudioLoadingError("Failed to load audio");
+ };
- handleError = () => {
- if (stopped) return;
- setAudioLoading(false);
- setAudioLoadingError("Failed to load audio");
- };
+ audioElement.addEventListener("canplay", handleCanPlay);
+ audioElement.addEventListener("error", handleError);
- audioElement.addEventListener("canplay", handleCanPlay);
- audioElement.addEventListener("error", handleError);
-
- if (!stopped) {
- setMedia(audioElement);
- }
- })
- .catch((error) => {
- if (stopped) return;
- console.error("Failed to fetch transcript:", error);
- setAudioDeleted(null);
- setTranscriptMetadataLoadingError(error.message);
- setAudioLoading(false);
- })
- .finally(() => {
- if (stopped) return;
- setTranscriptMetadataLoading(false);
- });
+ if (!stopped) {
+ setMedia(audioElement);
+ }
return () => {
stopped = true;
@@ -128,14 +110,18 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
if (handleError) audioElement.removeEventListener("error", handleError);
}
};
- }, [transcriptId, api, later, api_url]);
+ }, [transcriptId, transcript, later, api_url]);
const getNow = () => {
setLater(false);
};
const loading = audioLoading || transcriptMetadataLoading;
- const error = audioLoadingError || transcriptMetadataLoadingError;
+ const error =
+ audioLoadingError ||
+ (transcriptError
+ ? (transcriptError as any).message || String(transcriptError)
+ : null);
return { media, loading, error, getNow, audioDeleted };
};
diff --git a/www/app/(app)/transcripts/useParticipants.ts b/www/app/(app)/transcripts/useParticipants.ts
index 38f5aa35..a3674597 100644
--- a/www/app/(app)/transcripts/useParticipants.ts
+++ b/www/app/(app)/transcripts/useParticipants.ts
@@ -1,8 +1,6 @@
-import { useEffect, useState } from "react";
-import { Participant } from "../../api";
-import { useError } from "../../(errors)/errorContext";
-import useApi from "../../lib/useApi";
-import { shouldShowError } from "../../lib/errorUtils";
+import type { components } from "../../reflector-api";
+type Participant = components["schemas"]["Participant"];
+import { useTranscriptParticipants } from "../../lib/apiHooks";
type ErrorParticipants = {
error: Error;
@@ -29,46 +27,38 @@ export type UseParticipants = (
) & { refetch: () => void };
const useParticipants = (transcriptId: string): UseParticipants => {
- const [response, setResponse] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setErrorState] = useState(null);
- const { setError } = useError();
- const api = useApi();
- const [count, setCount] = useState(0);
+ const {
+ data: response,
+ isLoading: loading,
+ error,
+ refetch,
+ } = useTranscriptParticipants(transcriptId || null);
- const refetch = () => {
- if (!loading) {
- setCount(count + 1);
- setLoading(true);
- setErrorState(null);
- }
- };
+ // Type-safe return based on state
+ if (error) {
+ return {
+ error: error as Error,
+ loading: false,
+ response: null,
+ refetch,
+ } satisfies ErrorParticipants & { refetch: () => void };
+ }
- useEffect(() => {
- if (!transcriptId || !api) return;
+ if (loading || !response) {
+ return {
+ response: response || null,
+ loading: true,
+ error: null,
+ refetch,
+ } satisfies LoadingParticipants & { refetch: () => void };
+ }
- setLoading(true);
- api
- .v1TranscriptGetParticipants({ transcriptId })
- .then((result) => {
- setResponse(result);
- setLoading(false);
- console.debug("Participants Loaded:", result);
- })
- .catch((error) => {
- const shouldShowHuman = shouldShowError(error);
- if (shouldShowHuman) {
- setError(error, "There was an error loading the participants");
- } else {
- setError(error);
- }
- setErrorState(error);
- setResponse(null);
- setLoading(false);
- });
- }, [transcriptId, !api, count]);
-
- return { response, loading, error, refetch } as UseParticipants;
+ return {
+ response,
+ loading: false,
+ error: null,
+ refetch,
+ } satisfies SuccessParticipants & { refetch: () => void };
};
export default useParticipants;
diff --git a/www/app/(app)/transcripts/useSearchTranscripts.ts b/www/app/(app)/transcripts/useSearchTranscripts.ts
deleted file mode 100644
index 2e6a7311..00000000
--- a/www/app/(app)/transcripts/useSearchTranscripts.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-// this hook is not great, we want to substitute it with a proper state management solution that is also not re-invention
-
-import { useEffect, useRef, useState } from "react";
-import { SearchResult, SourceKind } from "../../api";
-import useApi from "../../lib/useApi";
-import {
- PaginationPage,
- paginationPageTo0Based,
-} from "../browse/_components/Pagination";
-
-interface SearchFilters {
- roomIds: readonly string[] | null;
- sourceKind: SourceKind | null;
-}
-
-const EMPTY_SEARCH_FILTERS: SearchFilters = {
- roomIds: null,
- sourceKind: null,
-};
-
-type UseSearchTranscriptsOptions = {
- pageSize: number;
- page: PaginationPage;
-};
-
-interface UseSearchTranscriptsReturn {
- results: SearchResult[];
- totalCount: number;
- isLoading: boolean;
- error: unknown;
- reload: () => void;
-}
-
-function hashEffectFilters(filters: SearchFilters): string {
- return JSON.stringify(filters);
-}
-
-export function useSearchTranscripts(
- query: string = "",
- filters: SearchFilters = EMPTY_SEARCH_FILTERS,
- options: UseSearchTranscriptsOptions = {
- pageSize: 20,
- page: PaginationPage(1),
- },
-): UseSearchTranscriptsReturn {
- const { pageSize, page } = options;
-
- const [reloadCount, setReloadCount] = useState(0);
-
- const api = useApi();
- const abortControllerRef = useRef();
-
- const [data, setData] = useState<{ results: SearchResult[]; total: number }>({
- results: [],
- total: 0,
- });
- const [error, setError] = useState();
- const [isLoading, setIsLoading] = useState(false);
-
- const filterHash = hashEffectFilters(filters);
-
- useEffect(() => {
- if (!api) {
- setData({ results: [], total: 0 });
- setError(undefined);
- setIsLoading(false);
- return;
- }
-
- if (abortControllerRef.current) {
- abortControllerRef.current.abort();
- }
-
- const abortController = new AbortController();
- abortControllerRef.current = abortController;
-
- const performSearch = async () => {
- setIsLoading(true);
-
- try {
- const response = await api.v1TranscriptsSearch({
- q: query || "",
- limit: pageSize,
- offset: paginationPageTo0Based(page) * pageSize,
- roomId: filters.roomIds?.[0],
- sourceKind: filters.sourceKind || undefined,
- });
-
- if (abortController.signal.aborted) return;
- setData(response);
- setError(undefined);
- } catch (err: unknown) {
- if ((err as Error).name === "AbortError") {
- return;
- }
- if (abortController.signal.aborted) {
- console.error("Aborted search but error", err);
- return;
- }
-
- setError(err);
- } finally {
- if (!abortController.signal.aborted) {
- setIsLoading(false);
- }
- }
- };
-
- performSearch().then(() => {});
-
- return () => {
- abortController.abort();
- };
- }, [api, query, page, filterHash, pageSize, reloadCount]);
-
- return {
- results: data.results,
- totalCount: data.total,
- isLoading,
- error,
- reload: () => setReloadCount(reloadCount + 1),
- };
-}
diff --git a/www/app/(app)/transcripts/useTopicWithWords.ts b/www/app/(app)/transcripts/useTopicWithWords.ts
index 29d0b982..31e184cc 100644
--- a/www/app/(app)/transcripts/useTopicWithWords.ts
+++ b/www/app/(app)/transcripts/useTopicWithWords.ts
@@ -1,9 +1,8 @@
-import { useEffect, useState } from "react";
+import type { components } from "../../reflector-api";
+import { useTranscriptTopicsWithWordsPerSpeaker } from "../../lib/apiHooks";
-import { GetTranscriptTopicWithWordsPerSpeaker } from "../../api";
-import { useError } from "../../(errors)/errorContext";
-import useApi from "../../lib/useApi";
-import { shouldShowError } from "../../lib/errorUtils";
+type GetTranscriptTopicWithWordsPerSpeaker =
+ components["schemas"]["GetTranscriptTopicWithWordsPerSpeaker"];
type ErrorTopicWithWords = {
error: Error;
@@ -33,47 +32,40 @@ const useTopicWithWords = (
topicId: string | undefined,
transcriptId: string,
): UseTopicWithWords => {
- const [response, setResponse] =
- useState(null);
- const [loading, setLoading] = useState(false);
- const [error, setErrorState] = useState(null);
- const { setError } = useError();
- const api = useApi();
+ const {
+ data: response,
+ isLoading: loading,
+ error,
+ refetch,
+ } = useTranscriptTopicsWithWordsPerSpeaker(
+ transcriptId || null,
+ topicId || null,
+ );
- const [count, setCount] = useState(0);
+ if (error) {
+ return {
+ error: error as Error,
+ loading: false,
+ response: null,
+ refetch,
+ } satisfies ErrorTopicWithWords & { refetch: () => void };
+ }
- const refetch = () => {
- if (!loading) {
- setCount(count + 1);
- setLoading(true);
- setErrorState(null);
- }
- };
+ if (loading || !response) {
+ return {
+ response: response || null,
+ loading: true,
+ error: false,
+ refetch,
+ } satisfies LoadingTopicWithWords & { refetch: () => void };
+ }
- useEffect(() => {
- if (!transcriptId || !topicId || !api) return;
-
- setLoading(true);
-
- api
- .v1TranscriptGetTopicsWithWordsPerSpeaker({ transcriptId, topicId })
- .then((result) => {
- setResponse(result);
- setLoading(false);
- console.debug("Topics with words Loaded:", result);
- })
- .catch((error) => {
- const shouldShowHuman = shouldShowError(error);
- if (shouldShowHuman) {
- setError(error, "There was an error loading the topics with words");
- } else {
- setError(error);
- }
- setErrorState(error);
- });
- }, [transcriptId, !api, topicId, count]);
-
- return { response, loading, error, refetch } as UseTopicWithWords;
+ return {
+ response,
+ loading: false,
+ error: null,
+ refetch,
+ } satisfies SuccessTopicWithWords & { refetch: () => void };
};
export default useTopicWithWords;
diff --git a/www/app/(app)/transcripts/useTopics.ts b/www/app/(app)/transcripts/useTopics.ts
index ff17beaf..7f337582 100644
--- a/www/app/(app)/transcripts/useTopics.ts
+++ b/www/app/(app)/transcripts/useTopics.ts
@@ -1,10 +1,7 @@
-import { useEffect, useState } from "react";
+import { useTranscriptTopics } from "../../lib/apiHooks";
+import type { components } from "../../reflector-api";
-import { useError } from "../../(errors)/errorContext";
-import { Topic } from "./webSocketTypes";
-import useApi from "../../lib/useApi";
-import { shouldShowError } from "../../lib/errorUtils";
-import { GetTranscriptTopic } from "../../api";
+type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
type TranscriptTopics = {
topics: GetTranscriptTopic[] | null;
@@ -13,34 +10,13 @@ type TranscriptTopics = {
};
const useTopics = (id: string): TranscriptTopics => {
- const [topics, setTopics] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setErrorState] = useState(null);
- const { setError } = useError();
- const api = useApi();
- useEffect(() => {
- if (!id || !api) return;
+ const { data: topics, isLoading: loading, error } = useTranscriptTopics(id);
- setLoading(true);
- api
- .v1TranscriptGetTopics({ transcriptId: id })
- .then((result) => {
- setTopics(result);
- setLoading(false);
- console.debug("Transcript topics loaded:", result);
- })
- .catch((err) => {
- setErrorState(err);
- const shouldShowHuman = shouldShowError(err);
- if (shouldShowHuman) {
- setError(err, "There was an error loading the topics");
- } else {
- setError(err);
- }
- });
- }, [id, !api]);
-
- return { topics, loading, error };
+ return {
+ topics: topics || null,
+ loading,
+ error: error as Error | null,
+ };
};
export default useTopics;
diff --git a/www/app/(app)/transcripts/useTranscript.ts b/www/app/(app)/transcripts/useTranscript.ts
index 49d257f0..3e56fb9e 100644
--- a/www/app/(app)/transcripts/useTranscript.ts
+++ b/www/app/(app)/transcripts/useTranscript.ts
@@ -1,8 +1,7 @@
-import { useEffect, useState } from "react";
-import { GetTranscript } from "../../api";
-import { useError } from "../../(errors)/errorContext";
-import { shouldShowError } from "../../lib/errorUtils";
-import useApi from "../../lib/useApi";
+import type { components } from "../../reflector-api";
+import { useTranscriptGet } from "../../lib/apiHooks";
+
+type GetTranscript = components["schemas"]["GetTranscript"];
type ErrorTranscript = {
error: Error;
@@ -28,43 +27,43 @@ type SuccessTranscript = {
const useTranscript = (
id: string | null,
): ErrorTranscript | LoadingTranscript | SuccessTranscript => {
- 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);
+ const { data, isLoading, error, refetch } = useTranscriptGet(id);
- useEffect(() => {
- if (!id || !api) return;
+ // Map to the expected return format
+ if (isLoading) {
+ return {
+ response: null,
+ loading: true,
+ error: false,
+ reload: refetch,
+ };
+ }
- if (!response) {
- setLoading(true);
- }
+ if (error) {
+ return {
+ error: error as Error,
+ loading: false,
+ response: null,
+ reload: refetch,
+ };
+ }
- api
- .v1TranscriptGet({ transcriptId: id })
- .then((result) => {
- setResponse(result);
- setLoading(false);
- console.debug("Transcript Loaded:", result);
- })
- .catch((error) => {
- const shouldShowHuman = shouldShowError(error);
- if (shouldShowHuman) {
- setError(error, "There was an error loading the transcript");
- } else {
- setError(error);
- }
- setErrorState(error);
- });
- }, [id, !api, reload]);
+ // Check if data is undefined or null
+ if (!data) {
+ return {
+ response: null,
+ loading: true,
+ error: false,
+ reload: refetch,
+ };
+ }
- return { response, loading, error, reload: reloadHandler } as
- | ErrorTranscript
- | LoadingTranscript
- | SuccessTranscript;
+ return {
+ response: data,
+ loading: false,
+ error: null,
+ reload: refetch,
+ };
};
export default useTranscript;
diff --git a/www/app/(app)/transcripts/useWaveform.ts b/www/app/(app)/transcripts/useWaveform.ts
index 19b2a265..8bb8c4c9 100644
--- a/www/app/(app)/transcripts/useWaveform.ts
+++ b/www/app/(app)/transcripts/useWaveform.ts
@@ -1,8 +1,7 @@
-import { useEffect, useState } from "react";
-import { AudioWaveform } from "../../api";
-import { useError } from "../../(errors)/errorContext";
-import useApi from "../../lib/useApi";
-import { shouldShowError } from "../../lib/errorUtils";
+import type { components } from "../../reflector-api";
+import { useTranscriptWaveform } from "../../lib/apiHooks";
+
+type AudioWaveform = components["schemas"]["AudioWaveform"];
type AudioWaveFormResponse = {
waveform: AudioWaveform | null;
@@ -11,35 +10,17 @@ type AudioWaveFormResponse = {
};
const useWaveform = (id: string, skip: boolean): AudioWaveFormResponse => {
- const [waveform, setWaveform] = useState(null);
- const [loading, setLoading] = useState(false);
- const [error, setErrorState] = useState(null);
- const { setError } = useError();
- const api = useApi();
+ const {
+ data: waveform,
+ isLoading: loading,
+ error,
+ } = useTranscriptWaveform(skip ? null : id);
- useEffect(() => {
- if (!id || !api || skip) {
- setLoading(false);
- setErrorState(null);
- setWaveform(null);
- return;
- }
- setLoading(true);
- setErrorState(null);
- api
- .v1TranscriptGetAudioWaveform({ transcriptId: id })
- .then((result) => {
- setWaveform(result);
- setLoading(false);
- console.debug("Transcript waveform loaded:", result);
- })
- .catch((err) => {
- setErrorState(err);
- setLoading(false);
- });
- }, [id, api, skip]);
-
- return { waveform, loading, error };
+ return {
+ waveform: waveform || null,
+ loading,
+ error: error as Error | null,
+ };
};
export default useWaveform;
diff --git a/www/app/(app)/transcripts/useWebRTC.ts b/www/app/(app)/transcripts/useWebRTC.ts
index c8370aa4..89a2a946 100644
--- a/www/app/(app)/transcripts/useWebRTC.ts
+++ b/www/app/(app)/transcripts/useWebRTC.ts
@@ -1,8 +1,9 @@
import { useEffect, useState } from "react";
import Peer from "simple-peer";
import { useError } from "../../(errors)/errorContext";
-import useApi from "../../lib/useApi";
-import { RtcOffer } from "../../api";
+import { useTranscriptWebRTC } from "../../lib/apiHooks";
+import type { components } from "../../reflector-api";
+type RtcOffer = components["schemas"]["RtcOffer"];
const useWebRTC = (
stream: MediaStream | null,
@@ -10,10 +11,10 @@ const useWebRTC = (
): Peer => {
const [peer, setPeer] = useState(null);
const { setError } = useError();
- const api = useApi();
+ const { mutateAsync: mutateWebRtcTranscriptAsync } = useTranscriptWebRTC();
useEffect(() => {
- if (!stream || !transcriptId || !api) {
+ if (!stream || !transcriptId) {
return;
}
@@ -24,7 +25,7 @@ const useWebRTC = (
try {
p = new Peer({ initiator: true, stream: stream });
} catch (error) {
- setError(error, "Error creating WebRTC");
+ setError(error as Error, "Error creating WebRTC");
return;
}
@@ -32,26 +33,31 @@ const useWebRTC = (
setError(new Error(`WebRTC error: ${err}`));
});
- p.on("signal", (data: any) => {
- if (!api) return;
+ p.on("signal", async (data: any) => {
if ("sdp" in data) {
const rtcOffer: RtcOffer = {
sdp: data.sdp,
type: data.type,
};
- api
- .v1TranscriptRecordWebrtc({ transcriptId, requestBody: rtcOffer })
- .then((answer) => {
- try {
- p.signal(answer);
- } catch (error) {
- setError(error);
- }
- })
- .catch((error) => {
- setError(error, "Error loading WebRTCOffer");
+ try {
+ const answer = await mutateWebRtcTranscriptAsync({
+ params: {
+ path: {
+ transcript_id: transcriptId,
+ },
+ },
+ body: rtcOffer,
});
+
+ try {
+ p.signal(answer);
+ } catch (error) {
+ setError(error as Error);
+ }
+ } catch (error) {
+ setError(error as Error, "Error loading WebRTCOffer");
+ }
}
});
@@ -63,7 +69,7 @@ const useWebRTC = (
return () => {
p.destroy();
};
- }, [stream, transcriptId, !api]);
+ }, [stream, transcriptId, mutateWebRtcTranscriptAsync]);
return peer;
};
diff --git a/www/app/(app)/transcripts/useWebSockets.ts b/www/app/(app)/transcripts/useWebSockets.ts
index 6fa5edc7..2b3205c4 100644
--- a/www/app/(app)/transcripts/useWebSockets.ts
+++ b/www/app/(app)/transcripts/useWebSockets.ts
@@ -2,8 +2,12 @@ import { useContext, useEffect, useState } from "react";
import { Topic, FinalSummary, Status } from "./webSocketTypes";
import { useError } from "../../(errors)/errorContext";
import { DomainContext } from "../../domainContext";
-import { AudioWaveform, GetTranscriptSegmentTopic } from "../../api";
-import useApi from "../../lib/useApi";
+import type { components } from "../../reflector-api";
+type AudioWaveform = components["schemas"]["AudioWaveform"];
+type GetTranscriptSegmentTopic =
+ components["schemas"]["GetTranscriptSegmentTopic"];
+import { useQueryClient } from "@tanstack/react-query";
+import { $api } from "../../lib/apiClient";
export type UseWebSockets = {
transcriptTextLive: string;
@@ -33,8 +37,8 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
const [status, setStatus] = useState({ value: "" });
const { setError } = useError();
- const { websocket_url } = useContext(DomainContext);
- const api = useApi();
+ const { websocket_url: websocketUrl } = useContext(DomainContext);
+ const queryClient = useQueryClient();
const [accumulatedText, setAccumulatedText] = useState("");
@@ -105,6 +109,13 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
title: "Topic 1: Introduction to Quantum Mechanics",
transcript:
"A brief overview of quantum mechanics and its principles.",
+ segments: [
+ {
+ speaker: 1,
+ start: 0,
+ text: "This is the transcription of an example title",
+ },
+ ],
},
{
id: "2",
@@ -315,11 +326,9 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
}
};
- if (!transcriptId || !api) return;
+ if (!transcriptId) return;
- api?.v1TranscriptGetWebsocketEvents({ transcriptId }).then((result) => {});
-
- const url = `${websocket_url}/v1/transcripts/${transcriptId}/events`;
+ const url = `${websocketUrl}/v1/transcripts/${transcriptId}/events`;
let ws = new WebSocket(url);
ws.onopen = () => {
@@ -361,6 +370,16 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
return [...prevTopics, topic];
});
console.debug("TOPIC event:", message.data);
+ // Invalidate topics query to sync with WebSocket data
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/topics",
+ {
+ params: { path: { transcript_id: transcriptId } },
+ },
+ ).queryKey,
+ });
break;
case "FINAL_SHORT_SUMMARY":
@@ -370,6 +389,16 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
case "FINAL_LONG_SUMMARY":
if (message.data) {
setFinalSummary(message.data);
+ // Invalidate transcript query to sync summary
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: { path: { transcript_id: transcriptId } },
+ },
+ ).queryKey,
+ });
}
break;
@@ -377,6 +406,16 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
console.debug("FINAL_TITLE event:", message.data);
if (message.data) {
setTitle(message.data.title);
+ // Invalidate transcript query to sync title
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: { path: { transcript_id: transcriptId } },
+ },
+ ).queryKey,
+ });
}
break;
@@ -434,6 +473,11 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
break;
case 1001: // Navigate away
break;
+ case 1006: // Closed by client Chrome
+ console.warn(
+ "WebSocket closed by client, likely duplicated connection in react dev mode",
+ );
+ break;
default:
setError(
new Error(`WebSocket closed unexpectedly with code: ${event.code}`),
@@ -450,7 +494,7 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
return () => {
ws.close();
};
- }, [transcriptId, !api]);
+ }, [transcriptId, websocketUrl]);
return {
transcriptTextLive,
diff --git a/www/app/(app)/transcripts/webSocketTypes.ts b/www/app/(app)/transcripts/webSocketTypes.ts
index edd35eb6..4ec98946 100644
--- a/www/app/(app)/transcripts/webSocketTypes.ts
+++ b/www/app/(app)/transcripts/webSocketTypes.ts
@@ -1,4 +1,6 @@
-import { GetTranscriptTopic } from "../../api";
+import type { components } from "../../reflector-api";
+
+type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
export type Topic = GetTranscriptTopic;
diff --git a/www/app/(auth)/userInfo.tsx b/www/app/(auth)/userInfo.tsx
index ffb286b3..bf6a5b62 100644
--- a/www/app/(auth)/userInfo.tsx
+++ b/www/app/(auth)/userInfo.tsx
@@ -1,18 +1,21 @@
"use client";
-import { signOut, signIn } from "next-auth/react";
-import useSessionStatus from "../lib/useSessionStatus";
+
import { Spinner, Link } from "@chakra-ui/react";
+import { useAuth } from "../lib/AuthProvider";
export default function UserInfo() {
- const { isLoading, isAuthenticated } = useSessionStatus();
-
+ const auth = useAuth();
+ const status = auth.status;
+ const isLoading = status === "loading";
+ const isAuthenticated = status === "authenticated";
+ const isRefreshing = status === "refreshing";
return isLoading ? (
- ) : !isAuthenticated ? (
+ ) : !isAuthenticated && !isRefreshing ? (
signIn("authentik")}
+ onClick={() => auth.signIn("authentik")}
>
Log in
@@ -20,7 +23,7 @@ export default function UserInfo() {
signOut({ callbackUrl: "/" })}
+ onClick={() => auth.signOut({ callbackUrl: "/" })}
>
Log out
diff --git a/www/app/[roomName]/page.tsx b/www/app/[roomName]/page.tsx
index b03a7e4f..0130588b 100644
--- a/www/app/[roomName]/page.tsx
+++ b/www/app/[roomName]/page.tsx
@@ -21,11 +21,13 @@ import { toaster } from "../components/ui/toaster";
import useRoomMeeting from "./useRoomMeeting";
import { useRouter } from "next/navigation";
import { notFound } from "next/navigation";
-import useSessionStatus from "../lib/useSessionStatus";
import { useRecordingConsent } from "../recordingConsentContext";
-import useApi from "../lib/useApi";
-import { Meeting } from "../api";
+import { useMeetingAudioConsent } from "../lib/apiHooks";
+import type { components } from "../reflector-api";
+
+type Meeting = components["schemas"]["Meeting"];
import { FaBars } from "react-icons/fa6";
+import { useAuth } from "../lib/AuthProvider";
export type RoomDetails = {
params: {
@@ -76,31 +78,30 @@ const useConsentDialog = (
wherebyRef: RefObject /*accessibility*/,
) => {
const { state: consentState, touch, hasConsent } = useRecordingConsent();
- const [consentLoading, setConsentLoading] = useState(false);
// toast would open duplicates, even with using "id=" prop
const [modalOpen, setModalOpen] = useState(false);
- const api = useApi();
+ const audioConsentMutation = useMeetingAudioConsent();
const handleConsent = useCallback(
async (meetingId: string, given: boolean) => {
- if (!api) return;
-
- setConsentLoading(true);
-
try {
- await api.v1MeetingAudioConsent({
- meetingId,
- requestBody: { consent_given: given },
+ await audioConsentMutation.mutateAsync({
+ params: {
+ path: {
+ meeting_id: meetingId,
+ },
+ },
+ body: {
+ consent_given: given,
+ },
});
touch(meetingId);
} catch (error) {
console.error("Error submitting consent:", error);
- } finally {
- setConsentLoading(false);
}
},
- [api, touch],
+ [audioConsentMutation, touch],
);
const showConsentModal = useCallback(() => {
@@ -194,7 +195,12 @@ const useConsentDialog = (
return cleanup;
}, [meetingId, handleConsent, wherebyRef, modalOpen]);
- return { showConsentModal, consentState, hasConsent, consentLoading };
+ return {
+ showConsentModal,
+ consentState,
+ hasConsent,
+ consentLoading: audioConsentMutation.isPending,
+ };
};
function ConsentDialogButton({
@@ -254,7 +260,9 @@ export default function Room(details: RoomDetails) {
const roomName = details.params.roomName;
const meeting = useRoomMeeting(roomName);
const router = useRouter();
- const { isLoading, isAuthenticated } = useSessionStatus();
+ const status = useAuth().status;
+ const isAuthenticated = status === "authenticated";
+ const isLoading = status === "loading" || meeting.loading;
const roomUrl = meeting?.response?.host_room_url
? meeting?.response?.host_room_url
diff --git a/www/app/[roomName]/useRoomMeeting.tsx b/www/app/[roomName]/useRoomMeeting.tsx
index 98c2f1f2..93491a05 100644
--- a/www/app/[roomName]/useRoomMeeting.tsx
+++ b/www/app/[roomName]/useRoomMeeting.tsx
@@ -1,8 +1,10 @@
import { useEffect, useState } from "react";
import { useError } from "../(errors)/errorContext";
-import { Meeting } from "../api";
+import type { components } from "../reflector-api";
import { shouldShowError } from "../lib/errorUtils";
-import useApi from "../lib/useApi";
+
+type Meeting = components["schemas"]["Meeting"];
+import { useRoomsCreateMeeting } from "../lib/apiHooks";
import { notFound } from "next/navigation";
type ErrorMeeting = {
@@ -30,27 +32,25 @@ 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 createMeetingMutation = useRoomsCreateMeeting();
const reloadHandler = () => setReload((prev) => prev + 1);
useEffect(() => {
- if (!roomName || !api) return;
+ if (!roomName) return;
- if (!response) {
- setLoading(true);
- }
-
- api
- .v1RoomsCreateMeeting({ roomName })
- .then((result) => {
+ const createMeeting = async () => {
+ try {
+ const result = await createMeetingMutation.mutateAsync({
+ params: {
+ path: {
+ room_name: roomName,
+ },
+ },
+ });
setResponse(result);
- setLoading(false);
- })
- .catch((error) => {
+ } catch (error: any) {
const shouldShowHuman = shouldShowError(error);
if (shouldShowHuman && error.status !== 404) {
setError(
@@ -60,9 +60,14 @@ const useRoomMeeting = (
} else {
setError(error);
}
- setErrorState(error);
- });
- }, [roomName, !api, reload]);
+ }
+ };
+
+ createMeeting();
+ }, [roomName, reload]);
+
+ const loading = createMeetingMutation.isPending && !response;
+ const error = createMeetingMutation.error as Error | null;
return { response, loading, error, reload: reloadHandler } as
| ErrorMeeting
diff --git a/www/app/api/OpenApi.ts b/www/app/api/OpenApi.ts
deleted file mode 100644
index 23cc35f3..00000000
--- a/www/app/api/OpenApi.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import type { BaseHttpRequest } from "./core/BaseHttpRequest";
-import type { OpenAPIConfig } from "./core/OpenAPI";
-import { Interceptors } from "./core/OpenAPI";
-import { AxiosHttpRequest } from "./core/AxiosHttpRequest";
-
-import { DefaultService } from "./services.gen";
-
-type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;
-
-export class OpenApi {
- public readonly default: DefaultService;
-
- public readonly request: BaseHttpRequest;
-
- constructor(
- config?: Partial,
- HttpRequest: HttpRequestConstructor = AxiosHttpRequest,
- ) {
- this.request = new HttpRequest({
- BASE: config?.BASE ?? "",
- VERSION: config?.VERSION ?? "0.1.0",
- WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
- CREDENTIALS: config?.CREDENTIALS ?? "include",
- TOKEN: config?.TOKEN,
- USERNAME: config?.USERNAME,
- PASSWORD: config?.PASSWORD,
- HEADERS: config?.HEADERS,
- ENCODE_PATH: config?.ENCODE_PATH,
- interceptors: {
- request: config?.interceptors?.request ?? new Interceptors(),
- response: config?.interceptors?.response ?? new Interceptors(),
- },
- });
-
- this.default = new DefaultService(this.request);
- }
-}
diff --git a/www/app/api/auth/[...nextauth]/route.ts b/www/app/api/auth/[...nextauth]/route.ts
index 915ed04d..7b73c22a 100644
--- a/www/app/api/auth/[...nextauth]/route.ts
+++ b/www/app/api/auth/[...nextauth]/route.ts
@@ -1,8 +1,5 @@
-// NextAuth route handler for Authentik
-// Refresh rotation has been taken from https://next-auth.js.org/v3/tutorials/refresh-token-rotation even if we are using 4.x
-
import NextAuth from "next-auth";
-import { authOptions } from "../../../lib/auth";
+import { authOptions } from "../../../lib/authBackend";
const handler = NextAuth(authOptions);
diff --git a/www/app/api/core/ApiError.ts b/www/app/api/core/ApiError.ts
deleted file mode 100644
index 1d07bb31..00000000
--- a/www/app/api/core/ApiError.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { ApiRequestOptions } from "./ApiRequestOptions";
-import type { ApiResult } from "./ApiResult";
-
-export class ApiError extends Error {
- public readonly url: string;
- public readonly status: number;
- public readonly statusText: string;
- public readonly body: unknown;
- public readonly request: ApiRequestOptions;
-
- constructor(
- request: ApiRequestOptions,
- response: ApiResult,
- message: string,
- ) {
- super(message);
-
- this.name = "ApiError";
- this.url = response.url;
- this.status = response.status;
- this.statusText = response.statusText;
- this.body = response.body;
- this.request = request;
- }
-}
diff --git a/www/app/api/core/ApiRequestOptions.ts b/www/app/api/core/ApiRequestOptions.ts
deleted file mode 100644
index 57fbb095..00000000
--- a/www/app/api/core/ApiRequestOptions.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-export type ApiRequestOptions = {
- readonly method:
- | "GET"
- | "PUT"
- | "POST"
- | "DELETE"
- | "OPTIONS"
- | "HEAD"
- | "PATCH";
- readonly url: string;
- readonly path?: Record;
- readonly cookies?: Record;
- readonly headers?: Record;
- readonly query?: Record;
- readonly formData?: Record;
- readonly body?: any;
- readonly mediaType?: string;
- readonly responseHeader?: string;
- readonly responseTransformer?: (data: unknown) => Promise;
- readonly errors?: Record;
-};
diff --git a/www/app/api/core/ApiResult.ts b/www/app/api/core/ApiResult.ts
deleted file mode 100644
index 05040ba8..00000000
--- a/www/app/api/core/ApiResult.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type ApiResult = {
- readonly body: TData;
- readonly ok: boolean;
- readonly status: number;
- readonly statusText: string;
- readonly url: string;
-};
diff --git a/www/app/api/core/AxiosHttpRequest.ts b/www/app/api/core/AxiosHttpRequest.ts
deleted file mode 100644
index aba5096e..00000000
--- a/www/app/api/core/AxiosHttpRequest.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { ApiRequestOptions } from "./ApiRequestOptions";
-import { BaseHttpRequest } from "./BaseHttpRequest";
-import type { CancelablePromise } from "./CancelablePromise";
-import type { OpenAPIConfig } from "./OpenAPI";
-import { request as __request } from "./request";
-
-export class AxiosHttpRequest extends BaseHttpRequest {
- constructor(config: OpenAPIConfig) {
- super(config);
- }
-
- /**
- * Request method
- * @param options The request options from the service
- * @returns CancelablePromise
- * @throws ApiError
- */
- public override request(
- options: ApiRequestOptions,
- ): CancelablePromise {
- return __request(this.config, options);
- }
-}
diff --git a/www/app/api/core/BaseHttpRequest.ts b/www/app/api/core/BaseHttpRequest.ts
deleted file mode 100644
index 3f89861c..00000000
--- a/www/app/api/core/BaseHttpRequest.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { ApiRequestOptions } from "./ApiRequestOptions";
-import type { CancelablePromise } from "./CancelablePromise";
-import type { OpenAPIConfig } from "./OpenAPI";
-
-export abstract class BaseHttpRequest {
- constructor(public readonly config: OpenAPIConfig) {}
-
- public abstract request(
- options: ApiRequestOptions,
- ): CancelablePromise;
-}
diff --git a/www/app/api/core/CancelablePromise.ts b/www/app/api/core/CancelablePromise.ts
deleted file mode 100644
index 0640e989..00000000
--- a/www/app/api/core/CancelablePromise.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-export class CancelError extends Error {
- constructor(message: string) {
- super(message);
- this.name = "CancelError";
- }
-
- public get isCancelled(): boolean {
- return true;
- }
-}
-
-export interface OnCancel {
- readonly isResolved: boolean;
- readonly isRejected: boolean;
- readonly isCancelled: boolean;
-
- (cancelHandler: () => void): void;
-}
-
-export class CancelablePromise implements Promise {
- private _isResolved: boolean;
- private _isRejected: boolean;
- private _isCancelled: boolean;
- readonly cancelHandlers: (() => void)[];
- readonly promise: Promise;
- private _resolve?: (value: T | PromiseLike) => void;
- private _reject?: (reason?: unknown) => void;
-
- constructor(
- executor: (
- resolve: (value: T | PromiseLike) => void,
- reject: (reason?: unknown) => void,
- onCancel: OnCancel,
- ) => void,
- ) {
- this._isResolved = false;
- this._isRejected = false;
- this._isCancelled = false;
- this.cancelHandlers = [];
- this.promise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
-
- const onResolve = (value: T | PromiseLike): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return;
- }
- this._isResolved = true;
- if (this._resolve) this._resolve(value);
- };
-
- const onReject = (reason?: unknown): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return;
- }
- this._isRejected = true;
- if (this._reject) this._reject(reason);
- };
-
- const onCancel = (cancelHandler: () => void): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return;
- }
- this.cancelHandlers.push(cancelHandler);
- };
-
- Object.defineProperty(onCancel, "isResolved", {
- get: (): boolean => this._isResolved,
- });
-
- Object.defineProperty(onCancel, "isRejected", {
- get: (): boolean => this._isRejected,
- });
-
- Object.defineProperty(onCancel, "isCancelled", {
- get: (): boolean => this._isCancelled,
- });
-
- return executor(onResolve, onReject, onCancel as OnCancel);
- });
- }
-
- get [Symbol.toStringTag]() {
- return "Cancellable Promise";
- }
-
- public then(
- onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null,
- onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null,
- ): Promise {
- return this.promise.then(onFulfilled, onRejected);
- }
-
- public catch(
- onRejected?: ((reason: unknown) => TResult | PromiseLike) | null,
- ): Promise {
- return this.promise.catch(onRejected);
- }
-
- public finally(onFinally?: (() => void) | null): Promise {
- return this.promise.finally(onFinally);
- }
-
- public cancel(): void {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return;
- }
- this._isCancelled = true;
- if (this.cancelHandlers.length) {
- try {
- for (const cancelHandler of this.cancelHandlers) {
- cancelHandler();
- }
- } catch (error) {
- console.warn("Cancellation threw an error", error);
- return;
- }
- }
- this.cancelHandlers.length = 0;
- if (this._reject) this._reject(new CancelError("Request aborted"));
- }
-
- public get isCancelled(): boolean {
- return this._isCancelled;
- }
-}
diff --git a/www/app/api/core/OpenAPI.ts b/www/app/api/core/OpenAPI.ts
deleted file mode 100644
index 20ea0ed9..00000000
--- a/www/app/api/core/OpenAPI.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import type { AxiosRequestConfig, AxiosResponse } from "axios";
-import type { ApiRequestOptions } from "./ApiRequestOptions";
-
-type Headers = Record;
-type Middleware = (value: T) => T | Promise;
-type Resolver = (options: ApiRequestOptions) => Promise;
-
-export class Interceptors {
- _fns: Middleware[];
-
- constructor() {
- this._fns = [];
- }
-
- eject(fn: Middleware): void {
- const index = this._fns.indexOf(fn);
- if (index !== -1) {
- this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
- }
- }
-
- use(fn: Middleware): void {
- this._fns = [...this._fns, fn];
- }
-}
-
-export type OpenAPIConfig = {
- BASE: string;
- CREDENTIALS: "include" | "omit" | "same-origin";
- ENCODE_PATH?: ((path: string) => string) | undefined;
- HEADERS?: Headers | Resolver | undefined;
- PASSWORD?: string | Resolver | undefined;
- TOKEN?: string | Resolver | undefined;
- USERNAME?: string | Resolver | undefined;
- VERSION: string;
- WITH_CREDENTIALS: boolean;
- interceptors: {
- request: Interceptors;
- response: Interceptors;
- };
-};
-
-export const OpenAPI: OpenAPIConfig = {
- BASE: "",
- CREDENTIALS: "include",
- ENCODE_PATH: undefined,
- HEADERS: undefined,
- PASSWORD: undefined,
- TOKEN: undefined,
- USERNAME: undefined,
- VERSION: "0.1.0",
- WITH_CREDENTIALS: false,
- interceptors: {
- request: new Interceptors(),
- response: new Interceptors(),
- },
-};
diff --git a/www/app/api/core/request.ts b/www/app/api/core/request.ts
deleted file mode 100644
index b576207e..00000000
--- a/www/app/api/core/request.ts
+++ /dev/null
@@ -1,387 +0,0 @@
-import axios from "axios";
-import type {
- AxiosError,
- AxiosRequestConfig,
- AxiosResponse,
- AxiosInstance,
-} from "axios";
-
-import { ApiError } from "./ApiError";
-import type { ApiRequestOptions } from "./ApiRequestOptions";
-import type { ApiResult } from "./ApiResult";
-import { CancelablePromise } from "./CancelablePromise";
-import type { OnCancel } from "./CancelablePromise";
-import type { OpenAPIConfig } from "./OpenAPI";
-
-export const isString = (value: unknown): value is string => {
- return typeof value === "string";
-};
-
-export const isStringWithValue = (value: unknown): value is string => {
- return isString(value) && value !== "";
-};
-
-export const isBlob = (value: any): value is Blob => {
- return value instanceof Blob;
-};
-
-export const isFormData = (value: unknown): value is FormData => {
- return value instanceof FormData;
-};
-
-export const isSuccess = (status: number): boolean => {
- return status >= 200 && status < 300;
-};
-
-export const base64 = (str: string): string => {
- try {
- return btoa(str);
- } catch (err) {
- // @ts-ignore
- return Buffer.from(str).toString("base64");
- }
-};
-
-export const getQueryString = (params: Record): string => {
- const qs: string[] = [];
-
- const append = (key: string, value: unknown) => {
- qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
- };
-
- const encodePair = (key: string, value: unknown) => {
- if (value === undefined || value === null) {
- return;
- }
-
- if (value instanceof Date) {
- append(key, value.toISOString());
- } else if (Array.isArray(value)) {
- value.forEach((v) => encodePair(key, v));
- } else if (typeof value === "object") {
- Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v));
- } else {
- append(key, value);
- }
- };
-
- Object.entries(params).forEach(([key, value]) => encodePair(key, value));
-
- return qs.length ? `?${qs.join("&")}` : "";
-};
-
-const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
- const encoder = config.ENCODE_PATH || encodeURI;
-
- const path = options.url
- .replace("{api-version}", config.VERSION)
- .replace(/{(.*?)}/g, (substring: string, group: string) => {
- if (options.path?.hasOwnProperty(group)) {
- return encoder(String(options.path[group]));
- }
- return substring;
- });
-
- const url = config.BASE + path;
- return options.query ? url + getQueryString(options.query) : url;
-};
-
-export const getFormData = (
- options: ApiRequestOptions,
-): FormData | undefined => {
- if (options.formData) {
- const formData = new FormData();
-
- const process = (key: string, value: unknown) => {
- if (isString(value) || isBlob(value)) {
- formData.append(key, value);
- } else {
- formData.append(key, JSON.stringify(value));
- }
- };
-
- Object.entries(options.formData)
- .filter(([, value]) => value !== undefined && value !== null)
- .forEach(([key, value]) => {
- if (Array.isArray(value)) {
- value.forEach((v) => process(key, v));
- } else {
- process(key, value);
- }
- });
-
- return formData;
- }
- return undefined;
-};
-
-type Resolver = (options: ApiRequestOptions) => Promise;
-
-export const resolve = async (
- options: ApiRequestOptions,
- resolver?: T | Resolver,
-): Promise => {
- if (typeof resolver === "function") {
- return (resolver as Resolver)(options);
- }
- return resolver;
-};
-
-export const getHeaders = async (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
-): Promise> => {
- const [token, username, password, additionalHeaders] = await Promise.all([
- // @ts-ignore
- resolve(options, config.TOKEN),
- // @ts-ignore
- resolve(options, config.USERNAME),
- // @ts-ignore
- resolve(options, config.PASSWORD),
- // @ts-ignore
- resolve(options, config.HEADERS),
- ]);
-
- const headers = Object.entries({
- Accept: "application/json",
- ...additionalHeaders,
- ...options.headers,
- })
- .filter(([, value]) => value !== undefined && value !== null)
- .reduce(
- (headers, [key, value]) => ({
- ...headers,
- [key]: String(value),
- }),
- {} as Record,
- );
-
- if (isStringWithValue(token)) {
- headers["Authorization"] = `Bearer ${token}`;
- }
-
- if (isStringWithValue(username) && isStringWithValue(password)) {
- const credentials = base64(`${username}:${password}`);
- headers["Authorization"] = `Basic ${credentials}`;
- }
-
- if (options.body !== undefined) {
- if (options.mediaType) {
- headers["Content-Type"] = options.mediaType;
- } else if (isBlob(options.body)) {
- headers["Content-Type"] = options.body.type || "application/octet-stream";
- } else if (isString(options.body)) {
- headers["Content-Type"] = "text/plain";
- } else if (!isFormData(options.body)) {
- headers["Content-Type"] = "application/json";
- }
- } else if (options.formData !== undefined) {
- if (options.mediaType) {
- headers["Content-Type"] = options.mediaType;
- }
- }
-
- return headers;
-};
-
-export const getRequestBody = (options: ApiRequestOptions): unknown => {
- if (options.body) {
- return options.body;
- }
- return undefined;
-};
-
-export const sendRequest = async (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
- url: string,
- body: unknown,
- formData: FormData | undefined,
- headers: Record,
- onCancel: OnCancel,
- axiosClient: AxiosInstance,
-): Promise> => {
- const controller = new AbortController();
-
- let requestConfig: AxiosRequestConfig = {
- data: body ?? formData,
- headers,
- method: options.method,
- signal: controller.signal,
- url,
- withCredentials: config.WITH_CREDENTIALS,
- };
-
- onCancel(() => controller.abort());
-
- for (const fn of config.interceptors.request._fns) {
- requestConfig = await fn(requestConfig);
- }
-
- try {
- return await axiosClient.request(requestConfig);
- } catch (error) {
- const axiosError = error as AxiosError;
- if (axiosError.response) {
- return axiosError.response;
- }
- throw error;
- }
-};
-
-export const getResponseHeader = (
- response: AxiosResponse,
- responseHeader?: string,
-): string | undefined => {
- if (responseHeader) {
- const content = response.headers[responseHeader];
- if (isString(content)) {
- return content;
- }
- }
- return undefined;
-};
-
-export const getResponseBody = (response: AxiosResponse): unknown => {
- if (response.status !== 204) {
- return response.data;
- }
- return undefined;
-};
-
-export const catchErrorCodes = (
- options: ApiRequestOptions,
- result: ApiResult,
-): void => {
- const errors: Record = {
- 400: "Bad Request",
- 401: "Unauthorized",
- 402: "Payment Required",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 406: "Not Acceptable",
- 407: "Proxy Authentication Required",
- 408: "Request Timeout",
- 409: "Conflict",
- 410: "Gone",
- 411: "Length Required",
- 412: "Precondition Failed",
- 413: "Payload Too Large",
- 414: "URI Too Long",
- 415: "Unsupported Media Type",
- 416: "Range Not Satisfiable",
- 417: "Expectation Failed",
- 418: "Im a teapot",
- 421: "Misdirected Request",
- 422: "Unprocessable Content",
- 423: "Locked",
- 424: "Failed Dependency",
- 425: "Too Early",
- 426: "Upgrade Required",
- 428: "Precondition Required",
- 429: "Too Many Requests",
- 431: "Request Header Fields Too Large",
- 451: "Unavailable For Legal Reasons",
- 500: "Internal Server Error",
- 501: "Not Implemented",
- 502: "Bad Gateway",
- 503: "Service Unavailable",
- 504: "Gateway Timeout",
- 505: "HTTP Version Not Supported",
- 506: "Variant Also Negotiates",
- 507: "Insufficient Storage",
- 508: "Loop Detected",
- 510: "Not Extended",
- 511: "Network Authentication Required",
- ...options.errors,
- };
-
- const error = errors[result.status];
- if (error) {
- throw new ApiError(options, result, error);
- }
-
- if (!result.ok) {
- const errorStatus = result.status ?? "unknown";
- const errorStatusText = result.statusText ?? "unknown";
- const errorBody = (() => {
- try {
- return JSON.stringify(result.body, null, 2);
- } catch (e) {
- return undefined;
- }
- })();
-
- throw new ApiError(
- options,
- result,
- `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
- );
- }
-};
-
-/**
- * Request method
- * @param config The OpenAPI configuration object
- * @param options The request options from the service
- * @param axiosClient The axios client instance to use
- * @returns CancelablePromise
- * @throws ApiError
- */
-export const request = (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
- axiosClient: AxiosInstance = axios,
-): CancelablePromise => {
- return new CancelablePromise(async (resolve, reject, onCancel) => {
- try {
- const url = getUrl(config, options);
- const formData = getFormData(options);
- const body = getRequestBody(options);
- const headers = await getHeaders(config, options);
-
- if (!onCancel.isCancelled) {
- let response = await sendRequest(
- config,
- options,
- url,
- body,
- formData,
- headers,
- onCancel,
- axiosClient,
- );
-
- for (const fn of config.interceptors.response._fns) {
- response = await fn(response);
- }
-
- const responseBody = getResponseBody(response);
- const responseHeader = getResponseHeader(
- response,
- options.responseHeader,
- );
-
- let transformedBody = responseBody;
- if (options.responseTransformer && isSuccess(response.status)) {
- transformedBody = await options.responseTransformer(responseBody);
- }
-
- const result: ApiResult = {
- url,
- ok: isSuccess(response.status),
- status: response.status,
- statusText: response.statusText,
- body: responseHeader ?? transformedBody,
- };
-
- catchErrorCodes(options, result);
-
- resolve(result.body);
- }
- } catch (error) {
- reject(error);
- }
- });
-};
diff --git a/www/app/api/index.ts b/www/app/api/index.ts
deleted file mode 100644
index 27fbb57d..00000000
--- a/www/app/api/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// This file is auto-generated by @hey-api/openapi-ts
-export { OpenApi } from "./OpenApi";
-export { ApiError } from "./core/ApiError";
-export { BaseHttpRequest } from "./core/BaseHttpRequest";
-export { CancelablePromise, CancelError } from "./core/CancelablePromise";
-export { OpenAPI, type OpenAPIConfig } from "./core/OpenAPI";
-export * from "./schemas.gen";
-export * from "./services.gen";
-export * from "./types.gen";
diff --git a/www/app/api/schemas.gen.ts b/www/app/api/schemas.gen.ts
index 03091a5f..e69de29b 100644
--- a/www/app/api/schemas.gen.ts
+++ b/www/app/api/schemas.gen.ts
@@ -1,1776 +0,0 @@
-// This file is auto-generated by @hey-api/openapi-ts
-
-export const $AudioWaveform = {
- properties: {
- data: {
- items: {
- type: "number",
- },
- type: "array",
- title: "Data",
- },
- },
- type: "object",
- required: ["data"],
- title: "AudioWaveform",
-} as const;
-
-export const $Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post =
- {
- properties: {
- chunk: {
- type: "string",
- format: "binary",
- title: "Chunk",
- },
- },
- type: "object",
- required: ["chunk"],
- title:
- "Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post",
- } as const;
-
-export const $CreateParticipant = {
- properties: {
- speaker: {
- anyOf: [
- {
- type: "integer",
- },
- {
- type: "null",
- },
- ],
- title: "Speaker",
- },
- name: {
- type: "string",
- title: "Name",
- },
- },
- type: "object",
- required: ["name"],
- title: "CreateParticipant",
-} as const;
-
-export const $CreateRoom = {
- properties: {
- name: {
- type: "string",
- title: "Name",
- },
- zulip_auto_post: {
- type: "boolean",
- title: "Zulip Auto Post",
- },
- zulip_stream: {
- type: "string",
- title: "Zulip Stream",
- },
- zulip_topic: {
- type: "string",
- title: "Zulip Topic",
- },
- is_locked: {
- type: "boolean",
- title: "Is Locked",
- },
- room_mode: {
- type: "string",
- title: "Room Mode",
- },
- recording_type: {
- type: "string",
- title: "Recording Type",
- },
- recording_trigger: {
- type: "string",
- title: "Recording Trigger",
- },
- is_shared: {
- type: "boolean",
- title: "Is Shared",
- },
- webhook_url: {
- type: "string",
- title: "Webhook Url",
- },
- webhook_secret: {
- type: "string",
- title: "Webhook Secret",
- },
- },
- type: "object",
- required: [
- "name",
- "zulip_auto_post",
- "zulip_stream",
- "zulip_topic",
- "is_locked",
- "room_mode",
- "recording_type",
- "recording_trigger",
- "is_shared",
- "webhook_url",
- "webhook_secret",
- ],
- title: "CreateRoom",
-} as const;
-
-export const $CreateTranscript = {
- properties: {
- name: {
- type: "string",
- title: "Name",
- },
- source_language: {
- type: "string",
- title: "Source Language",
- default: "en",
- },
- target_language: {
- type: "string",
- title: "Target Language",
- default: "en",
- },
- source_kind: {
- anyOf: [
- {
- $ref: "#/components/schemas/SourceKind",
- },
- {
- type: "null",
- },
- ],
- },
- },
- type: "object",
- required: ["name"],
- title: "CreateTranscript",
-} as const;
-
-export const $DeletionStatus = {
- properties: {
- status: {
- type: "string",
- title: "Status",
- },
- },
- type: "object",
- required: ["status"],
- title: "DeletionStatus",
-} as const;
-
-export const $GetTranscript = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- user_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "User Id",
- },
- name: {
- type: "string",
- title: "Name",
- },
- status: {
- type: "string",
- title: "Status",
- },
- locked: {
- type: "boolean",
- title: "Locked",
- },
- duration: {
- type: "number",
- title: "Duration",
- },
- title: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Title",
- },
- short_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Short Summary",
- },
- long_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Long Summary",
- },
- created_at: {
- type: "string",
- title: "Created At",
- },
- share_mode: {
- type: "string",
- title: "Share Mode",
- default: "private",
- },
- source_language: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Source Language",
- },
- target_language: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Target Language",
- },
- reviewed: {
- type: "boolean",
- title: "Reviewed",
- },
- meeting_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Meeting Id",
- },
- source_kind: {
- $ref: "#/components/schemas/SourceKind",
- },
- room_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Id",
- },
- room_name: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Name",
- },
- audio_deleted: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Audio Deleted",
- },
- participants: {
- anyOf: [
- {
- items: {
- $ref: "#/components/schemas/TranscriptParticipant",
- },
- type: "array",
- },
- {
- type: "null",
- },
- ],
- title: "Participants",
- },
- },
- type: "object",
- required: [
- "id",
- "user_id",
- "name",
- "status",
- "locked",
- "duration",
- "title",
- "short_summary",
- "long_summary",
- "created_at",
- "source_language",
- "target_language",
- "reviewed",
- "meeting_id",
- "source_kind",
- "participants",
- ],
- title: "GetTranscript",
-} as const;
-
-export const $GetTranscriptMinimal = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- user_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "User Id",
- },
- name: {
- type: "string",
- title: "Name",
- },
- status: {
- type: "string",
- title: "Status",
- },
- locked: {
- type: "boolean",
- title: "Locked",
- },
- duration: {
- type: "number",
- title: "Duration",
- },
- title: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Title",
- },
- short_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Short Summary",
- },
- long_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Long Summary",
- },
- created_at: {
- type: "string",
- title: "Created At",
- },
- share_mode: {
- type: "string",
- title: "Share Mode",
- default: "private",
- },
- source_language: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Source Language",
- },
- target_language: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Target Language",
- },
- reviewed: {
- type: "boolean",
- title: "Reviewed",
- },
- meeting_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Meeting Id",
- },
- source_kind: {
- $ref: "#/components/schemas/SourceKind",
- },
- room_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Id",
- },
- room_name: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Name",
- },
- audio_deleted: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Audio Deleted",
- },
- },
- type: "object",
- required: [
- "id",
- "user_id",
- "name",
- "status",
- "locked",
- "duration",
- "title",
- "short_summary",
- "long_summary",
- "created_at",
- "source_language",
- "target_language",
- "reviewed",
- "meeting_id",
- "source_kind",
- ],
- title: "GetTranscriptMinimal",
-} as const;
-
-export const $GetTranscriptSegmentTopic = {
- properties: {
- text: {
- type: "string",
- title: "Text",
- },
- start: {
- type: "number",
- title: "Start",
- },
- speaker: {
- type: "integer",
- title: "Speaker",
- },
- },
- type: "object",
- required: ["text", "start", "speaker"],
- title: "GetTranscriptSegmentTopic",
-} as const;
-
-export const $GetTranscriptTopic = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- title: {
- type: "string",
- title: "Title",
- },
- summary: {
- type: "string",
- title: "Summary",
- },
- timestamp: {
- type: "number",
- title: "Timestamp",
- },
- duration: {
- anyOf: [
- {
- type: "number",
- },
- {
- type: "null",
- },
- ],
- title: "Duration",
- },
- transcript: {
- type: "string",
- title: "Transcript",
- },
- segments: {
- items: {
- $ref: "#/components/schemas/GetTranscriptSegmentTopic",
- },
- type: "array",
- title: "Segments",
- default: [],
- },
- },
- type: "object",
- required: ["id", "title", "summary", "timestamp", "duration", "transcript"],
- title: "GetTranscriptTopic",
-} as const;
-
-export const $GetTranscriptTopicWithWords = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- title: {
- type: "string",
- title: "Title",
- },
- summary: {
- type: "string",
- title: "Summary",
- },
- timestamp: {
- type: "number",
- title: "Timestamp",
- },
- duration: {
- anyOf: [
- {
- type: "number",
- },
- {
- type: "null",
- },
- ],
- title: "Duration",
- },
- transcript: {
- type: "string",
- title: "Transcript",
- },
- segments: {
- items: {
- $ref: "#/components/schemas/GetTranscriptSegmentTopic",
- },
- type: "array",
- title: "Segments",
- default: [],
- },
- words: {
- items: {
- $ref: "#/components/schemas/Word",
- },
- type: "array",
- title: "Words",
- default: [],
- },
- },
- type: "object",
- required: ["id", "title", "summary", "timestamp", "duration", "transcript"],
- title: "GetTranscriptTopicWithWords",
-} as const;
-
-export const $GetTranscriptTopicWithWordsPerSpeaker = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- title: {
- type: "string",
- title: "Title",
- },
- summary: {
- type: "string",
- title: "Summary",
- },
- timestamp: {
- type: "number",
- title: "Timestamp",
- },
- duration: {
- anyOf: [
- {
- type: "number",
- },
- {
- type: "null",
- },
- ],
- title: "Duration",
- },
- transcript: {
- type: "string",
- title: "Transcript",
- },
- segments: {
- items: {
- $ref: "#/components/schemas/GetTranscriptSegmentTopic",
- },
- type: "array",
- title: "Segments",
- default: [],
- },
- words_per_speaker: {
- items: {
- $ref: "#/components/schemas/SpeakerWords",
- },
- type: "array",
- title: "Words Per Speaker",
- default: [],
- },
- },
- type: "object",
- required: ["id", "title", "summary", "timestamp", "duration", "transcript"],
- title: "GetTranscriptTopicWithWordsPerSpeaker",
-} as const;
-
-export const $HTTPValidationError = {
- properties: {
- detail: {
- items: {
- $ref: "#/components/schemas/ValidationError",
- },
- type: "array",
- title: "Detail",
- },
- },
- type: "object",
- title: "HTTPValidationError",
-} as const;
-
-export const $Meeting = {
- 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",
- },
- start_date: {
- type: "string",
- format: "date-time",
- title: "Start Date",
- },
- end_date: {
- type: "string",
- format: "date-time",
- title: "End Date",
- },
- recording_type: {
- type: "string",
- enum: ["none", "local", "cloud"],
- title: "Recording Type",
- default: "cloud",
- },
- },
- type: "object",
- required: [
- "id",
- "room_name",
- "room_url",
- "host_room_url",
- "start_date",
- "end_date",
- ],
- title: "Meeting",
-} as const;
-
-export const $MeetingConsentRequest = {
- properties: {
- consent_given: {
- type: "boolean",
- title: "Consent Given",
- },
- },
- type: "object",
- required: ["consent_given"],
- title: "MeetingConsentRequest",
-} as const;
-
-export const $Page_GetTranscriptMinimal_ = {
- properties: {
- items: {
- items: {
- $ref: "#/components/schemas/GetTranscriptMinimal",
- },
- type: "array",
- title: "Items",
- },
- total: {
- anyOf: [
- {
- type: "integer",
- minimum: 0,
- },
- {
- type: "null",
- },
- ],
- 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", "page", "size"],
- title: "Page[GetTranscriptMinimal]",
-} as const;
-
-export const $Page_RoomDetails_ = {
- properties: {
- items: {
- items: {
- $ref: "#/components/schemas/RoomDetails",
- },
- type: "array",
- title: "Items",
- },
- total: {
- anyOf: [
- {
- type: "integer",
- minimum: 0,
- },
- {
- type: "null",
- },
- ],
- 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", "page", "size"],
- title: "Page[RoomDetails]",
-} as const;
-
-export const $Participant = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- speaker: {
- anyOf: [
- {
- type: "integer",
- },
- {
- type: "null",
- },
- ],
- title: "Speaker",
- },
- name: {
- type: "string",
- title: "Name",
- },
- },
- type: "object",
- required: ["id", "speaker", "name"],
- 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",
- },
- zulip_auto_post: {
- type: "boolean",
- title: "Zulip Auto Post",
- },
- zulip_stream: {
- type: "string",
- title: "Zulip Stream",
- },
- zulip_topic: {
- type: "string",
- title: "Zulip Topic",
- },
- is_locked: {
- type: "boolean",
- title: "Is Locked",
- },
- room_mode: {
- type: "string",
- title: "Room Mode",
- },
- recording_type: {
- type: "string",
- title: "Recording Type",
- },
- recording_trigger: {
- type: "string",
- title: "Recording Trigger",
- },
- is_shared: {
- type: "boolean",
- title: "Is Shared",
- },
- },
- type: "object",
- required: [
- "id",
- "name",
- "user_id",
- "created_at",
- "zulip_auto_post",
- "zulip_stream",
- "zulip_topic",
- "is_locked",
- "room_mode",
- "recording_type",
- "recording_trigger",
- "is_shared",
- ],
- title: "Room",
-} as const;
-
-export const $RoomDetails = {
- 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",
- },
- zulip_auto_post: {
- type: "boolean",
- title: "Zulip Auto Post",
- },
- zulip_stream: {
- type: "string",
- title: "Zulip Stream",
- },
- zulip_topic: {
- type: "string",
- title: "Zulip Topic",
- },
- is_locked: {
- type: "boolean",
- title: "Is Locked",
- },
- room_mode: {
- type: "string",
- title: "Room Mode",
- },
- recording_type: {
- type: "string",
- title: "Recording Type",
- },
- recording_trigger: {
- type: "string",
- title: "Recording Trigger",
- },
- is_shared: {
- type: "boolean",
- title: "Is Shared",
- },
- webhook_url: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Webhook Url",
- },
- webhook_secret: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Webhook Secret",
- },
- },
- type: "object",
- required: [
- "id",
- "name",
- "user_id",
- "created_at",
- "zulip_auto_post",
- "zulip_stream",
- "zulip_topic",
- "is_locked",
- "room_mode",
- "recording_type",
- "recording_trigger",
- "is_shared",
- "webhook_url",
- "webhook_secret",
- ],
- title: "RoomDetails",
-} as const;
-
-export const $RtcOffer = {
- properties: {
- sdp: {
- type: "string",
- title: "Sdp",
- },
- type: {
- type: "string",
- title: "Type",
- },
- },
- type: "object",
- required: ["sdp", "type"],
- title: "RtcOffer",
-} as const;
-
-export const $SearchResponse = {
- properties: {
- results: {
- items: {
- $ref: "#/components/schemas/SearchResult",
- },
- type: "array",
- title: "Results",
- },
- total: {
- type: "integer",
- minimum: 0,
- title: "Total",
- description: "Total number of search results",
- },
- query: {
- anyOf: [
- {
- type: "string",
- minLength: 1,
- description: "Search query text",
- },
- {
- type: "null",
- },
- ],
- title: "Query",
- },
- limit: {
- type: "integer",
- maximum: 100,
- minimum: 1,
- title: "Limit",
- description: "Results per page",
- },
- offset: {
- type: "integer",
- minimum: 0,
- title: "Offset",
- description: "Number of results to skip",
- },
- },
- type: "object",
- required: ["results", "total", "limit", "offset"],
- title: "SearchResponse",
-} as const;
-
-export const $SearchResult = {
- properties: {
- id: {
- type: "string",
- minLength: 1,
- title: "Id",
- },
- title: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Title",
- },
- user_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "User Id",
- },
- room_id: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Id",
- },
- room_name: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Room Name",
- },
- source_kind: {
- $ref: "#/components/schemas/SourceKind",
- },
- created_at: {
- type: "string",
- title: "Created At",
- },
- status: {
- type: "string",
- minLength: 1,
- title: "Status",
- },
- rank: {
- type: "number",
- maximum: 1,
- minimum: 0,
- title: "Rank",
- },
- duration: {
- anyOf: [
- {
- type: "number",
- minimum: 0,
- },
- {
- type: "null",
- },
- ],
- title: "Duration",
- description: "Duration in seconds",
- },
- search_snippets: {
- items: {
- type: "string",
- },
- type: "array",
- title: "Search Snippets",
- description: "Text snippets around search matches",
- },
- total_match_count: {
- type: "integer",
- minimum: 0,
- title: "Total Match Count",
- description: "Total number of matches found in the transcript",
- default: 0,
- },
- },
- type: "object",
- required: [
- "id",
- "source_kind",
- "created_at",
- "status",
- "rank",
- "duration",
- "search_snippets",
- ],
- title: "SearchResult",
- description: "Public search result model with computed fields.",
-} as const;
-
-export const $SourceKind = {
- type: "string",
- enum: ["room", "live", "file"],
- title: "SourceKind",
-} as const;
-
-export const $SpeakerAssignment = {
- properties: {
- speaker: {
- anyOf: [
- {
- type: "integer",
- minimum: 0,
- },
- {
- type: "null",
- },
- ],
- title: "Speaker",
- },
- participant: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Participant",
- },
- timestamp_from: {
- type: "number",
- title: "Timestamp From",
- },
- timestamp_to: {
- type: "number",
- title: "Timestamp To",
- },
- },
- type: "object",
- required: ["timestamp_from", "timestamp_to"],
- title: "SpeakerAssignment",
-} as const;
-
-export const $SpeakerAssignmentStatus = {
- properties: {
- status: {
- type: "string",
- title: "Status",
- },
- },
- type: "object",
- required: ["status"],
- title: "SpeakerAssignmentStatus",
-} as const;
-
-export const $SpeakerMerge = {
- properties: {
- speaker_from: {
- type: "integer",
- title: "Speaker From",
- },
- speaker_to: {
- type: "integer",
- title: "Speaker To",
- },
- },
- type: "object",
- required: ["speaker_from", "speaker_to"],
- title: "SpeakerMerge",
-} as const;
-
-export const $SpeakerWords = {
- properties: {
- speaker: {
- type: "integer",
- title: "Speaker",
- },
- words: {
- items: {
- $ref: "#/components/schemas/Word",
- },
- type: "array",
- title: "Words",
- },
- },
- type: "object",
- required: ["speaker", "words"],
- title: "SpeakerWords",
-} as const;
-
-export const $Stream = {
- properties: {
- stream_id: {
- type: "integer",
- title: "Stream Id",
- },
- name: {
- type: "string",
- title: "Name",
- },
- },
- type: "object",
- required: ["stream_id", "name"],
- title: "Stream",
-} as const;
-
-export const $Topic = {
- properties: {
- name: {
- type: "string",
- title: "Name",
- },
- },
- type: "object",
- required: ["name"],
- title: "Topic",
-} as const;
-
-export const $TranscriptParticipant = {
- properties: {
- id: {
- type: "string",
- title: "Id",
- },
- speaker: {
- anyOf: [
- {
- type: "integer",
- },
- {
- type: "null",
- },
- ],
- title: "Speaker",
- },
- name: {
- type: "string",
- title: "Name",
- },
- },
- type: "object",
- required: ["speaker", "name"],
- title: "TranscriptParticipant",
-} as const;
-
-export const $UpdateParticipant = {
- properties: {
- speaker: {
- anyOf: [
- {
- type: "integer",
- },
- {
- type: "null",
- },
- ],
- title: "Speaker",
- },
- name: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Name",
- },
- },
- type: "object",
- title: "UpdateParticipant",
-} as const;
-
-export const $UpdateRoom = {
- properties: {
- name: {
- type: "string",
- title: "Name",
- },
- zulip_auto_post: {
- type: "boolean",
- title: "Zulip Auto Post",
- },
- zulip_stream: {
- type: "string",
- title: "Zulip Stream",
- },
- zulip_topic: {
- type: "string",
- title: "Zulip Topic",
- },
- is_locked: {
- type: "boolean",
- title: "Is Locked",
- },
- room_mode: {
- type: "string",
- title: "Room Mode",
- },
- recording_type: {
- type: "string",
- title: "Recording Type",
- },
- recording_trigger: {
- type: "string",
- title: "Recording Trigger",
- },
- is_shared: {
- type: "boolean",
- title: "Is Shared",
- },
- webhook_url: {
- type: "string",
- title: "Webhook Url",
- },
- webhook_secret: {
- type: "string",
- title: "Webhook Secret",
- },
- },
- type: "object",
- required: [
- "name",
- "zulip_auto_post",
- "zulip_stream",
- "zulip_topic",
- "is_locked",
- "room_mode",
- "recording_type",
- "recording_trigger",
- "is_shared",
- "webhook_url",
- "webhook_secret",
- ],
- title: "UpdateRoom",
-} as const;
-
-export const $UpdateTranscript = {
- properties: {
- name: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Name",
- },
- locked: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Locked",
- },
- title: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Title",
- },
- short_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Short Summary",
- },
- long_summary: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Long Summary",
- },
- share_mode: {
- anyOf: [
- {
- type: "string",
- enum: ["public", "semi-private", "private"],
- },
- {
- type: "null",
- },
- ],
- title: "Share Mode",
- },
- participants: {
- anyOf: [
- {
- items: {
- $ref: "#/components/schemas/TranscriptParticipant",
- },
- type: "array",
- },
- {
- type: "null",
- },
- ],
- title: "Participants",
- },
- reviewed: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Reviewed",
- },
- audio_deleted: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Audio Deleted",
- },
- },
- type: "object",
- title: "UpdateTranscript",
-} as const;
-
-export const $UserInfo = {
- properties: {
- sub: {
- type: "string",
- title: "Sub",
- },
- email: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Email",
- },
- email_verified: {
- anyOf: [
- {
- type: "boolean",
- },
- {
- type: "null",
- },
- ],
- title: "Email Verified",
- },
- },
- type: "object",
- required: ["sub", "email", "email_verified"],
- title: "UserInfo",
-} as const;
-
-export const $ValidationError = {
- properties: {
- loc: {
- items: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "integer",
- },
- ],
- },
- type: "array",
- title: "Location",
- },
- msg: {
- type: "string",
- title: "Message",
- },
- type: {
- type: "string",
- title: "Error Type",
- },
- },
- type: "object",
- required: ["loc", "msg", "type"],
- title: "ValidationError",
-} as const;
-
-export const $WebhookTestResult = {
- properties: {
- success: {
- type: "boolean",
- title: "Success",
- },
- message: {
- type: "string",
- title: "Message",
- default: "",
- },
- error: {
- type: "string",
- title: "Error",
- default: "",
- },
- status_code: {
- anyOf: [
- {
- type: "integer",
- },
- {
- type: "null",
- },
- ],
- title: "Status Code",
- },
- response_preview: {
- anyOf: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- title: "Response Preview",
- },
- },
- type: "object",
- required: ["success"],
- title: "WebhookTestResult",
-} as const;
-
-export const $WherebyWebhookEvent = {
- properties: {
- apiVersion: {
- type: "string",
- title: "Apiversion",
- },
- id: {
- type: "string",
- title: "Id",
- },
- createdAt: {
- type: "string",
- format: "date-time",
- title: "Createdat",
- },
- type: {
- type: "string",
- title: "Type",
- },
- data: {
- additionalProperties: true,
- type: "object",
- title: "Data",
- },
- },
- type: "object",
- required: ["apiVersion", "id", "createdAt", "type", "data"],
- title: "WherebyWebhookEvent",
-} as const;
-
-export const $Word = {
- properties: {
- text: {
- type: "string",
- title: "Text",
- },
- start: {
- type: "number",
- minimum: 0,
- title: "Start",
- description: "Time in seconds with float part",
- },
- end: {
- type: "number",
- minimum: 0,
- title: "End",
- description: "Time in seconds with float part",
- },
- speaker: {
- type: "integer",
- title: "Speaker",
- default: 0,
- },
- },
- type: "object",
- required: ["text", "start", "end"],
- title: "Word",
-} as const;
diff --git a/www/app/api/services.gen.ts b/www/app/api/services.gen.ts
index c9e027fb..e69de29b 100644
--- a/www/app/api/services.gen.ts
+++ b/www/app/api/services.gen.ts
@@ -1,942 +0,0 @@
-// This file is auto-generated by @hey-api/openapi-ts
-
-import type { CancelablePromise } from "./core/CancelablePromise";
-import type { BaseHttpRequest } from "./core/BaseHttpRequest";
-import type {
- MetricsResponse,
- V1MeetingAudioConsentData,
- V1MeetingAudioConsentResponse,
- V1RoomsListData,
- V1RoomsListResponse,
- V1RoomsCreateData,
- V1RoomsCreateResponse,
- V1RoomsGetData,
- V1RoomsGetResponse,
- V1RoomsUpdateData,
- V1RoomsUpdateResponse,
- V1RoomsDeleteData,
- V1RoomsDeleteResponse,
- V1RoomsCreateMeetingData,
- V1RoomsCreateMeetingResponse,
- V1RoomsTestWebhookData,
- V1RoomsTestWebhookResponse,
- V1TranscriptsListData,
- V1TranscriptsListResponse,
- V1TranscriptsCreateData,
- V1TranscriptsCreateResponse,
- V1TranscriptsSearchData,
- V1TranscriptsSearchResponse,
- V1TranscriptGetData,
- V1TranscriptGetResponse,
- V1TranscriptUpdateData,
- V1TranscriptUpdateResponse,
- V1TranscriptDeleteData,
- V1TranscriptDeleteResponse,
- V1TranscriptGetTopicsData,
- V1TranscriptGetTopicsResponse,
- V1TranscriptGetTopicsWithWordsData,
- V1TranscriptGetTopicsWithWordsResponse,
- V1TranscriptGetTopicsWithWordsPerSpeakerData,
- V1TranscriptGetTopicsWithWordsPerSpeakerResponse,
- V1TranscriptPostToZulipData,
- V1TranscriptPostToZulipResponse,
- V1TranscriptHeadAudioMp3Data,
- V1TranscriptHeadAudioMp3Response,
- V1TranscriptGetAudioMp3Data,
- V1TranscriptGetAudioMp3Response,
- V1TranscriptGetAudioWaveformData,
- V1TranscriptGetAudioWaveformResponse,
- V1TranscriptGetParticipantsData,
- V1TranscriptGetParticipantsResponse,
- V1TranscriptAddParticipantData,
- V1TranscriptAddParticipantResponse,
- V1TranscriptGetParticipantData,
- V1TranscriptGetParticipantResponse,
- V1TranscriptUpdateParticipantData,
- V1TranscriptUpdateParticipantResponse,
- V1TranscriptDeleteParticipantData,
- V1TranscriptDeleteParticipantResponse,
- V1TranscriptAssignSpeakerData,
- V1TranscriptAssignSpeakerResponse,
- V1TranscriptMergeSpeakerData,
- V1TranscriptMergeSpeakerResponse,
- V1TranscriptRecordUploadData,
- V1TranscriptRecordUploadResponse,
- V1TranscriptGetWebsocketEventsData,
- V1TranscriptGetWebsocketEventsResponse,
- V1TranscriptRecordWebrtcData,
- V1TranscriptRecordWebrtcResponse,
- V1TranscriptProcessData,
- V1TranscriptProcessResponse,
- V1UserMeResponse,
- V1ZulipGetStreamsResponse,
- V1ZulipGetTopicsData,
- V1ZulipGetTopicsResponse,
- V1WherebyWebhookData,
- V1WherebyWebhookResponse,
-} from "./types.gen";
-
-export class DefaultService {
- constructor(public readonly httpRequest: BaseHttpRequest) {}
-
- /**
- * Metrics
- * Endpoint that serves Prometheus metrics.
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public metrics(): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/metrics",
- });
- }
-
- /**
- * Meeting Audio Consent
- * @param data The data for the request.
- * @param data.meetingId
- * @param data.requestBody
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1MeetingAudioConsent(
- data: V1MeetingAudioConsentData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/meetings/{meeting_id}/consent",
- path: {
- meeting_id: data.meetingId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- 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_RoomDetails_ 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 Get
- * @param data The data for the request.
- * @param data.roomId
- * @returns RoomDetails Successful Response
- * @throws ApiError
- */
- public v1RoomsGet(
- data: V1RoomsGetData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/rooms/{room_id}",
- path: {
- room_id: data.roomId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Rooms Update
- * @param data The data for the request.
- * @param data.roomId
- * @param data.requestBody
- * @returns RoomDetails Successful Response
- * @throws ApiError
- */
- public v1RoomsUpdate(
- data: V1RoomsUpdateData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "PATCH",
- url: "/v1/rooms/{room_id}",
- path: {
- room_id: data.roomId,
- },
- 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 Meeting 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",
- },
- });
- }
-
- /**
- * Rooms Test Webhook
- * Test webhook configuration by sending a sample payload.
- * @param data The data for the request.
- * @param data.roomId
- * @returns WebhookTestResult Successful Response
- * @throws ApiError
- */
- public v1RoomsTestWebhook(
- data: V1RoomsTestWebhookData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/rooms/{room_id}/webhook/test",
- path: {
- room_id: data.roomId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcripts List
- * @param data The data for the request.
- * @param data.sourceKind
- * @param data.roomId
- * @param data.searchTerm
- * @param data.page Page number
- * @param data.size Page size
- * @returns Page_GetTranscriptMinimal_ Successful Response
- * @throws ApiError
- */
- public v1TranscriptsList(
- data: V1TranscriptsListData = {},
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts",
- query: {
- source_kind: data.sourceKind,
- room_id: data.roomId,
- search_term: data.searchTerm,
- page: data.page,
- size: data.size,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcripts Create
- * @param data The data for the request.
- * @param data.requestBody
- * @returns GetTranscript Successful Response
- * @throws ApiError
- */
- public v1TranscriptsCreate(
- data: V1TranscriptsCreateData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts",
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcripts Search
- * Full-text search across transcript titles and content.
- * @param data The data for the request.
- * @param data.q Search query text
- * @param data.limit Results per page
- * @param data.offset Number of results to skip
- * @param data.roomId
- * @param data.sourceKind
- * @returns SearchResponse Successful Response
- * @throws ApiError
- */
- public v1TranscriptsSearch(
- data: V1TranscriptsSearchData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/search",
- query: {
- q: data.q,
- limit: data.limit,
- offset: data.offset,
- room_id: data.roomId,
- source_kind: data.sourceKind,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns GetTranscript Successful Response
- * @throws ApiError
- */
- public v1TranscriptGet(
- data: V1TranscriptGetData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Update
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.requestBody
- * @returns GetTranscript Successful Response
- * @throws ApiError
- */
- public v1TranscriptUpdate(
- data: V1TranscriptUpdateData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "PATCH",
- url: "/v1/transcripts/{transcript_id}",
- path: {
- transcript_id: data.transcriptId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Delete
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns DeletionStatus Successful Response
- * @throws ApiError
- */
- public v1TranscriptDelete(
- data: V1TranscriptDeleteData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "DELETE",
- url: "/v1/transcripts/{transcript_id}",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Topics
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns GetTranscriptTopic Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetTopics(
- data: V1TranscriptGetTopicsData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/topics",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Topics With Words
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns GetTranscriptTopicWithWords Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetTopicsWithWords(
- data: V1TranscriptGetTopicsWithWordsData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/topics/with-words",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Topics With Words Per Speaker
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.topicId
- * @returns GetTranscriptTopicWithWordsPerSpeaker Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetTopicsWithWordsPerSpeaker(
- data: V1TranscriptGetTopicsWithWordsPerSpeakerData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker",
- path: {
- transcript_id: data.transcriptId,
- topic_id: data.topicId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Post To Zulip
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.stream
- * @param data.topic
- * @param data.includeTopics
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptPostToZulip(
- data: V1TranscriptPostToZulipData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts/{transcript_id}/zulip",
- path: {
- transcript_id: data.transcriptId,
- },
- query: {
- stream: data.stream,
- topic: data.topic,
- include_topics: data.includeTopics,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Audio Mp3
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.token
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptHeadAudioMp3(
- data: V1TranscriptHeadAudioMp3Data,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "HEAD",
- url: "/v1/transcripts/{transcript_id}/audio/mp3",
- path: {
- transcript_id: data.transcriptId,
- },
- query: {
- token: data.token,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Audio Mp3
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.token
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetAudioMp3(
- data: V1TranscriptGetAudioMp3Data,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/audio/mp3",
- path: {
- transcript_id: data.transcriptId,
- },
- query: {
- token: data.token,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Audio Waveform
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns AudioWaveform Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetAudioWaveform(
- data: V1TranscriptGetAudioWaveformData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/audio/waveform",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Participants
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns Participant Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetParticipants(
- data: V1TranscriptGetParticipantsData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/participants",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Add Participant
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.requestBody
- * @returns Participant Successful Response
- * @throws ApiError
- */
- public v1TranscriptAddParticipant(
- data: V1TranscriptAddParticipantData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts/{transcript_id}/participants",
- path: {
- transcript_id: data.transcriptId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Participant
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.participantId
- * @returns Participant Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetParticipant(
- data: V1TranscriptGetParticipantData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/participants/{participant_id}",
- path: {
- transcript_id: data.transcriptId,
- participant_id: data.participantId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Update Participant
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.participantId
- * @param data.requestBody
- * @returns Participant Successful Response
- * @throws ApiError
- */
- public v1TranscriptUpdateParticipant(
- data: V1TranscriptUpdateParticipantData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "PATCH",
- url: "/v1/transcripts/{transcript_id}/participants/{participant_id}",
- path: {
- transcript_id: data.transcriptId,
- participant_id: data.participantId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Delete Participant
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.participantId
- * @returns DeletionStatus Successful Response
- * @throws ApiError
- */
- public v1TranscriptDeleteParticipant(
- data: V1TranscriptDeleteParticipantData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "DELETE",
- url: "/v1/transcripts/{transcript_id}/participants/{participant_id}",
- path: {
- transcript_id: data.transcriptId,
- participant_id: data.participantId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Assign Speaker
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.requestBody
- * @returns SpeakerAssignmentStatus Successful Response
- * @throws ApiError
- */
- public v1TranscriptAssignSpeaker(
- data: V1TranscriptAssignSpeakerData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "PATCH",
- url: "/v1/transcripts/{transcript_id}/speaker/assign",
- path: {
- transcript_id: data.transcriptId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Merge Speaker
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.requestBody
- * @returns SpeakerAssignmentStatus Successful Response
- * @throws ApiError
- */
- public v1TranscriptMergeSpeaker(
- data: V1TranscriptMergeSpeakerData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "PATCH",
- url: "/v1/transcripts/{transcript_id}/speaker/merge",
- path: {
- transcript_id: data.transcriptId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Record Upload
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.chunkNumber
- * @param data.totalChunks
- * @param data.formData
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptRecordUpload(
- data: V1TranscriptRecordUploadData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts/{transcript_id}/record/upload",
- path: {
- transcript_id: data.transcriptId,
- },
- query: {
- chunk_number: data.chunkNumber,
- total_chunks: data.totalChunks,
- },
- formData: data.formData,
- mediaType: "multipart/form-data",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Get Websocket Events
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptGetWebsocketEvents(
- data: V1TranscriptGetWebsocketEventsData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/transcripts/{transcript_id}/events",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Record Webrtc
- * @param data The data for the request.
- * @param data.transcriptId
- * @param data.requestBody
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptRecordWebrtc(
- data: V1TranscriptRecordWebrtcData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts/{transcript_id}/record/webrtc",
- path: {
- transcript_id: data.transcriptId,
- },
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Transcript Process
- * @param data The data for the request.
- * @param data.transcriptId
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1TranscriptProcess(
- data: V1TranscriptProcessData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/transcripts/{transcript_id}/process",
- path: {
- transcript_id: data.transcriptId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * User Me
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1UserMe(): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/me",
- });
- }
-
- /**
- * Zulip Get Streams
- * Get all Zulip streams.
- * @returns Stream Successful Response
- * @throws ApiError
- */
- public v1ZulipGetStreams(): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/zulip/streams",
- });
- }
-
- /**
- * Zulip Get Topics
- * Get all topics for a specific Zulip stream.
- * @param data The data for the request.
- * @param data.streamId
- * @returns Topic Successful Response
- * @throws ApiError
- */
- public v1ZulipGetTopics(
- data: V1ZulipGetTopicsData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "GET",
- url: "/v1/zulip/streams/{stream_id}/topics",
- path: {
- stream_id: data.streamId,
- },
- errors: {
- 422: "Validation Error",
- },
- });
- }
-
- /**
- * Whereby Webhook
- * @param data The data for the request.
- * @param data.requestBody
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public v1WherebyWebhook(
- data: V1WherebyWebhookData,
- ): CancelablePromise {
- return this.httpRequest.request({
- method: "POST",
- url: "/v1/whereby",
- body: data.requestBody,
- mediaType: "application/json",
- errors: {
- 422: "Validation Error",
- },
- });
- }
-}
diff --git a/www/app/api/types.gen.ts b/www/app/api/types.gen.ts
index d724fc98..e69de29b 100644
--- a/www/app/api/types.gen.ts
+++ b/www/app/api/types.gen.ts
@@ -1,1143 +0,0 @@
-// This file is auto-generated by @hey-api/openapi-ts
-
-export type AudioWaveform = {
- data: Array;
-};
-
-export type Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post =
- {
- chunk: Blob | File;
- };
-
-export type CreateParticipant = {
- speaker?: number | null;
- name: string;
-};
-
-export type CreateRoom = {
- name: string;
- zulip_auto_post: boolean;
- zulip_stream: string;
- zulip_topic: string;
- is_locked: boolean;
- room_mode: string;
- recording_type: string;
- recording_trigger: string;
- is_shared: boolean;
- webhook_url: string;
- webhook_secret: string;
-};
-
-export type CreateTranscript = {
- name: string;
- source_language?: string;
- target_language?: string;
- source_kind?: SourceKind | null;
-};
-
-export type DeletionStatus = {
- status: string;
-};
-
-export type GetTranscript = {
- id: string;
- user_id: string | null;
- name: string;
- status: string;
- locked: boolean;
- duration: number;
- title: string | null;
- short_summary: string | null;
- long_summary: string | null;
- created_at: string;
- share_mode?: string;
- source_language: string | null;
- target_language: string | null;
- reviewed: boolean;
- meeting_id: string | null;
- source_kind: SourceKind;
- room_id?: string | null;
- room_name?: string | null;
- audio_deleted?: boolean | null;
- participants: Array | null;
-};
-
-export type GetTranscriptMinimal = {
- id: string;
- user_id: string | null;
- name: string;
- status: string;
- locked: boolean;
- duration: number;
- title: string | null;
- short_summary: string | null;
- long_summary: string | null;
- created_at: string;
- share_mode?: string;
- source_language: string | null;
- target_language: string | null;
- reviewed: boolean;
- meeting_id: string | null;
- source_kind: SourceKind;
- room_id?: string | null;
- room_name?: string | null;
- audio_deleted?: boolean | null;
-};
-
-export type GetTranscriptSegmentTopic = {
- text: string;
- start: number;
- speaker: number;
-};
-
-export type GetTranscriptTopic = {
- id: string;
- title: string;
- summary: string;
- timestamp: number;
- duration: number | null;
- transcript: string;
- segments?: Array;
-};
-
-export type GetTranscriptTopicWithWords = {
- id: string;
- title: string;
- summary: string;
- timestamp: number;
- duration: number | null;
- transcript: string;
- segments?: Array;
- words?: Array;
-};
-
-export type GetTranscriptTopicWithWordsPerSpeaker = {
- id: string;
- title: string;
- summary: string;
- timestamp: number;
- duration: number | null;
- transcript: string;
- segments?: Array;
- words_per_speaker?: Array;
-};
-
-export type HTTPValidationError = {
- detail?: Array;
-};
-
-export type Meeting = {
- id: string;
- room_name: string;
- room_url: string;
- host_room_url: string;
- start_date: string;
- end_date: string;
- recording_type?: "none" | "local" | "cloud";
-};
-
-export type recording_type = "none" | "local" | "cloud";
-
-export type MeetingConsentRequest = {
- consent_given: boolean;
-};
-
-export type Page_GetTranscriptMinimal_ = {
- items: Array;
- total?: number | null;
- page: number | null;
- size: number | null;
- pages?: number | null;
-};
-
-export type Page_RoomDetails_ = {
- items: Array;
- total?: number | null;
- 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;
- zulip_auto_post: boolean;
- zulip_stream: string;
- zulip_topic: string;
- is_locked: boolean;
- room_mode: string;
- recording_type: string;
- recording_trigger: string;
- is_shared: boolean;
-};
-
-export type RoomDetails = {
- id: string;
- name: string;
- user_id: string;
- created_at: string;
- zulip_auto_post: boolean;
- zulip_stream: string;
- zulip_topic: string;
- is_locked: boolean;
- room_mode: string;
- recording_type: string;
- recording_trigger: string;
- is_shared: boolean;
- webhook_url: string | null;
- webhook_secret: string | null;
-};
-
-export type RtcOffer = {
- sdp: string;
- type: string;
-};
-
-export type SearchResponse = {
- results: Array;
- /**
- * Total number of search results
- */
- total: number;
- query?: string | null;
- /**
- * Results per page
- */
- limit: number;
- /**
- * Number of results to skip
- */
- offset: number;
-};
-
-/**
- * Public search result model with computed fields.
- */
-export type SearchResult = {
- id: string;
- title?: string | null;
- user_id?: string | null;
- room_id?: string | null;
- room_name?: string | null;
- source_kind: SourceKind;
- created_at: string;
- status: string;
- rank: number;
- /**
- * Duration in seconds
- */
- duration: number | null;
- /**
- * Text snippets around search matches
- */
- search_snippets: Array;
- /**
- * Total number of matches found in the transcript
- */
- total_match_count?: number;
-};
-
-export type SourceKind = "room" | "live" | "file";
-
-export type SpeakerAssignment = {
- speaker?: number | null;
- participant?: string | null;
- timestamp_from: number;
- timestamp_to: number;
-};
-
-export type SpeakerAssignmentStatus = {
- status: string;
-};
-
-export type SpeakerMerge = {
- speaker_from: number;
- speaker_to: number;
-};
-
-export type SpeakerWords = {
- speaker: number;
- words: Array;
-};
-
-export type Stream = {
- stream_id: number;
- name: string;
-};
-
-export type Topic = {
- name: string;
-};
-
-export type TranscriptParticipant = {
- id?: string;
- speaker: number | null;
- name: string;
-};
-
-export type UpdateParticipant = {
- speaker?: number | null;
- name?: string | null;
-};
-
-export type UpdateRoom = {
- name: string;
- zulip_auto_post: boolean;
- zulip_stream: string;
- zulip_topic: string;
- is_locked: boolean;
- room_mode: string;
- recording_type: string;
- recording_trigger: string;
- is_shared: boolean;
- webhook_url: string;
- webhook_secret: string;
-};
-
-export type UpdateTranscript = {
- name?: string | null;
- locked?: boolean | null;
- title?: string | null;
- short_summary?: string | null;
- long_summary?: string | null;
- share_mode?: "public" | "semi-private" | "private" | null;
- participants?: Array | null;
- reviewed?: boolean | null;
- audio_deleted?: boolean | null;
-};
-
-export type UserInfo = {
- sub: string;
- email: string | null;
- email_verified: boolean | null;
-};
-
-export type ValidationError = {
- loc: Array;
- msg: string;
- type: string;
-};
-
-export type WebhookTestResult = {
- success: boolean;
- message?: string;
- error?: string;
- status_code?: number | null;
- response_preview?: string | null;
-};
-
-export type WherebyWebhookEvent = {
- apiVersion: string;
- id: string;
- createdAt: string;
- type: string;
- data: {
- [key: string]: unknown;
- };
-};
-
-export type Word = {
- text: string;
- /**
- * Time in seconds with float part
- */
- start: number;
- /**
- * Time in seconds with float part
- */
- end: number;
- speaker?: number;
-};
-
-export type MetricsResponse = unknown;
-
-export type V1MeetingAudioConsentData = {
- meetingId: string;
- requestBody: MeetingConsentRequest;
-};
-
-export type V1MeetingAudioConsentResponse = unknown;
-
-export type V1RoomsListData = {
- /**
- * Page number
- */
- page?: number;
- /**
- * Page size
- */
- size?: number;
-};
-
-export type V1RoomsListResponse = Page_RoomDetails_;
-
-export type V1RoomsCreateData = {
- requestBody: CreateRoom;
-};
-
-export type V1RoomsCreateResponse = Room;
-
-export type V1RoomsGetData = {
- roomId: string;
-};
-
-export type V1RoomsGetResponse = RoomDetails;
-
-export type V1RoomsUpdateData = {
- requestBody: UpdateRoom;
- roomId: string;
-};
-
-export type V1RoomsUpdateResponse = RoomDetails;
-
-export type V1RoomsDeleteData = {
- roomId: string;
-};
-
-export type V1RoomsDeleteResponse = DeletionStatus;
-
-export type V1RoomsCreateMeetingData = {
- roomName: string;
-};
-
-export type V1RoomsCreateMeetingResponse = Meeting;
-
-export type V1RoomsTestWebhookData = {
- roomId: string;
-};
-
-export type V1RoomsTestWebhookResponse = WebhookTestResult;
-
-export type V1TranscriptsListData = {
- /**
- * Page number
- */
- page?: number;
- roomId?: string | null;
- searchTerm?: string | null;
- /**
- * Page size
- */
- size?: number;
- sourceKind?: SourceKind | null;
-};
-
-export type V1TranscriptsListResponse = Page_GetTranscriptMinimal_;
-
-export type V1TranscriptsCreateData = {
- requestBody: CreateTranscript;
-};
-
-export type V1TranscriptsCreateResponse = GetTranscript;
-
-export type V1TranscriptsSearchData = {
- /**
- * Results per page
- */
- limit?: number;
- /**
- * Number of results to skip
- */
- offset?: number;
- /**
- * Search query text
- */
- q: string;
- roomId?: string | null;
- sourceKind?: SourceKind | null;
-};
-
-export type V1TranscriptsSearchResponse = SearchResponse;
-
-export type V1TranscriptGetData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetResponse = GetTranscript;
-
-export type V1TranscriptUpdateData = {
- requestBody: UpdateTranscript;
- transcriptId: string;
-};
-
-export type V1TranscriptUpdateResponse = GetTranscript;
-
-export type V1TranscriptDeleteData = {
- transcriptId: string;
-};
-
-export type V1TranscriptDeleteResponse = DeletionStatus;
-
-export type V1TranscriptGetTopicsData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetTopicsResponse = Array;
-
-export type V1TranscriptGetTopicsWithWordsData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetTopicsWithWordsResponse =
- Array;
-
-export type V1TranscriptGetTopicsWithWordsPerSpeakerData = {
- topicId: string;
- transcriptId: string;
-};
-
-export type V1TranscriptGetTopicsWithWordsPerSpeakerResponse =
- GetTranscriptTopicWithWordsPerSpeaker;
-
-export type V1TranscriptPostToZulipData = {
- includeTopics: boolean;
- stream: string;
- topic: string;
- transcriptId: string;
-};
-
-export type V1TranscriptPostToZulipResponse = unknown;
-
-export type V1TranscriptHeadAudioMp3Data = {
- token?: string | null;
- transcriptId: string;
-};
-
-export type V1TranscriptHeadAudioMp3Response = unknown;
-
-export type V1TranscriptGetAudioMp3Data = {
- token?: string | null;
- transcriptId: string;
-};
-
-export type V1TranscriptGetAudioMp3Response = unknown;
-
-export type V1TranscriptGetAudioWaveformData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetAudioWaveformResponse = AudioWaveform;
-
-export type V1TranscriptGetParticipantsData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetParticipantsResponse = Array;
-
-export type V1TranscriptAddParticipantData = {
- requestBody: CreateParticipant;
- transcriptId: string;
-};
-
-export type V1TranscriptAddParticipantResponse = Participant;
-
-export type V1TranscriptGetParticipantData = {
- participantId: string;
- transcriptId: string;
-};
-
-export type V1TranscriptGetParticipantResponse = Participant;
-
-export type V1TranscriptUpdateParticipantData = {
- participantId: string;
- requestBody: UpdateParticipant;
- transcriptId: string;
-};
-
-export type V1TranscriptUpdateParticipantResponse = Participant;
-
-export type V1TranscriptDeleteParticipantData = {
- participantId: string;
- transcriptId: string;
-};
-
-export type V1TranscriptDeleteParticipantResponse = DeletionStatus;
-
-export type V1TranscriptAssignSpeakerData = {
- requestBody: SpeakerAssignment;
- transcriptId: string;
-};
-
-export type V1TranscriptAssignSpeakerResponse = SpeakerAssignmentStatus;
-
-export type V1TranscriptMergeSpeakerData = {
- requestBody: SpeakerMerge;
- transcriptId: string;
-};
-
-export type V1TranscriptMergeSpeakerResponse = SpeakerAssignmentStatus;
-
-export type V1TranscriptRecordUploadData = {
- chunkNumber: number;
- formData: Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post;
- totalChunks: number;
- transcriptId: string;
-};
-
-export type V1TranscriptRecordUploadResponse = unknown;
-
-export type V1TranscriptGetWebsocketEventsData = {
- transcriptId: string;
-};
-
-export type V1TranscriptGetWebsocketEventsResponse = unknown;
-
-export type V1TranscriptRecordWebrtcData = {
- requestBody: RtcOffer;
- transcriptId: string;
-};
-
-export type V1TranscriptRecordWebrtcResponse = unknown;
-
-export type V1TranscriptProcessData = {
- transcriptId: string;
-};
-
-export type V1TranscriptProcessResponse = unknown;
-
-export type V1UserMeResponse = UserInfo | null;
-
-export type V1ZulipGetStreamsResponse = Array;
-
-export type V1ZulipGetTopicsData = {
- streamId: number;
-};
-
-export type V1ZulipGetTopicsResponse = Array;
-
-export type V1WherebyWebhookData = {
- requestBody: WherebyWebhookEvent;
-};
-
-export type V1WherebyWebhookResponse = unknown;
-
-export type $OpenApiTs = {
- "/metrics": {
- get: {
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- };
- };
- };
- "/v1/meetings/{meeting_id}/consent": {
- post: {
- req: V1MeetingAudioConsentData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/rooms": {
- get: {
- req: V1RoomsListData;
- res: {
- /**
- * Successful Response
- */
- 200: Page_RoomDetails_;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- post: {
- req: V1RoomsCreateData;
- res: {
- /**
- * Successful Response
- */
- 200: Room;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/rooms/{room_id}": {
- get: {
- req: V1RoomsGetData;
- res: {
- /**
- * Successful Response
- */
- 200: RoomDetails;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- patch: {
- req: V1RoomsUpdateData;
- res: {
- /**
- * Successful Response
- */
- 200: RoomDetails;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- delete: {
- req: V1RoomsDeleteData;
- res: {
- /**
- * Successful Response
- */
- 200: DeletionStatus;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/rooms/{room_name}/meeting": {
- post: {
- req: V1RoomsCreateMeetingData;
- res: {
- /**
- * Successful Response
- */
- 200: Meeting;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/rooms/{room_id}/webhook/test": {
- post: {
- req: V1RoomsTestWebhookData;
- res: {
- /**
- * Successful Response
- */
- 200: WebhookTestResult;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts": {
- get: {
- req: V1TranscriptsListData;
- res: {
- /**
- * Successful Response
- */
- 200: Page_GetTranscriptMinimal_;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- post: {
- req: V1TranscriptsCreateData;
- res: {
- /**
- * Successful Response
- */
- 200: GetTranscript;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/search": {
- get: {
- req: V1TranscriptsSearchData;
- res: {
- /**
- * Successful Response
- */
- 200: SearchResponse;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}": {
- get: {
- req: V1TranscriptGetData;
- res: {
- /**
- * Successful Response
- */
- 200: GetTranscript;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- patch: {
- req: V1TranscriptUpdateData;
- res: {
- /**
- * Successful Response
- */
- 200: GetTranscript;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- delete: {
- req: V1TranscriptDeleteData;
- res: {
- /**
- * Successful Response
- */
- 200: DeletionStatus;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/topics": {
- get: {
- req: V1TranscriptGetTopicsData;
- res: {
- /**
- * Successful Response
- */
- 200: Array;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/topics/with-words": {
- get: {
- req: V1TranscriptGetTopicsWithWordsData;
- res: {
- /**
- * Successful Response
- */
- 200: Array;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker": {
- get: {
- req: V1TranscriptGetTopicsWithWordsPerSpeakerData;
- res: {
- /**
- * Successful Response
- */
- 200: GetTranscriptTopicWithWordsPerSpeaker;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/zulip": {
- post: {
- req: V1TranscriptPostToZulipData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/audio/mp3": {
- head: {
- req: V1TranscriptHeadAudioMp3Data;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- get: {
- req: V1TranscriptGetAudioMp3Data;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/audio/waveform": {
- get: {
- req: V1TranscriptGetAudioWaveformData;
- res: {
- /**
- * Successful Response
- */
- 200: AudioWaveform;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/participants": {
- get: {
- req: V1TranscriptGetParticipantsData;
- res: {
- /**
- * Successful Response
- */
- 200: Array;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- post: {
- req: V1TranscriptAddParticipantData;
- res: {
- /**
- * Successful Response
- */
- 200: Participant;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/participants/{participant_id}": {
- get: {
- req: V1TranscriptGetParticipantData;
- res: {
- /**
- * Successful Response
- */
- 200: Participant;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- patch: {
- req: V1TranscriptUpdateParticipantData;
- res: {
- /**
- * Successful Response
- */
- 200: Participant;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- delete: {
- req: V1TranscriptDeleteParticipantData;
- res: {
- /**
- * Successful Response
- */
- 200: DeletionStatus;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/speaker/assign": {
- patch: {
- req: V1TranscriptAssignSpeakerData;
- res: {
- /**
- * Successful Response
- */
- 200: SpeakerAssignmentStatus;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/speaker/merge": {
- patch: {
- req: V1TranscriptMergeSpeakerData;
- res: {
- /**
- * Successful Response
- */
- 200: SpeakerAssignmentStatus;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/record/upload": {
- post: {
- req: V1TranscriptRecordUploadData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/events": {
- get: {
- req: V1TranscriptGetWebsocketEventsData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/record/webrtc": {
- post: {
- req: V1TranscriptRecordWebrtcData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/transcripts/{transcript_id}/process": {
- post: {
- req: V1TranscriptProcessData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/me": {
- get: {
- res: {
- /**
- * Successful Response
- */
- 200: UserInfo | null;
- };
- };
- };
- "/v1/zulip/streams": {
- get: {
- res: {
- /**
- * Successful Response
- */
- 200: Array;
- };
- };
- };
- "/v1/zulip/streams/{stream_id}/topics": {
- get: {
- req: V1ZulipGetTopicsData;
- res: {
- /**
- * Successful Response
- */
- 200: Array;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
- "/v1/whereby": {
- post: {
- req: V1WherebyWebhookData;
- res: {
- /**
- * Successful Response
- */
- 200: unknown;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
-};
diff --git a/www/app/api/urls.ts b/www/app/api/urls.ts
index bd0a910c..89ce5af8 100644
--- a/www/app/api/urls.ts
+++ b/www/app/api/urls.ts
@@ -1,2 +1 @@
-// TODO better connection with generated schema; it's duplication
export const RECORD_A_MEETING_URL = "/transcripts/new" as const;
diff --git a/www/app/layout.tsx b/www/app/layout.tsx
index f73b8813..62175be9 100644
--- a/www/app/layout.tsx
+++ b/www/app/layout.tsx
@@ -1,7 +1,6 @@
import "./styles/globals.scss";
import { Metadata, Viewport } from "next";
import { Poppins } from "next/font/google";
-import SessionProvider from "./lib/SessionProvider";
import { ErrorProvider } from "./(errors)/errorContext";
import ErrorMessage from "./(errors)/errorMessage";
import { DomainContextProvider } from "./domainContext";
@@ -74,18 +73,16 @@ export default async function RootLayout({
return (
-
-
-
- "something went really wrong"
}>
-
-
- {children}
-
-
-
-
-
+
+
+ "something went really wrong"}>
+
+
+ {children}
+
+
+
+
);
diff --git a/www/app/lib/AuthProvider.tsx b/www/app/lib/AuthProvider.tsx
new file mode 100644
index 00000000..96f49f87
--- /dev/null
+++ b/www/app/lib/AuthProvider.tsx
@@ -0,0 +1,104 @@
+"use client";
+
+import { createContext, useContext } from "react";
+import { useSession as useNextAuthSession } from "next-auth/react";
+import { signOut, signIn } from "next-auth/react";
+import { configureApiAuth } from "./apiClient";
+import { assertCustomSession, CustomSession } from "./types";
+import { Session } from "next-auth";
+import { SessionAutoRefresh } from "./SessionAutoRefresh";
+import { REFRESH_ACCESS_TOKEN_ERROR } from "./auth";
+
+type AuthContextType = (
+ | { status: "loading" }
+ | { status: "refreshing" }
+ | { status: "unauthenticated"; error?: string }
+ | {
+ status: "authenticated";
+ accessToken: string;
+ accessTokenExpires: number;
+ user: CustomSession["user"];
+ }
+) & {
+ update: () => Promise;
+ signIn: typeof signIn;
+ signOut: typeof signOut;
+};
+
+const AuthContext = createContext(undefined);
+
+export function AuthProvider({ children }: { children: React.ReactNode }) {
+ const { data: session, status, update } = useNextAuthSession();
+ const customSession = session ? assertCustomSession(session) : null;
+
+ const contextValue: AuthContextType = {
+ ...(() => {
+ switch (status) {
+ case "loading": {
+ const sessionIsHere = !!customSession;
+ switch (sessionIsHere) {
+ case false: {
+ return { status };
+ }
+ case true: {
+ return { status: "refreshing" as const };
+ }
+ default: {
+ const _: never = sessionIsHere;
+ throw new Error("unreachable");
+ }
+ }
+ }
+ case "authenticated": {
+ if (customSession?.error === REFRESH_ACCESS_TOKEN_ERROR) {
+ // token had expired but next auth still returns "authenticated" so show user unauthenticated state
+ return {
+ status: "unauthenticated" as const,
+ };
+ } else if (customSession?.accessToken) {
+ return {
+ status,
+ accessToken: customSession.accessToken,
+ accessTokenExpires: customSession.accessTokenExpires,
+ user: customSession.user,
+ };
+ } else {
+ console.warn(
+ "illegal state: authenticated but have no session/or access token. ignoring",
+ );
+ return { status: "unauthenticated" as const };
+ }
+ }
+ case "unauthenticated": {
+ return { status: "unauthenticated" as const };
+ }
+ default: {
+ const _: never = status;
+ throw new Error("unreachable");
+ }
+ }
+ })(),
+ update,
+ signIn,
+ signOut,
+ };
+
+ // not useEffect, we need it ASAP
+ configureApiAuth(
+ contextValue.status === "authenticated" ? contextValue.accessToken : null,
+ );
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAuth() {
+ const context = useContext(AuthContext);
+ if (context === undefined) {
+ throw new Error("useAuth must be used within an AuthProvider");
+ }
+ return context;
+}
diff --git a/www/app/lib/SessionAutoRefresh.tsx b/www/app/lib/SessionAutoRefresh.tsx
index 1e230d6c..fd29367f 100644
--- a/www/app/lib/SessionAutoRefresh.tsx
+++ b/www/app/lib/SessionAutoRefresh.tsx
@@ -1,5 +1,5 @@
/**
- * This is a custom hook that automatically refreshes the session when the access token is about to expire.
+ * This is a custom provider that automatically refreshes the session when the access token is about to expire.
* When communicating with the reflector API, we need to ensure that the access token is always valid.
*
* We could have implemented that as an interceptor on the API client, but not everything is using the
@@ -7,30 +7,38 @@
*/
"use client";
-import { useSession } from "next-auth/react";
import { useEffect } from "react";
-import { CustomSession } from "./types";
+import { useAuth } from "./AuthProvider";
+import { REFRESH_ACCESS_TOKEN_BEFORE } from "./auth";
-export function SessionAutoRefresh({
- children,
- refreshInterval = 20 /* seconds */,
-}) {
- const { data: session, update } = useSession();
- const customSession = session as CustomSession;
- const accessTokenExpires = customSession?.accessTokenExpires;
+const REFRESH_BEFORE = REFRESH_ACCESS_TOKEN_BEFORE;
+
+export function SessionAutoRefresh({ children }) {
+ const auth = useAuth();
+ const accessTokenExpires =
+ auth.status === "authenticated" ? auth.accessTokenExpires : null;
useEffect(() => {
+ // technical value for how often the setInterval will be polling news - not too fast (no spam in case of errors)
+ // and not too slow (debuggable)
+ const INTERVAL_REFRESH_MS = 5000;
const interval = setInterval(() => {
- if (accessTokenExpires) {
+ if (accessTokenExpires !== null) {
const timeLeft = accessTokenExpires - Date.now();
- if (timeLeft < refreshInterval * 1000) {
- update();
+ if (timeLeft < REFRESH_BEFORE) {
+ auth
+ .update()
+ .then(() => {})
+ .catch((e) => {
+ // note: 401 won't be considered error here
+ console.error("error refreshing auth token", e);
+ });
}
}
- }, refreshInterval * 1000);
+ }, INTERVAL_REFRESH_MS);
return () => clearInterval(interval);
- }, [accessTokenExpires, refreshInterval, update]);
+ }, [accessTokenExpires, auth.update]);
return children;
}
diff --git a/www/app/lib/SessionProvider.tsx b/www/app/lib/SessionProvider.tsx
deleted file mode 100644
index 9c95fbc8..00000000
--- a/www/app/lib/SessionProvider.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-"use client";
-import { SessionProvider as SessionProviderNextAuth } from "next-auth/react";
-import { SessionAutoRefresh } from "./SessionAutoRefresh";
-
-export default function SessionProvider({ children }) {
- return (
-
- {children}
-
- );
-}
diff --git a/www/app/lib/__tests__/redisTokenCache.test.ts b/www/app/lib/__tests__/redisTokenCache.test.ts
new file mode 100644
index 00000000..8ca8e8a1
--- /dev/null
+++ b/www/app/lib/__tests__/redisTokenCache.test.ts
@@ -0,0 +1,85 @@
+import {
+ getTokenCache,
+ setTokenCache,
+ deleteTokenCache,
+ TokenCacheEntry,
+ KV,
+} from "../redisTokenCache";
+
+const mockKV: KV & {
+ clear: () => void;
+} = (() => {
+ const data = new Map();
+ return {
+ async get(key: string): Promise {
+ return data.get(key) || null;
+ },
+
+ async setex(key: string, seconds_: number, value: string): Promise<"OK"> {
+ data.set(key, value);
+ return "OK";
+ },
+
+ async del(key: string): Promise {
+ const existed = data.has(key);
+ data.delete(key);
+ return existed ? 1 : 0;
+ },
+
+ clear() {
+ data.clear();
+ },
+ };
+})();
+
+describe("Redis Token Cache", () => {
+ beforeEach(() => {
+ mockKV.clear();
+ });
+
+ test("basic write/read - value written equals value read", async () => {
+ const testKey = "token:test-user-123";
+ const testValue: TokenCacheEntry = {
+ token: {
+ sub: "test-user-123",
+ name: "Test User",
+ email: "test@example.com",
+ accessToken: "access-token-123",
+ accessTokenExpires: Date.now() + 3600000, // 1 hour from now
+ refreshToken: "refresh-token-456",
+ },
+ timestamp: Date.now(),
+ };
+
+ await setTokenCache(mockKV, testKey, testValue);
+ const retrievedValue = await getTokenCache(mockKV, testKey);
+
+ expect(retrievedValue).not.toBeNull();
+ expect(retrievedValue).toEqual(testValue);
+ expect(retrievedValue?.token.accessToken).toBe(testValue.token.accessToken);
+ expect(retrievedValue?.token.sub).toBe(testValue.token.sub);
+ expect(retrievedValue?.timestamp).toBe(testValue.timestamp);
+ });
+
+ test("get returns null for non-existent key", async () => {
+ const result = await getTokenCache(mockKV, "non-existent-key");
+ expect(result).toBeNull();
+ });
+
+ test("delete removes token from cache", async () => {
+ const testKey = "token:delete-test";
+ const testValue: TokenCacheEntry = {
+ token: {
+ accessToken: "test-token",
+ accessTokenExpires: Date.now() + 3600000,
+ },
+ timestamp: Date.now(),
+ };
+
+ await setTokenCache(mockKV, testKey, testValue);
+ await deleteTokenCache(mockKV, testKey);
+
+ const result = await getTokenCache(mockKV, testKey);
+ expect(result).toBeNull();
+ });
+});
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
new file mode 100644
index 00000000..cd97e151
--- /dev/null
+++ b/www/app/lib/apiClient.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import createClient from "openapi-fetch";
+import type { paths } from "../reflector-api";
+import {
+ queryOptions,
+ useMutation,
+ useQuery,
+ useSuspenseQuery,
+} from "@tanstack/react-query";
+import createFetchClient from "openapi-react-query";
+import { assertExistsAndNonEmptyString } from "./utils";
+import { isBuildPhase } from "./next";
+
+const API_URL = !isBuildPhase
+ ? assertExistsAndNonEmptyString(process.env.NEXT_PUBLIC_API_URL)
+ : "http://localhost";
+
+// Create the base openapi-fetch client with a default URL
+// The actual URL will be set via middleware in AuthProvider
+export const client = createClient({
+ baseUrl: API_URL,
+});
+
+export const $api = createFetchClient(client);
+
+let currentAuthToken: string | null | undefined = null;
+
+client.use({
+ onRequest({ request }) {
+ if (currentAuthToken) {
+ request.headers.set("Authorization", `Bearer ${currentAuthToken}`);
+ }
+ // XXX Only set Content-Type if not already set (FormData will set its own boundary)
+ // This is a work around for uploading file, we're passing a formdata
+ // but the content type was still application/json
+ if (
+ !request.headers.has("Content-Type") &&
+ !(request.body instanceof FormData)
+ ) {
+ request.headers.set("Content-Type", "application/json");
+ }
+ return request;
+ },
+});
+
+// the function contract: lightweight, idempotent
+export const configureApiAuth = (token: string | null | undefined) => {
+ currentAuthToken = token;
+};
diff --git a/www/app/lib/apiHooks.ts b/www/app/lib/apiHooks.ts
new file mode 100644
index 00000000..94d84c9b
--- /dev/null
+++ b/www/app/lib/apiHooks.ts
@@ -0,0 +1,618 @@
+"use client";
+
+import { $api } from "./apiClient";
+import { useError } from "../(errors)/errorContext";
+import { useQueryClient } from "@tanstack/react-query";
+import type { components } from "../reflector-api";
+import { useAuth } from "./AuthProvider";
+
+/*
+ * XXX error types returned from the hooks are not always correct; declared types are ValidationError but real type could be string or any other
+ * this is either a limitation or incorrect usage of Python json schema generator
+ * or, limitation or incorrect usage of .d type generator from json schema
+ * */
+
+const useAuthReady = () => {
+ const auth = useAuth();
+
+ return {
+ isAuthenticated: auth.status === "authenticated",
+ isLoading: auth.status === "loading",
+ };
+};
+
+export function useRoomsList(page: number = 1) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/rooms",
+ {
+ params: {
+ query: { page },
+ },
+ },
+ {
+ enabled: isAuthenticated,
+ },
+ );
+}
+
+type SourceKind = components["schemas"]["SourceKind"];
+
+export function useTranscriptsSearch(
+ q: string = "",
+ options: {
+ limit?: number;
+ offset?: number;
+ room_id?: string;
+ source_kind?: SourceKind;
+ } = {},
+) {
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/search",
+ {
+ params: {
+ query: {
+ q,
+ limit: options.limit,
+ offset: options.offset,
+ room_id: options.room_id,
+ source_kind: options.source_kind,
+ },
+ },
+ },
+ {
+ enabled: true,
+ },
+ );
+}
+
+export function useTranscriptDelete() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("delete", "/v1/transcripts/{transcript_id}", {
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ["get", "/v1/transcripts/search"],
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error deleting the transcript");
+ },
+ });
+}
+
+export function useTranscriptProcess() {
+ const { setError } = useError();
+
+ return $api.useMutation("post", "/v1/transcripts/{transcript_id}/process", {
+ onError: (error) => {
+ setError(error as Error, "There was an error processing the transcript");
+ },
+ });
+}
+
+export function useTranscriptGet(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: {
+ path: {
+ transcript_id: transcriptId || "",
+ },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useRoomGet(roomId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/rooms/{room_id}",
+ {
+ params: {
+ path: { room_id: roomId || "" },
+ },
+ },
+ {
+ enabled: !!roomId && isAuthenticated,
+ },
+ );
+}
+
+export function useRoomTestWebhook() {
+ const { setError } = useError();
+
+ return $api.useMutation("post", "/v1/rooms/{room_id}/webhook/test", {
+ onError: (error) => {
+ setError(error as Error, "There was an error testing the webhook");
+ },
+ });
+}
+
+export function useRoomCreate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("post", "/v1/rooms", {
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error creating the room");
+ },
+ });
+}
+
+export function useRoomUpdate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("patch", "/v1/rooms/{room_id}", {
+ onSuccess: async (room) => {
+ await Promise.all([
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
+ }),
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/rooms/{room_id}", {
+ params: {
+ path: {
+ room_id: room.id,
+ },
+ },
+ }).queryKey,
+ }),
+ ]);
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error updating the room");
+ },
+ });
+}
+
+export function useRoomDelete() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("delete", "/v1/rooms/{room_id}", {
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error deleting the room");
+ },
+ });
+}
+
+export function useZulipStreams() {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/zulip/streams",
+ {},
+ {
+ enabled: isAuthenticated,
+ },
+ );
+}
+
+export function useZulipTopics(streamId: number | null) {
+ const { isAuthenticated } = useAuthReady();
+ const enabled = !!streamId && isAuthenticated;
+ return $api.useQuery(
+ "get",
+ "/v1/zulip/streams/{stream_id}/topics",
+ {
+ params: {
+ path: {
+ stream_id: enabled ? streamId : 0,
+ },
+ },
+ },
+ {
+ enabled,
+ },
+ );
+}
+
+export function useTranscriptUpdate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("patch", "/v1/transcripts/{transcript_id}", {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/transcripts/{transcript_id}", {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ }).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error updating the transcript");
+ },
+ });
+}
+
+export function useTranscriptPostToZulip() {
+ const { setError } = useError();
+
+ // @ts-ignore - Zulip endpoint not in OpenAPI spec
+ return $api.useMutation("post", "/v1/transcripts/{transcript_id}/zulip", {
+ onError: (error) => {
+ setError(error as Error, "There was an error posting to Zulip");
+ },
+ });
+}
+
+export function useTranscriptUploadAudio() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "post",
+ "/v1/transcripts/{transcript_id}/record/upload",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error uploading the audio file");
+ },
+ },
+ );
+}
+
+export function useTranscriptWaveform(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/audio/waveform",
+ {
+ params: {
+ path: { transcript_id: transcriptId || "" },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptMP3(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/audio/mp3",
+ {
+ params: {
+ path: { transcript_id: transcriptId || "" },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptTopics(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/topics",
+ {
+ params: {
+ path: { transcript_id: transcriptId || "" },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptTopicsWithWords(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/topics/with-words",
+ {
+ params: {
+ path: { transcript_id: transcriptId || "" },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptTopicsWithWordsPerSpeaker(
+ transcriptId: string | null,
+ topicId: string | null,
+) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker",
+ {
+ params: {
+ path: {
+ transcript_id: transcriptId || "",
+ topic_id: topicId || "",
+ },
+ },
+ },
+ {
+ enabled: !!transcriptId && !!topicId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptParticipants(transcriptId: string | null) {
+ const { isAuthenticated } = useAuthReady();
+
+ return $api.useQuery(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: transcriptId || "" },
+ },
+ },
+ {
+ enabled: !!transcriptId && isAuthenticated,
+ },
+ );
+}
+
+export function useTranscriptParticipantUpdate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "patch",
+ "/v1/transcripts/{transcript_id}/participants/{participant_id}",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error updating the participant");
+ },
+ },
+ );
+}
+
+export function useTranscriptParticipantCreate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "post",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error creating the participant");
+ },
+ },
+ );
+}
+
+export function useTranscriptParticipantDelete() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "delete",
+ "/v1/transcripts/{transcript_id}/participants/{participant_id}",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error deleting the participant");
+ },
+ },
+ );
+}
+
+export function useTranscriptSpeakerAssign() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "patch",
+ "/v1/transcripts/{transcript_id}/speaker/assign",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error assigning the speaker");
+ },
+ },
+ );
+}
+
+export function useTranscriptSpeakerMerge() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation(
+ "patch",
+ "/v1/transcripts/{transcript_id}/speaker/merge",
+ {
+ onSuccess: (data, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions(
+ "get",
+ "/v1/transcripts/{transcript_id}/participants",
+ {
+ params: {
+ path: { transcript_id: variables.params.path.transcript_id },
+ },
+ },
+ ).queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error merging speakers");
+ },
+ },
+ );
+}
+
+export function useMeetingAudioConsent() {
+ const { setError } = useError();
+
+ return $api.useMutation("post", "/v1/meetings/{meeting_id}/consent", {
+ onError: (error) => {
+ setError(error as Error, "There was an error recording consent");
+ },
+ });
+}
+
+export function useTranscriptWebRTC() {
+ const { setError } = useError();
+
+ return $api.useMutation(
+ "post",
+ "/v1/transcripts/{transcript_id}/record/webrtc",
+ {
+ onError: (error) => {
+ setError(error as Error, "There was an error with WebRTC connection");
+ },
+ },
+ );
+}
+
+export function useTranscriptCreate() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("post", "/v1/transcripts", {
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ["get", "/v1/transcripts/search"],
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error creating the transcript");
+ },
+ });
+}
+
+export function useRoomsCreateMeeting() {
+ const { setError } = useError();
+ const queryClient = useQueryClient();
+
+ return $api.useMutation("post", "/v1/rooms/{room_name}/meeting", {
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
+ });
+ },
+ onError: (error) => {
+ setError(error as Error, "There was an error creating the meeting");
+ },
+ });
+}
diff --git a/www/app/lib/auth.ts b/www/app/lib/auth.ts
index 9169c694..f6e60513 100644
--- a/www/app/lib/auth.ts
+++ b/www/app/lib/auth.ts
@@ -1,157 +1,13 @@
-// import { kv } from "@vercel/kv";
-import Redlock, { ResourceLockedError } from "redlock";
-import { AuthOptions } from "next-auth";
-import AuthentikProvider from "next-auth/providers/authentik";
-import { JWT } from "next-auth/jwt";
-import { JWTWithAccessToken, CustomSession } from "./types";
-import Redis from "ioredis";
+export const REFRESH_ACCESS_TOKEN_ERROR = "RefreshAccessTokenError" as const;
+// 4 min is 1 min less than default authentic value. here we assume that authentic won't be set to access tokens < 4 min
+export const REFRESH_ACCESS_TOKEN_BEFORE = 4 * 60 * 1000;
-const PRETIMEOUT = 60; // seconds before token expires to refresh it
-const DEFAULT_REDIS_KEY_TIMEOUT = 60 * 60 * 24 * 30; // 30 days (refresh token expires in 30 days)
-const kv = new Redis(process.env.KV_URL || "", {
- tls: {},
-});
-const redlock = new Redlock([kv], {});
+export const LOGIN_REQUIRED_PAGES = [
+ "/transcripts/[!new]",
+ "/browse(.*)",
+ "/rooms(.*)",
+];
-redlock.on("error", (error) => {
- if (error instanceof ResourceLockedError) {
- return;
- }
-
- // Log all other errors.
- console.error(error);
-});
-
-export const authOptions: AuthOptions = {
- providers: [
- AuthentikProvider({
- clientId: process.env.AUTHENTIK_CLIENT_ID as string,
- clientSecret: process.env.AUTHENTIK_CLIENT_SECRET as string,
- issuer: process.env.AUTHENTIK_ISSUER,
- authorization: {
- params: {
- scope: "openid email profile offline_access",
- },
- },
- }),
- ],
- session: {
- strategy: "jwt",
- },
- callbacks: {
- async jwt({ token, account, user }) {
- const extendedToken = token as JWTWithAccessToken;
- if (account && user) {
- // called only on first login
- // XXX account.expires_in used in example is not defined for authentik backend, but expires_at is
- const expiresAt = (account.expires_at as number) - PRETIMEOUT;
- const jwtToken = {
- ...extendedToken,
- accessToken: account.access_token,
- accessTokenExpires: expiresAt * 1000,
- refreshToken: account.refresh_token,
- };
- kv.set(
- `token:${jwtToken.sub}`,
- JSON.stringify(jwtToken),
- "EX",
- DEFAULT_REDIS_KEY_TIMEOUT,
- );
- return jwtToken;
- }
-
- if (Date.now() < extendedToken.accessTokenExpires) {
- return token;
- }
-
- // access token has expired, try to update it
- return await redisLockedrefreshAccessToken(token);
- },
- async session({ session, token }) {
- const extendedToken = token as JWTWithAccessToken;
- const customSession = session as CustomSession;
- customSession.accessToken = extendedToken.accessToken;
- customSession.accessTokenExpires = extendedToken.accessTokenExpires;
- customSession.error = extendedToken.error;
- customSession.user = {
- id: extendedToken.sub,
- name: extendedToken.name,
- email: extendedToken.email,
- };
- return customSession;
- },
- },
-};
-
-async function redisLockedrefreshAccessToken(token: JWT) {
- return await redlock.using(
- [token.sub as string, "jwt-refresh"],
- 5000,
- async () => {
- const redisToken = await kv.get(`token:${token.sub}`);
- const currentToken = JSON.parse(
- redisToken as string,
- ) as JWTWithAccessToken;
-
- // if there is multiple requests for the same token, it may already have been refreshed
- if (Date.now() < currentToken.accessTokenExpires) {
- return currentToken;
- }
-
- // now really do the request
- const newToken = await refreshAccessToken(currentToken);
- await kv.set(
- `token:${currentToken.sub}`,
- JSON.stringify(newToken),
- "EX",
- DEFAULT_REDIS_KEY_TIMEOUT,
- );
- return newToken;
- },
- );
-}
-
-async function refreshAccessToken(token: JWT): Promise {
- try {
- const url = `${process.env.AUTHENTIK_REFRESH_TOKEN_URL}`;
-
- const options = {
- headers: {
- "Content-Type": "application/x-www-form-urlencoded",
- },
- body: new URLSearchParams({
- client_id: process.env.AUTHENTIK_CLIENT_ID as string,
- client_secret: process.env.AUTHENTIK_CLIENT_SECRET as string,
- grant_type: "refresh_token",
- refresh_token: token.refreshToken as string,
- }).toString(),
- method: "POST",
- };
-
- const response = await fetch(url, options);
- if (!response.ok) {
- console.error(
- new Date().toISOString(),
- "Failed to refresh access token. Response status:",
- response.status,
- );
- const responseBody = await response.text();
- console.error(new Date().toISOString(), "Response body:", responseBody);
- throw new Error(`Failed to refresh access token: ${response.statusText}`);
- }
- const refreshedTokens = await response.json();
- return {
- ...token,
- accessToken: refreshedTokens.access_token,
- accessTokenExpires:
- Date.now() + (refreshedTokens.expires_in - PRETIMEOUT) * 1000,
- refreshToken: refreshedTokens.refresh_token,
- };
- } catch (error) {
- console.error("Error refreshing access token", error);
- return {
- ...token,
- error: "RefreshAccessTokenError",
- } as JWTWithAccessToken;
- }
-}
+export const PROTECTED_PAGES = new RegExp(
+ LOGIN_REQUIRED_PAGES.map((page) => `^${page}$`).join("|"),
+);
diff --git a/www/app/lib/authBackend.ts b/www/app/lib/authBackend.ts
new file mode 100644
index 00000000..af93b274
--- /dev/null
+++ b/www/app/lib/authBackend.ts
@@ -0,0 +1,178 @@
+import { AuthOptions } from "next-auth";
+import AuthentikProvider from "next-auth/providers/authentik";
+import type { JWT } from "next-auth/jwt";
+import { JWTWithAccessToken, CustomSession } from "./types";
+import { assertExists, assertExistsAndNonEmptyString } from "./utils";
+import {
+ REFRESH_ACCESS_TOKEN_BEFORE,
+ REFRESH_ACCESS_TOKEN_ERROR,
+} from "./auth";
+import {
+ getTokenCache,
+ setTokenCache,
+ deleteTokenCache,
+} from "./redisTokenCache";
+import { tokenCacheRedis } from "./redisClient";
+import { isBuildPhase } from "./next";
+
+// REFRESH_ACCESS_TOKEN_BEFORE because refresh is based on access token expiration (imagine we cache it 30 days)
+const TOKEN_CACHE_TTL = REFRESH_ACCESS_TOKEN_BEFORE;
+
+const refreshLocks = new Map>();
+
+const CLIENT_ID = !isBuildPhase
+ ? assertExistsAndNonEmptyString(process.env.AUTHENTIK_CLIENT_ID)
+ : "noop";
+const CLIENT_SECRET = !isBuildPhase
+ ? assertExistsAndNonEmptyString(process.env.AUTHENTIK_CLIENT_SECRET)
+ : "noop";
+
+export const authOptions: AuthOptions = {
+ providers: [
+ AuthentikProvider({
+ clientId: CLIENT_ID,
+ clientSecret: CLIENT_SECRET,
+ issuer: process.env.AUTHENTIK_ISSUER,
+ authorization: {
+ params: {
+ scope: "openid email profile offline_access",
+ },
+ },
+ }),
+ ],
+ session: {
+ strategy: "jwt",
+ },
+ callbacks: {
+ async jwt({ token, account, user }) {
+ const KEY = `token:${token.sub}`;
+
+ if (account && user) {
+ // called only on first login
+ // XXX account.expires_in used in example is not defined for authentik backend, but expires_at is
+ const expiresAtS = assertExists(account.expires_at);
+ const expiresAtMs = expiresAtS * 1000;
+ if (!account.access_token) {
+ await deleteTokenCache(tokenCacheRedis, KEY);
+ } else {
+ const jwtToken: JWTWithAccessToken = {
+ ...token,
+ accessToken: account.access_token,
+ accessTokenExpires: expiresAtMs,
+ refreshToken: account.refresh_token,
+ };
+ await setTokenCache(tokenCacheRedis, KEY, {
+ token: jwtToken,
+ timestamp: Date.now(),
+ });
+ return jwtToken;
+ }
+ }
+
+ const currentToken = await getTokenCache(tokenCacheRedis, KEY);
+ if (currentToken && Date.now() < currentToken.token.accessTokenExpires) {
+ return currentToken.token;
+ }
+
+ // access token has expired, try to update it
+ return await lockedRefreshAccessToken(token);
+ },
+ async session({ session, token }) {
+ const extendedToken = token as JWTWithAccessToken;
+ return {
+ ...session,
+ accessToken: extendedToken.accessToken,
+ accessTokenExpires: extendedToken.accessTokenExpires,
+ error: extendedToken.error,
+ user: {
+ id: assertExists(extendedToken.sub),
+ name: extendedToken.name,
+ email: extendedToken.email,
+ },
+ } satisfies CustomSession;
+ },
+ },
+};
+
+async function lockedRefreshAccessToken(
+ token: JWT,
+): Promise {
+ const lockKey = `${token.sub}-refresh`;
+
+ const existingRefresh = refreshLocks.get(lockKey);
+ if (existingRefresh) {
+ return await existingRefresh;
+ }
+
+ const refreshPromise = (async () => {
+ try {
+ const cached = await getTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ if (cached) {
+ if (Date.now() - cached.timestamp > TOKEN_CACHE_TTL) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ } else if (Date.now() < cached.token.accessTokenExpires) {
+ return cached.token;
+ }
+ }
+
+ const currentToken = cached?.token || (token as JWTWithAccessToken);
+ const newToken = await refreshAccessToken(currentToken);
+
+ await setTokenCache(tokenCacheRedis, `token:${token.sub}`, {
+ token: newToken,
+ timestamp: Date.now(),
+ });
+
+ return newToken;
+ } finally {
+ setTimeout(() => refreshLocks.delete(lockKey), 100);
+ }
+ })();
+
+ refreshLocks.set(lockKey, refreshPromise);
+ return refreshPromise;
+}
+
+async function refreshAccessToken(token: JWT): Promise {
+ try {
+ const url = `${process.env.AUTHENTIK_REFRESH_TOKEN_URL}`;
+
+ const options = {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: new URLSearchParams({
+ client_id: process.env.AUTHENTIK_CLIENT_ID as string,
+ client_secret: process.env.AUTHENTIK_CLIENT_SECRET as string,
+ grant_type: "refresh_token",
+ refresh_token: token.refreshToken as string,
+ }).toString(),
+ method: "POST",
+ };
+
+ const response = await fetch(url, options);
+ if (!response.ok) {
+ console.error(
+ new Date().toISOString(),
+ "Failed to refresh access token. Response status:",
+ response.status,
+ );
+ const responseBody = await response.text();
+ console.error(new Date().toISOString(), "Response body:", responseBody);
+ throw new Error(`Failed to refresh access token: ${response.statusText}`);
+ }
+ const refreshedTokens = await response.json();
+ return {
+ ...token,
+ accessToken: refreshedTokens.access_token,
+ accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
+ refreshToken: refreshedTokens.refresh_token,
+ };
+ } catch (error) {
+ console.error("Error refreshing access token", error);
+ return {
+ ...token,
+ error: REFRESH_ACCESS_TOKEN_ERROR,
+ } as JWTWithAccessToken;
+ }
+}
diff --git a/www/app/lib/edgeConfig.ts b/www/app/lib/edgeConfig.ts
index 2e31e146..f234a2cf 100644
--- a/www/app/lib/edgeConfig.ts
+++ b/www/app/lib/edgeConfig.ts
@@ -1,5 +1,5 @@
import { get } from "@vercel/edge-config";
-import { isDevelopment } from "./utils";
+import { isBuildPhase } from "./next";
type EdgeConfig = {
[domainWithDash: string]: {
@@ -29,12 +29,18 @@ export function edgeDomainToKey(domain: string) {
// get edge config server-side (prefer DomainContext when available), domain is the hostname
export async function getConfig() {
- const domain = new URL(process.env.NEXT_PUBLIC_SITE_URL!).hostname;
-
if (process.env.NEXT_PUBLIC_ENV === "development") {
- return require("../../config").localConfig;
+ try {
+ return require("../../config").localConfig;
+ } catch (e) {
+ // next build() WILL try to execute the require above even if conditionally protected
+ // but thank god it at least runs catch{} block properly
+ if (!isBuildPhase) throw new Error(e);
+ return require("../../config-template").localConfig;
+ }
}
+ const domain = new URL(process.env.NEXT_PUBLIC_SITE_URL!).hostname;
let config = await get(edgeDomainToKey(domain));
if (typeof config !== "object") {
diff --git a/www/app/lib/next.ts b/www/app/lib/next.ts
new file mode 100644
index 00000000..91d88bd2
--- /dev/null
+++ b/www/app/lib/next.ts
@@ -0,0 +1,2 @@
+// next.js tries to run all the lib code during build phase; we don't always want it when e.g. we have connections initialized we don't want to have
+export const isBuildPhase = process.env.NEXT_PHASE?.includes("build");
diff --git a/www/app/lib/queryClient.tsx b/www/app/lib/queryClient.tsx
new file mode 100644
index 00000000..bd5946e0
--- /dev/null
+++ b/www/app/lib/queryClient.tsx
@@ -0,0 +1,17 @@
+"use client";
+
+import { QueryClient } from "@tanstack/react-query";
+
+export const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000, // 1 minute
+ gcTime: 5 * 60 * 1000, // 5 minutes (formerly cacheTime)
+ retry: 1,
+ refetchOnWindowFocus: false,
+ },
+ mutations: {
+ retry: 0,
+ },
+ },
+});
diff --git a/www/app/lib/redisClient.ts b/www/app/lib/redisClient.ts
new file mode 100644
index 00000000..1be36538
--- /dev/null
+++ b/www/app/lib/redisClient.ts
@@ -0,0 +1,46 @@
+import Redis from "ioredis";
+import { isBuildPhase } from "./next";
+
+export type RedisClient = Pick;
+
+const getRedisClient = (): RedisClient => {
+ const redisUrl = process.env.KV_URL;
+ if (!redisUrl) {
+ throw new Error("KV_URL environment variable is required");
+ }
+ const redis = new Redis(redisUrl, {
+ maxRetriesPerRequest: 3,
+ lazyConnect: true,
+ });
+
+ redis.on("error", (error) => {
+ console.error("Redis error:", error);
+ });
+
+ // not necessary but will indicate redis config errors by failfast at startup
+ // happens only once; after that connection is allowed to die and the lib is assumed to be able to restore it eventually
+ redis.connect().catch((e) => {
+ console.error("Failed to connect to Redis:", e);
+ process.exit(1);
+ });
+
+ return redis;
+};
+
+// next.js buildtime usage - we want to isolate next.js "build" time concepts here
+const noopClient: RedisClient = (() => {
+ const noopSetex: Redis["setex"] = async () => {
+ return "OK" as const;
+ };
+ const noopDel: Redis["del"] = async () => {
+ return 0;
+ };
+ return {
+ get: async () => {
+ return null;
+ },
+ setex: noopSetex,
+ del: noopDel,
+ };
+})();
+export const tokenCacheRedis = isBuildPhase ? noopClient : getRedisClient();
diff --git a/www/app/lib/redisTokenCache.ts b/www/app/lib/redisTokenCache.ts
new file mode 100644
index 00000000..4fa4e304
--- /dev/null
+++ b/www/app/lib/redisTokenCache.ts
@@ -0,0 +1,61 @@
+import { z } from "zod";
+import { REFRESH_ACCESS_TOKEN_BEFORE } from "./auth";
+
+const TokenCacheEntrySchema = z.object({
+ token: z.object({
+ sub: z.string().optional(),
+ name: z.string().nullish(),
+ email: z.string().nullish(),
+ accessToken: z.string(),
+ accessTokenExpires: z.number(),
+ refreshToken: z.string().optional(),
+ error: z.string().optional(),
+ }),
+ timestamp: z.number(),
+});
+
+const TokenCacheEntryCodec = z.codec(z.string(), TokenCacheEntrySchema, {
+ decode: (jsonString) => {
+ const parsed = JSON.parse(jsonString);
+ return TokenCacheEntrySchema.parse(parsed);
+ },
+ encode: (value) => JSON.stringify(value),
+});
+
+export type TokenCacheEntry = z.infer;
+
+export type KV = {
+ get(key: string): Promise;
+ setex(key: string, seconds: number, value: string): Promise<"OK">;
+ del(key: string): Promise;
+};
+
+export async function getTokenCache(
+ redis: KV,
+ key: string,
+): Promise {
+ const data = await redis.get(key);
+ if (!data) return null;
+
+ try {
+ return TokenCacheEntryCodec.decode(data);
+ } catch (error) {
+ console.error("Invalid token cache data:", error);
+ await redis.del(key);
+ return null;
+ }
+}
+
+export async function setTokenCache(
+ redis: KV,
+ key: string,
+ value: TokenCacheEntry,
+): Promise {
+ const encodedValue = TokenCacheEntryCodec.encode(value);
+ const ttlSeconds = Math.floor(REFRESH_ACCESS_TOKEN_BEFORE / 1000);
+ await redis.setex(key, ttlSeconds, encodedValue);
+}
+
+export async function deleteTokenCache(redis: KV, key: string): Promise {
+ await redis.del(key);
+}
diff --git a/www/app/lib/types.ts b/www/app/lib/types.ts
index 851ee5be..0576e186 100644
--- a/www/app/lib/types.ts
+++ b/www/app/lib/types.ts
@@ -1,10 +1,11 @@
-import { Session } from "next-auth";
-import { JWT } from "next-auth/jwt";
+import type { Session } from "next-auth";
+import type { JWT } from "next-auth/jwt";
+import { parseMaybeNonEmptyString } from "./utils";
export interface JWTWithAccessToken extends JWT {
accessToken: string;
accessTokenExpires: number;
- refreshToken: string;
+ refreshToken?: string;
error?: string;
}
@@ -12,9 +13,62 @@ export interface CustomSession extends Session {
accessToken: string;
accessTokenExpires: number;
error?: string;
- user: {
- id?: string;
- name?: string | null;
- email?: string | null;
+ user: Session["user"] & {
+ id: string;
};
}
+
+// assumption that JWT is JWTWithAccessToken - we set it in jwt callback of auth; typing isn't strong around there
+// but the assumption is crucial to auth working
+export const assertExtendedToken = (
+ t: T,
+): T & {
+ accessTokenExpires: number;
+ accessToken: string;
+} => {
+ if (
+ typeof (t as { accessTokenExpires: any }).accessTokenExpires === "number" &&
+ !isNaN((t as { accessTokenExpires: any }).accessTokenExpires) &&
+ typeof (
+ t as {
+ accessToken: any;
+ }
+ ).accessToken === "string" &&
+ parseMaybeNonEmptyString((t as { accessToken: any }).accessToken) !== null
+ ) {
+ return t as T & {
+ accessTokenExpires: number;
+ accessToken: string;
+ };
+ }
+ throw new Error("Token is not extended with access token");
+};
+
+export const assertExtendedTokenAndUserId = (
+ t: T,
+): T & {
+ accessTokenExpires: number;
+ accessToken: string;
+ user: U & {
+ id: string;
+ };
+} => {
+ const extendedToken = assertExtendedToken(t);
+ if (typeof (extendedToken.user as any)?.id === "string") {
+ return t as T & {
+ accessTokenExpires: number;
+ accessToken: string;
+ user: U & {
+ id: string;
+ };
+ };
+ }
+ throw new Error("Token is not extended with user id");
+};
+
+// best attempt to check the session is valid
+export const assertCustomSession = (s: S): CustomSession => {
+ const r = assertExtendedTokenAndUserId(s);
+ // no other checks for now
+ return r as CustomSession;
+};
diff --git a/www/app/lib/useApi.ts b/www/app/lib/useApi.ts
deleted file mode 100644
index 837ef84f..00000000
--- a/www/app/lib/useApi.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { useSession, signOut } from "next-auth/react";
-import { useContext, useEffect, useState } from "react";
-import { DomainContext, featureEnabled } from "../domainContext";
-import { OpenApi, DefaultService } from "../api";
-import { CustomSession } from "./types";
-import useSessionStatus from "./useSessionStatus";
-import useSessionAccessToken from "./useSessionAccessToken";
-
-export default function useApi(): DefaultService | null {
- const api_url = useContext(DomainContext).api_url;
- const [api, setApi] = useState(null);
- const { isLoading, isAuthenticated } = useSessionStatus();
- const { accessToken, error } = useSessionAccessToken();
-
- if (!api_url) throw new Error("no API URL");
-
- useEffect(() => {
- if (error === "RefreshAccessTokenError") {
- signOut();
- }
- }, [error]);
-
- useEffect(() => {
- if (isLoading || (isAuthenticated && !accessToken)) {
- return;
- }
-
- const openApi = new OpenApi({
- BASE: api_url,
- TOKEN: accessToken || undefined,
- });
-
- setApi(openApi);
- }, [isLoading, isAuthenticated, accessToken]);
-
- return api?.default ?? null;
-}
diff --git a/www/app/lib/useLoginRequiredPages.ts b/www/app/lib/useLoginRequiredPages.ts
new file mode 100644
index 00000000..37ee96b1
--- /dev/null
+++ b/www/app/lib/useLoginRequiredPages.ts
@@ -0,0 +1,26 @@
+// for paths that are not supposed to be public
+import { PROTECTED_PAGES } from "./auth";
+import { usePathname } from "next/navigation";
+import { useAuth } from "./AuthProvider";
+import { useEffect } from "react";
+
+const HOME = "/" as const;
+
+export const useLoginRequiredPages = () => {
+ const pathname = usePathname();
+ const isProtected = PROTECTED_PAGES.test(pathname);
+ const auth = useAuth();
+ const isNotLoggedIn = auth.status === "unauthenticated";
+ // safety
+ const isLastDestination = pathname === HOME;
+ const shouldRedirect = isNotLoggedIn && isProtected && !isLastDestination;
+ useEffect(() => {
+ if (!shouldRedirect) return;
+ // on the backend, the redirect goes straight to the auth provider, but we don't have it because it's hidden inside next-auth middleware
+ // so we just "softly" lead the user to the main page
+ // warning: if HOME redirects somewhere else, we won't be protected by isLastDestination
+ window.location.href = HOME;
+ }, [shouldRedirect]);
+ // optionally save from blink, since window.location.href takes a bit of time
+ return shouldRedirect ? HOME : null;
+};
diff --git a/www/app/lib/useSessionAccessToken.ts b/www/app/lib/useSessionAccessToken.ts
deleted file mode 100644
index fc28c076..00000000
--- a/www/app/lib/useSessionAccessToken.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { useSession as useNextAuthSession } from "next-auth/react";
-import { CustomSession } from "./types";
-
-export default function useSessionAccessToken() {
- const { data: session } = useNextAuthSession();
- const customSession = session as CustomSession;
- const naAccessToken = customSession?.accessToken;
- const naAccessTokenExpires = customSession?.accessTokenExpires;
- const naError = customSession?.error;
- const [accessToken, setAccessToken] = useState(null);
- const [accessTokenExpires, setAccessTokenExpires] = useState(
- null,
- );
- const [error, setError] = useState();
-
- useEffect(() => {
- if (naAccessToken !== accessToken) {
- setAccessToken(naAccessToken);
- }
- }, [naAccessToken]);
-
- useEffect(() => {
- if (naAccessTokenExpires !== accessTokenExpires) {
- setAccessTokenExpires(naAccessTokenExpires);
- }
- }, [naAccessTokenExpires]);
-
- useEffect(() => {
- if (naError !== error) {
- setError(naError);
- }
- }, [naError]);
-
- return {
- accessToken,
- accessTokenExpires,
- error,
- };
-}
diff --git a/www/app/lib/useSessionStatus.ts b/www/app/lib/useSessionStatus.ts
deleted file mode 100644
index 5629c025..00000000
--- a/www/app/lib/useSessionStatus.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { useSession as useNextAuthSession } from "next-auth/react";
-import { Session } from "next-auth";
-
-export default function useSessionStatus() {
- const { status: naStatus } = useNextAuthSession();
- const [status, setStatus] = useState("loading");
-
- useEffect(() => {
- if (naStatus !== "loading" && naStatus !== status) {
- setStatus(naStatus);
- }
- }, [naStatus]);
-
- return {
- status,
- isLoading: status === "loading",
- isAuthenticated: status === "authenticated",
- };
-}
diff --git a/www/app/lib/useSessionUser.ts b/www/app/lib/useSessionUser.ts
deleted file mode 100644
index 2da299f5..00000000
--- a/www/app/lib/useSessionUser.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { useSession as useNextAuthSession } from "next-auth/react";
-import { Session } from "next-auth";
-
-// user type with id, name, email
-export interface User {
- id?: string | null;
- name?: string | null;
- email?: string | null;
-}
-
-export default function useSessionUser() {
- const { data: session } = useNextAuthSession();
- const [user, setUser] = useState(null);
-
- useEffect(() => {
- if (!session?.user) {
- setUser(null);
- return;
- }
- if (JSON.stringify(session.user) !== JSON.stringify(user)) {
- setUser(session.user);
- }
- }, [session]);
-
- return {
- id: user?.id,
- name: user?.name,
- email: user?.email,
- };
-}
diff --git a/www/app/lib/useUserName.ts b/www/app/lib/useUserName.ts
new file mode 100644
index 00000000..80814281
--- /dev/null
+++ b/www/app/lib/useUserName.ts
@@ -0,0 +1,7 @@
+import { useAuth } from "./AuthProvider";
+
+export const useUserName = (): string | null | undefined => {
+ const auth = useAuth();
+ if (auth.status !== "authenticated") return undefined;
+ return auth.user?.name || null;
+};
diff --git a/www/app/lib/utils.ts b/www/app/lib/utils.ts
index 80d0d91b..122ab234 100644
--- a/www/app/lib/utils.ts
+++ b/www/app/lib/utils.ts
@@ -137,9 +137,28 @@ export function extractDomain(url) {
}
}
-export function assertExists(value: T | null | undefined, err?: string): T {
+export type NonEmptyString = string & { __brand: "NonEmptyString" };
+export const parseMaybeNonEmptyString = (
+ s: string,
+ trim = true,
+): NonEmptyString | null => {
+ s = trim ? s.trim() : s;
+ return s.length > 0 ? (s as NonEmptyString) : null;
+};
+export const parseNonEmptyString = (s: string, trim = true): NonEmptyString =>
+ assertExists(parseMaybeNonEmptyString(s, trim), "Expected non-empty string");
+
+export const assertExists = (
+ value: T | null | undefined,
+ err?: string,
+): T => {
if (value === null || value === undefined) {
throw new Error(`Assertion failed: ${err ?? "value is null or undefined"}`);
}
return value;
-}
+};
+
+export const assertExistsAndNonEmptyString = (
+ value: string | null | undefined,
+): NonEmptyString =>
+ parseNonEmptyString(assertExists(value, "Expected non-empty string"));
diff --git a/www/app/providers.tsx b/www/app/providers.tsx
index f0f1ea52..2e3b78eb 100644
--- a/www/app/providers.tsx
+++ b/www/app/providers.tsx
@@ -6,16 +6,26 @@ import system from "./styles/theme";
import { WherebyProvider } from "@whereby.com/browser-sdk/react";
import { Toaster } from "./components/ui/toaster";
import { NuqsAdapter } from "nuqs/adapters/next/app";
+import { QueryClientProvider } from "@tanstack/react-query";
+import { queryClient } from "./lib/queryClient";
+import { AuthProvider } from "./lib/AuthProvider";
+import { SessionProvider as SessionProviderNextAuth } from "next-auth/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
-
-
- {children}
-
-
-
+
+
+
+
+
+ {children}
+
+
+
+
+
+
);
}
diff --git a/www/app/reflector-api.d.ts b/www/app/reflector-api.d.ts
new file mode 100644
index 00000000..8a2cadb0
--- /dev/null
+++ b/www/app/reflector-api.d.ts
@@ -0,0 +1,2330 @@
+/**
+ * This file was auto-generated by openapi-typescript.
+ * Do not make direct changes to the file.
+ */
+
+export interface paths {
+ "/metrics": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Metrics
+ * @description Endpoint that serves Prometheus metrics.
+ */
+ get: operations["metrics"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/meetings/{meeting_id}/consent": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Meeting Audio Consent */
+ post: operations["v1_meeting_audio_consent"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/rooms": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Rooms List */
+ get: operations["v1_rooms_list"];
+ put?: never;
+ /** Rooms Create */
+ post: operations["v1_rooms_create"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/rooms/{room_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Rooms Get */
+ get: operations["v1_rooms_get"];
+ put?: never;
+ post?: never;
+ /** Rooms Delete */
+ delete: operations["v1_rooms_delete"];
+ options?: never;
+ head?: never;
+ /** Rooms Update */
+ patch: operations["v1_rooms_update"];
+ trace?: never;
+ };
+ "/v1/rooms/{room_name}/meeting": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Rooms Create Meeting */
+ post: operations["v1_rooms_create_meeting"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/rooms/{room_id}/webhook/test": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Rooms Test Webhook
+ * @description Test webhook configuration by sending a sample payload.
+ */
+ post: operations["v1_rooms_test_webhook"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcripts List */
+ get: operations["v1_transcripts_list"];
+ put?: never;
+ /** Transcripts Create */
+ post: operations["v1_transcripts_create"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/search": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Transcripts Search
+ * @description Full-text search across transcript titles and content.
+ */
+ get: operations["v1_transcripts_search"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get */
+ get: operations["v1_transcript_get"];
+ put?: never;
+ post?: never;
+ /** Transcript Delete */
+ delete: operations["v1_transcript_delete"];
+ options?: never;
+ head?: never;
+ /** Transcript Update */
+ patch: operations["v1_transcript_update"];
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/topics": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Topics */
+ get: operations["v1_transcript_get_topics"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/topics/with-words": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Topics With Words */
+ get: operations["v1_transcript_get_topics_with_words"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Topics With Words Per Speaker */
+ get: operations["v1_transcript_get_topics_with_words_per_speaker"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/zulip": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Transcript Post To Zulip */
+ post: operations["v1_transcript_post_to_zulip"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/audio/mp3": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Audio Mp3 */
+ get: operations["v1_transcript_get_audio_mp3"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ /** Transcript Get Audio Mp3 */
+ head: operations["v1_transcript_head_audio_mp3"];
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/audio/waveform": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Audio Waveform */
+ get: operations["v1_transcript_get_audio_waveform"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/participants": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Participants */
+ get: operations["v1_transcript_get_participants"];
+ put?: never;
+ /** Transcript Add Participant */
+ post: operations["v1_transcript_add_participant"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/participants/{participant_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Participant */
+ get: operations["v1_transcript_get_participant"];
+ put?: never;
+ post?: never;
+ /** Transcript Delete Participant */
+ delete: operations["v1_transcript_delete_participant"];
+ options?: never;
+ head?: never;
+ /** Transcript Update Participant */
+ patch: operations["v1_transcript_update_participant"];
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/speaker/assign": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ /** Transcript Assign Speaker */
+ patch: operations["v1_transcript_assign_speaker"];
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/speaker/merge": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ /** Transcript Merge Speaker */
+ patch: operations["v1_transcript_merge_speaker"];
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/record/upload": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Transcript Record Upload */
+ post: operations["v1_transcript_record_upload"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/events": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Transcript Get Websocket Events */
+ get: operations["v1_transcript_get_websocket_events"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/record/webrtc": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Transcript Record Webrtc */
+ post: operations["v1_transcript_record_webrtc"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/transcripts/{transcript_id}/process": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Transcript Process */
+ post: operations["v1_transcript_process"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/me": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** User Me */
+ get: operations["v1_user_me"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/zulip/streams": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Zulip Get Streams
+ * @description Get all Zulip streams.
+ */
+ get: operations["v1_zulip_get_streams"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/zulip/streams/{stream_id}/topics": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Zulip Get Topics
+ * @description Get all topics for a specific Zulip stream.
+ */
+ get: operations["v1_zulip_get_topics"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/v1/whereby": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Whereby Webhook */
+ post: operations["v1_whereby_webhook"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+}
+export type webhooks = Record;
+export interface components {
+ schemas: {
+ /** AudioWaveform */
+ AudioWaveform: {
+ /** Data */
+ data: number[];
+ };
+ /** Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post */
+ Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post: {
+ /**
+ * Chunk
+ * Format: binary
+ */
+ chunk: string;
+ };
+ /** CreateParticipant */
+ CreateParticipant: {
+ /** Speaker */
+ speaker?: number | null;
+ /** Name */
+ name: string;
+ };
+ /** CreateRoom */
+ CreateRoom: {
+ /** Name */
+ name: string;
+ /** Zulip Auto Post */
+ zulip_auto_post: boolean;
+ /** Zulip Stream */
+ zulip_stream: string;
+ /** Zulip Topic */
+ zulip_topic: string;
+ /** Is Locked */
+ is_locked: boolean;
+ /** Room Mode */
+ room_mode: string;
+ /** Recording Type */
+ recording_type: string;
+ /** Recording Trigger */
+ recording_trigger: string;
+ /** Is Shared */
+ is_shared: boolean;
+ /** Webhook Url */
+ webhook_url: string;
+ /** Webhook Secret */
+ webhook_secret: string;
+ };
+ /** CreateTranscript */
+ CreateTranscript: {
+ /** Name */
+ name: string;
+ /**
+ * Source Language
+ * @default en
+ */
+ source_language: string;
+ /**
+ * Target Language
+ * @default en
+ */
+ target_language: string;
+ source_kind?: components["schemas"]["SourceKind"] | null;
+ };
+ /** DeletionStatus */
+ DeletionStatus: {
+ /** Status */
+ status: string;
+ };
+ /** GetTranscript */
+ GetTranscript: {
+ /** Id */
+ id: string;
+ /** User Id */
+ user_id: string | null;
+ /** Name */
+ name: string;
+ /**
+ * Status
+ * @enum {string}
+ */
+ status:
+ | "idle"
+ | "uploaded"
+ | "recording"
+ | "processing"
+ | "error"
+ | "ended";
+ /** Locked */
+ locked: boolean;
+ /** Duration */
+ duration: number;
+ /** Title */
+ title: string | null;
+ /** Short Summary */
+ short_summary: string | null;
+ /** Long Summary */
+ long_summary: string | null;
+ /** Created At */
+ created_at: string;
+ /**
+ * Share Mode
+ * @default private
+ */
+ share_mode: string;
+ /** Source Language */
+ source_language: string | null;
+ /** Target Language */
+ target_language: string | null;
+ /** Reviewed */
+ reviewed: boolean;
+ /** Meeting Id */
+ meeting_id: string | null;
+ source_kind: components["schemas"]["SourceKind"];
+ /** Room Id */
+ room_id?: string | null;
+ /** Room Name */
+ room_name?: string | null;
+ /** Audio Deleted */
+ audio_deleted?: boolean | null;
+ /** Participants */
+ participants: components["schemas"]["TranscriptParticipant"][] | null;
+ };
+ /** GetTranscriptMinimal */
+ GetTranscriptMinimal: {
+ /** Id */
+ id: string;
+ /** User Id */
+ user_id: string | null;
+ /** Name */
+ name: string;
+ /**
+ * Status
+ * @enum {string}
+ */
+ status:
+ | "idle"
+ | "uploaded"
+ | "recording"
+ | "processing"
+ | "error"
+ | "ended";
+ /** Locked */
+ locked: boolean;
+ /** Duration */
+ duration: number;
+ /** Title */
+ title: string | null;
+ /** Short Summary */
+ short_summary: string | null;
+ /** Long Summary */
+ long_summary: string | null;
+ /** Created At */
+ created_at: string;
+ /**
+ * Share Mode
+ * @default private
+ */
+ share_mode: string;
+ /** Source Language */
+ source_language: string | null;
+ /** Target Language */
+ target_language: string | null;
+ /** Reviewed */
+ reviewed: boolean;
+ /** Meeting Id */
+ meeting_id: string | null;
+ source_kind: components["schemas"]["SourceKind"];
+ /** Room Id */
+ room_id?: string | null;
+ /** Room Name */
+ room_name?: string | null;
+ /** Audio Deleted */
+ audio_deleted?: boolean | null;
+ };
+ /** GetTranscriptSegmentTopic */
+ GetTranscriptSegmentTopic: {
+ /** Text */
+ text: string;
+ /** Start */
+ start: number;
+ /** Speaker */
+ speaker: number;
+ };
+ /** GetTranscriptTopic */
+ GetTranscriptTopic: {
+ /** Id */
+ id: string;
+ /** Title */
+ title: string;
+ /** Summary */
+ summary: string;
+ /** Timestamp */
+ timestamp: number;
+ /** Duration */
+ duration: number | null;
+ /** Transcript */
+ transcript: string;
+ /**
+ * Segments
+ * @default []
+ */
+ segments: components["schemas"]["GetTranscriptSegmentTopic"][];
+ };
+ /** GetTranscriptTopicWithWords */
+ GetTranscriptTopicWithWords: {
+ /** Id */
+ id: string;
+ /** Title */
+ title: string;
+ /** Summary */
+ summary: string;
+ /** Timestamp */
+ timestamp: number;
+ /** Duration */
+ duration: number | null;
+ /** Transcript */
+ transcript: string;
+ /**
+ * Segments
+ * @default []
+ */
+ segments: components["schemas"]["GetTranscriptSegmentTopic"][];
+ /**
+ * Words
+ * @default []
+ */
+ words: components["schemas"]["Word"][];
+ };
+ /** GetTranscriptTopicWithWordsPerSpeaker */
+ GetTranscriptTopicWithWordsPerSpeaker: {
+ /** Id */
+ id: string;
+ /** Title */
+ title: string;
+ /** Summary */
+ summary: string;
+ /** Timestamp */
+ timestamp: number;
+ /** Duration */
+ duration: number | null;
+ /** Transcript */
+ transcript: string;
+ /**
+ * Segments
+ * @default []
+ */
+ segments: components["schemas"]["GetTranscriptSegmentTopic"][];
+ /**
+ * Words Per Speaker
+ * @default []
+ */
+ words_per_speaker: components["schemas"]["SpeakerWords"][];
+ };
+ /** HTTPValidationError */
+ HTTPValidationError: {
+ /** Detail */
+ detail?: components["schemas"]["ValidationError"][];
+ };
+ /** Meeting */
+ Meeting: {
+ /** Id */
+ id: string;
+ /** Room Name */
+ room_name: string;
+ /** Room Url */
+ room_url: string;
+ /** Host Room Url */
+ host_room_url: string;
+ /**
+ * Start Date
+ * Format: date-time
+ */
+ start_date: string;
+ /**
+ * End Date
+ * Format: date-time
+ */
+ end_date: string;
+ /**
+ * Recording Type
+ * @default cloud
+ * @enum {string}
+ */
+ recording_type: "none" | "local" | "cloud";
+ };
+ /** MeetingConsentRequest */
+ MeetingConsentRequest: {
+ /** Consent Given */
+ consent_given: boolean;
+ };
+ /** Page[GetTranscriptMinimal] */
+ Page_GetTranscriptMinimal_: {
+ /** Items */
+ items: components["schemas"]["GetTranscriptMinimal"][];
+ /** Total */
+ total?: number | null;
+ /** Page */
+ page: number | null;
+ /** Size */
+ size: number | null;
+ /** Pages */
+ pages?: number | null;
+ };
+ /** Page[RoomDetails] */
+ Page_RoomDetails_: {
+ /** Items */
+ items: components["schemas"]["RoomDetails"][];
+ /** Total */
+ total?: number | null;
+ /** Page */
+ page: number | null;
+ /** Size */
+ size: number | null;
+ /** Pages */
+ pages?: number | null;
+ };
+ /** Participant */
+ Participant: {
+ /** Id */
+ id: string;
+ /** Speaker */
+ speaker: number | null;
+ /** Name */
+ name: string;
+ };
+ /** Room */
+ Room: {
+ /** Id */
+ id: string;
+ /** Name */
+ name: string;
+ /** User Id */
+ user_id: string;
+ /**
+ * Created At
+ * Format: date-time
+ */
+ created_at: string;
+ /** Zulip Auto Post */
+ zulip_auto_post: boolean;
+ /** Zulip Stream */
+ zulip_stream: string;
+ /** Zulip Topic */
+ zulip_topic: string;
+ /** Is Locked */
+ is_locked: boolean;
+ /** Room Mode */
+ room_mode: string;
+ /** Recording Type */
+ recording_type: string;
+ /** Recording Trigger */
+ recording_trigger: string;
+ /** Is Shared */
+ is_shared: boolean;
+ };
+ /** RoomDetails */
+ RoomDetails: {
+ /** Id */
+ id: string;
+ /** Name */
+ name: string;
+ /** User Id */
+ user_id: string;
+ /**
+ * Created At
+ * Format: date-time
+ */
+ created_at: string;
+ /** Zulip Auto Post */
+ zulip_auto_post: boolean;
+ /** Zulip Stream */
+ zulip_stream: string;
+ /** Zulip Topic */
+ zulip_topic: string;
+ /** Is Locked */
+ is_locked: boolean;
+ /** Room Mode */
+ room_mode: string;
+ /** Recording Type */
+ recording_type: string;
+ /** Recording Trigger */
+ recording_trigger: string;
+ /** Is Shared */
+ is_shared: boolean;
+ /** Webhook Url */
+ webhook_url: string | null;
+ /** Webhook Secret */
+ webhook_secret: string | null;
+ };
+ /** RtcOffer */
+ RtcOffer: {
+ /** Sdp */
+ sdp: string;
+ /** Type */
+ type: string;
+ };
+ /** SearchResponse */
+ SearchResponse: {
+ /** Results */
+ results: components["schemas"]["SearchResult"][];
+ /**
+ * Total
+ * @description Total number of search results
+ */
+ total: number;
+ /** Query */
+ query?: string | null;
+ /**
+ * Limit
+ * @description Results per page
+ */
+ limit: number;
+ /**
+ * Offset
+ * @description Number of results to skip
+ */
+ offset: number;
+ };
+ /**
+ * SearchResult
+ * @description Public search result model with computed fields.
+ */
+ SearchResult: {
+ /** Id */
+ id: string;
+ /** Title */
+ title?: string | null;
+ /** User Id */
+ user_id?: string | null;
+ /** Room Id */
+ room_id?: string | null;
+ /** Room Name */
+ room_name?: string | null;
+ source_kind: components["schemas"]["SourceKind"];
+ /** Created At */
+ created_at: string;
+ /** Status */
+ status: string;
+ /** Rank */
+ rank: number;
+ /**
+ * Duration
+ * @description Duration in seconds
+ */
+ duration: number | null;
+ /**
+ * Search Snippets
+ * @description Text snippets around search matches
+ */
+ search_snippets: string[];
+ /**
+ * Total Match Count
+ * @description Total number of matches found in the transcript
+ * @default 0
+ */
+ total_match_count: number;
+ };
+ /**
+ * SourceKind
+ * @enum {string}
+ */
+ SourceKind: "room" | "live" | "file";
+ /** SpeakerAssignment */
+ SpeakerAssignment: {
+ /** Speaker */
+ speaker?: number | null;
+ /** Participant */
+ participant?: string | null;
+ /** Timestamp From */
+ timestamp_from: number;
+ /** Timestamp To */
+ timestamp_to: number;
+ };
+ /** SpeakerAssignmentStatus */
+ SpeakerAssignmentStatus: {
+ /** Status */
+ status: string;
+ };
+ /** SpeakerMerge */
+ SpeakerMerge: {
+ /** Speaker From */
+ speaker_from: number;
+ /** Speaker To */
+ speaker_to: number;
+ };
+ /** SpeakerWords */
+ SpeakerWords: {
+ /** Speaker */
+ speaker: number;
+ /** Words */
+ words: components["schemas"]["Word"][];
+ };
+ /** Stream */
+ Stream: {
+ /** Stream Id */
+ stream_id: number;
+ /** Name */
+ name: string;
+ };
+ /** Topic */
+ Topic: {
+ /** Name */
+ name: string;
+ };
+ /** TranscriptParticipant */
+ TranscriptParticipant: {
+ /** Id */
+ id?: string;
+ /** Speaker */
+ speaker: number | null;
+ /** Name */
+ name: string;
+ };
+ /** UpdateParticipant */
+ UpdateParticipant: {
+ /** Speaker */
+ speaker?: number | null;
+ /** Name */
+ name?: string | null;
+ };
+ /** UpdateRoom */
+ UpdateRoom: {
+ /** Name */
+ name: string;
+ /** Zulip Auto Post */
+ zulip_auto_post: boolean;
+ /** Zulip Stream */
+ zulip_stream: string;
+ /** Zulip Topic */
+ zulip_topic: string;
+ /** Is Locked */
+ is_locked: boolean;
+ /** Room Mode */
+ room_mode: string;
+ /** Recording Type */
+ recording_type: string;
+ /** Recording Trigger */
+ recording_trigger: string;
+ /** Is Shared */
+ is_shared: boolean;
+ /** Webhook Url */
+ webhook_url: string;
+ /** Webhook Secret */
+ webhook_secret: string;
+ };
+ /** UpdateTranscript */
+ UpdateTranscript: {
+ /** Name */
+ name?: string | null;
+ /** Locked */
+ locked?: boolean | null;
+ /** Title */
+ title?: string | null;
+ /** Short Summary */
+ short_summary?: string | null;
+ /** Long Summary */
+ long_summary?: string | null;
+ /** Share Mode */
+ share_mode?: ("public" | "semi-private" | "private") | null;
+ /** Participants */
+ participants?: components["schemas"]["TranscriptParticipant"][] | null;
+ /** Reviewed */
+ reviewed?: boolean | null;
+ /** Audio Deleted */
+ audio_deleted?: boolean | null;
+ };
+ /** UserInfo */
+ UserInfo: {
+ /** Sub */
+ sub: string;
+ /** Email */
+ email: string | null;
+ /** Email Verified */
+ email_verified: boolean | null;
+ };
+ /** ValidationError */
+ ValidationError: {
+ /** Location */
+ loc: (string | number)[];
+ /** Message */
+ msg: string;
+ /** Error Type */
+ type: string;
+ };
+ /** WebhookTestResult */
+ WebhookTestResult: {
+ /** Success */
+ success: boolean;
+ /**
+ * Message
+ * @default
+ */
+ message: string;
+ /**
+ * Error
+ * @default
+ */
+ error: string;
+ /** Status Code */
+ status_code?: number | null;
+ /** Response Preview */
+ response_preview?: string | null;
+ };
+ /** WherebyWebhookEvent */
+ WherebyWebhookEvent: {
+ /** Apiversion */
+ apiVersion: string;
+ /** Id */
+ id: string;
+ /**
+ * Createdat
+ * Format: date-time
+ */
+ createdAt: string;
+ /** Type */
+ type: string;
+ /** Data */
+ data: {
+ [key: string]: unknown;
+ };
+ };
+ /** Word */
+ Word: {
+ /** Text */
+ text: string;
+ /**
+ * Start
+ * @description Time in seconds with float part
+ */
+ start: number;
+ /**
+ * End
+ * @description Time in seconds with float part
+ */
+ end: number;
+ /**
+ * Speaker
+ * @default 0
+ */
+ speaker: number;
+ };
+ };
+ responses: never;
+ parameters: never;
+ requestBodies: never;
+ headers: never;
+ pathItems: never;
+}
+export type $defs = Record;
+export interface operations {
+ metrics: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ };
+ };
+ v1_meeting_audio_consent: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ meeting_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["MeetingConsentRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_list: {
+ parameters: {
+ query?: {
+ /** @description Page number */
+ page?: number;
+ /** @description Page size */
+ size?: number;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Page_RoomDetails_"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_create: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["CreateRoom"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Room"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ room_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RoomDetails"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_delete: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ room_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["DeletionStatus"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_update: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ room_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["UpdateRoom"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RoomDetails"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_create_meeting: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ room_name: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Meeting"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_rooms_test_webhook: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ room_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["WebhookTestResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcripts_list: {
+ parameters: {
+ query?: {
+ source_kind?: components["schemas"]["SourceKind"] | null;
+ room_id?: string | null;
+ search_term?: string | null;
+ /** @description Page number */
+ page?: number;
+ /** @description Page size */
+ size?: number;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Page_GetTranscriptMinimal_"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcripts_create: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["CreateTranscript"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscript"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcripts_search: {
+ parameters: {
+ query: {
+ /** @description Search query text */
+ q: string;
+ /** @description Results per page */
+ limit?: number;
+ /** @description Number of results to skip */
+ offset?: number;
+ room_id?: string | null;
+ source_kind?: components["schemas"]["SourceKind"] | null;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SearchResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscript"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_delete: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["DeletionStatus"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_update: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["UpdateTranscript"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscript"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_topics: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscriptTopic"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_topics_with_words: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscriptTopicWithWords"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_topics_with_words_per_speaker: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ topic_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GetTranscriptTopicWithWordsPerSpeaker"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_post_to_zulip: {
+ parameters: {
+ query: {
+ stream: string;
+ topic: string;
+ include_topics: boolean;
+ };
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_audio_mp3: {
+ parameters: {
+ query?: {
+ token?: string | null;
+ };
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_head_audio_mp3: {
+ parameters: {
+ query?: {
+ token?: string | null;
+ };
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_audio_waveform: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["AudioWaveform"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_participants: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Participant"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_add_participant: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["CreateParticipant"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Participant"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_participant: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ participant_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Participant"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_delete_participant: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ participant_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["DeletionStatus"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_update_participant: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ participant_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["UpdateParticipant"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Participant"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_assign_speaker: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["SpeakerAssignment"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SpeakerAssignmentStatus"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_merge_speaker: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["SpeakerMerge"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SpeakerAssignmentStatus"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_record_upload: {
+ parameters: {
+ query: {
+ chunk_number: number;
+ total_chunks: number;
+ };
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "multipart/form-data": components["schemas"]["Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_get_websocket_events: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_record_webrtc: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["RtcOffer"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_transcript_process: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ transcript_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_user_me: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["UserInfo"] | null;
+ };
+ };
+ };
+ };
+ v1_zulip_get_streams: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Stream"][];
+ };
+ };
+ };
+ };
+ v1_zulip_get_topics: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ stream_id: number;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Topic"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ v1_whereby_webhook: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["WherebyWebhookEvent"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+}
diff --git a/www/jest.config.js b/www/jest.config.js
new file mode 100644
index 00000000..d2f3247b
--- /dev/null
+++ b/www/jest.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ preset: "ts-jest",
+ testEnvironment: "node",
+ roots: ["/app"],
+ testMatch: ["**/__tests__/**/*.test.ts"],
+ collectCoverage: true,
+ collectCoverageFrom: ["app/**/*.ts", "!app/**/*.d.ts"],
+};
diff --git a/www/middleware.ts b/www/middleware.ts
index 39145220..2b60d715 100644
--- a/www/middleware.ts
+++ b/www/middleware.ts
@@ -1,16 +1,7 @@
import { withAuth } from "next-auth/middleware";
import { getConfig } from "./app/lib/edgeConfig";
import { NextResponse } from "next/server";
-
-const LOGIN_REQUIRED_PAGES = [
- "/transcripts/[!new]",
- "/browse(.*)",
- "/rooms(.*)",
-];
-
-const PROTECTED_PAGES = new RegExp(
- LOGIN_REQUIRED_PAGES.map((page) => `^${page}$`).join("|"),
-);
+import { PROTECTED_PAGES } from "./app/lib/auth";
export const config = {
matcher: [
diff --git a/www/next.config.js b/www/next.config.js
index e37d5402..bbc3f710 100644
--- a/www/next.config.js
+++ b/www/next.config.js
@@ -2,6 +2,9 @@
const nextConfig = {
output: "standalone",
experimental: { esmExternals: "loose" },
+ env: {
+ IS_CI: process.env.IS_CI,
+ },
};
module.exports = nextConfig;
diff --git a/www/openapi-ts.config.ts b/www/openapi-ts.config.ts
deleted file mode 100644
index 9304b8f7..00000000
--- a/www/openapi-ts.config.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { defineConfig } from "@hey-api/openapi-ts";
-
-export default defineConfig({
- client: "axios",
- name: "OpenApi",
- input: "http://127.0.0.1:1250/openapi.json",
- output: {
- path: "./app/api",
- format: "prettier",
- },
- services: {
- asClass: true,
- },
-});
diff --git a/www/package.json b/www/package.json
index 482a29f6..b7511147 100644
--- a/www/package.json
+++ b/www/package.json
@@ -8,7 +8,8 @@
"start": "next start",
"lint": "next lint",
"format": "prettier --write .",
- "openapi": "openapi-ts"
+ "openapi": "openapi-typescript http://127.0.0.1:1250/openapi.json -o ./app/reflector-api.d.ts",
+ "test": "jest"
},
"dependencies": {
"@chakra-ui/react": "^3.24.2",
@@ -17,21 +18,24 @@
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@sentry/nextjs": "^7.77.0",
+ "@tanstack/react-query": "^5.85.9",
+ "@types/ioredis": "^5.0.0",
"@vercel/edge-config": "^0.4.1",
- "@vercel/kv": "^2.0.0",
"@whereby.com/browser-sdk": "^3.3.4",
"autoprefixer": "10.4.20",
"axios": "^1.8.2",
"eslint": "^9.33.0",
"eslint-config-next": "^14.2.31",
"fontawesome": "^5.6.3",
- "ioredis": "^5.4.1",
+ "ioredis": "^5.7.0",
"jest-worker": "^29.6.2",
"lucide-react": "^0.525.0",
"next": "^14.2.30",
"next-auth": "^4.24.7",
"next-themes": "^0.4.6",
"nuqs": "^2.4.3",
+ "openapi-fetch": "^0.14.0",
+ "openapi-react-query": "^0.5.0",
"postcss": "8.4.31",
"prop-types": "^15.8.1",
"react": "^18.2.0",
@@ -41,21 +45,24 @@
"react-markdown": "^9.0.0",
"react-qr-code": "^2.0.12",
"react-select-search": "^4.1.7",
- "redlock": "^5.0.0-beta.2",
"sass": "^1.63.6",
"simple-peer": "^9.11.1",
"tailwindcss": "^3.3.2",
"typescript": "^5.1.6",
- "wavesurfer.js": "^7.4.2"
+ "wavesurfer.js": "^7.4.2",
+ "zod": "^4.1.5"
},
"main": "index.js",
"repository": "https://github.com/Monadical-SAS/reflector-ui.git",
"author": "Andreas ",
"license": "All Rights Reserved",
"devDependencies": {
- "@hey-api/openapi-ts": "^0.48.0",
+ "@types/jest": "^30.0.0",
"@types/react": "18.2.20",
+ "jest": "^30.1.3",
+ "openapi-typescript": "^7.9.1",
"prettier": "^3.0.0",
+ "ts-jest": "^29.4.1",
"vercel": "^37.3.0"
},
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml
index 55aef9c8..14b42c55 100644
--- a/www/pnpm-lock.yaml
+++ b/www/pnpm-lock.yaml
@@ -24,13 +24,16 @@ importers:
version: 0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@18.3.1)
"@sentry/nextjs":
specifier: ^7.77.0
- version: 7.120.4(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ version: 7.120.4(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ "@tanstack/react-query":
+ specifier: ^5.85.9
+ version: 5.85.9(react@18.3.1)
+ "@types/ioredis":
+ specifier: ^5.0.0
+ version: 5.0.0
"@vercel/edge-config":
specifier: ^0.4.1
version: 0.4.1
- "@vercel/kv":
- specifier: ^2.0.0
- version: 2.0.0
"@whereby.com/browser-sdk":
specifier: ^3.3.4
version: 3.13.1(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -50,7 +53,7 @@ importers:
specifier: ^5.6.3
version: 5.6.3
ioredis:
- specifier: ^5.4.1
+ specifier: ^5.7.0
version: 5.7.0
jest-worker:
specifier: ^29.6.2
@@ -60,16 +63,22 @@ importers:
version: 0.525.0(react@18.3.1)
next:
specifier: ^14.2.30
- version: 14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ version: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
next-auth:
specifier: ^4.24.7
- version: 4.24.11(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 4.24.11(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nuqs:
specifier: ^2.4.3
- version: 2.4.3(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ version: 2.4.3(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ openapi-fetch:
+ specifier: ^0.14.0
+ version: 0.14.0
+ openapi-react-query:
+ specifier: ^0.5.0
+ version: 0.5.0(@tanstack/react-query@5.85.9(react@18.3.1))(openapi-fetch@0.14.0)
postcss:
specifier: 8.4.31
version: 8.4.31
@@ -97,9 +106,6 @@ importers:
react-select-search:
specifier: ^4.1.7
version: 4.1.8(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- redlock:
- specifier: ^5.0.0-beta.2
- version: 5.0.0-beta.2
sass:
specifier: ^1.63.6
version: 1.90.0
@@ -115,16 +121,28 @@ importers:
wavesurfer.js:
specifier: ^7.4.2
version: 7.10.1
+ zod:
+ specifier: ^4.1.5
+ version: 4.1.5
devDependencies:
- "@hey-api/openapi-ts":
- specifier: ^0.48.0
- version: 0.48.3(typescript@5.9.2)
+ "@types/jest":
+ specifier: ^30.0.0
+ version: 30.0.0
"@types/react":
specifier: 18.2.20
version: 18.2.20
+ jest:
+ specifier: ^30.1.3
+ version: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ openapi-typescript:
+ specifier: ^7.9.1
+ version: 7.9.1(typescript@5.9.2)
prettier:
specifier: ^3.0.0
version: 3.6.2
+ ts-jest:
+ specifier: ^29.4.1
+ version: 29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)))(typescript@5.9.2)
vercel:
specifier: ^37.3.0
version: 37.14.0
@@ -137,12 +155,12 @@ packages:
}
engines: { node: ">=10" }
- "@apidevtools/json-schema-ref-parser@11.6.4":
+ "@ampproject/remapping@2.3.0":
resolution:
{
- integrity: sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==,
+ integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==,
}
- engines: { node: ">= 16" }
+ engines: { node: ">=6.0.0" }
"@ark-ui/react@5.18.2":
resolution:
@@ -160,6 +178,20 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@babel/compat-data@7.28.0":
+ resolution:
+ {
+ integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==,
+ }
+ engines: { node: ">=6.9.0" }
+
+ "@babel/core@7.28.3":
+ resolution:
+ {
+ integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==,
+ }
+ engines: { node: ">=6.9.0" }
+
"@babel/generator@7.28.0":
resolution:
{
@@ -167,6 +199,20 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@babel/generator@7.28.3":
+ resolution:
+ {
+ integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==,
+ }
+ engines: { node: ">=6.9.0" }
+
+ "@babel/helper-compilation-targets@7.27.2":
+ resolution:
+ {
+ integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==,
+ }
+ engines: { node: ">=6.9.0" }
+
"@babel/helper-globals@7.28.0":
resolution:
{
@@ -181,6 +227,22 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@babel/helper-module-transforms@7.28.3":
+ resolution:
+ {
+ integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0
+
+ "@babel/helper-plugin-utils@7.27.1":
+ resolution:
+ {
+ integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==,
+ }
+ engines: { node: ">=6.9.0" }
+
"@babel/helper-string-parser@7.27.1":
resolution:
{
@@ -195,6 +257,20 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@babel/helper-validator-option@7.27.1":
+ resolution:
+ {
+ integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==,
+ }
+ engines: { node: ">=6.9.0" }
+
+ "@babel/helpers@7.28.3":
+ resolution:
+ {
+ integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==,
+ }
+ engines: { node: ">=6.9.0" }
+
"@babel/parser@7.28.0":
resolution:
{
@@ -203,6 +279,156 @@ packages:
engines: { node: ">=6.0.0" }
hasBin: true
+ "@babel/parser@7.28.3":
+ resolution:
+ {
+ integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==,
+ }
+ engines: { node: ">=6.0.0" }
+ hasBin: true
+
+ "@babel/plugin-syntax-async-generators@7.8.4":
+ resolution:
+ {
+ integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-bigint@7.8.3":
+ resolution:
+ {
+ integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-class-properties@7.12.13":
+ resolution:
+ {
+ integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-class-static-block@7.14.5":
+ resolution:
+ {
+ integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-import-attributes@7.27.1":
+ resolution:
+ {
+ integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-import-meta@7.10.4":
+ resolution:
+ {
+ integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-json-strings@7.8.3":
+ resolution:
+ {
+ integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-jsx@7.27.1":
+ resolution:
+ {
+ integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-logical-assignment-operators@7.10.4":
+ resolution:
+ {
+ integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-nullish-coalescing-operator@7.8.3":
+ resolution:
+ {
+ integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-numeric-separator@7.10.4":
+ resolution:
+ {
+ integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-object-rest-spread@7.8.3":
+ resolution:
+ {
+ integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-optional-catch-binding@7.8.3":
+ resolution:
+ {
+ integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-optional-chaining@7.8.3":
+ resolution:
+ {
+ integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-private-property-in-object@7.14.5":
+ resolution:
+ {
+ integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-top-level-await@7.14.5":
+ resolution:
+ {
+ integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
+ "@babel/plugin-syntax-typescript@7.27.1":
+ resolution:
+ {
+ integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==,
+ }
+ engines: { node: ">=6.9.0" }
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+
"@babel/runtime@7.28.2":
resolution:
{
@@ -224,6 +450,13 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@babel/traverse@7.28.3":
+ resolution:
+ {
+ integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==,
+ }
+ engines: { node: ">=6.9.0" }
+
"@babel/types@7.28.2":
resolution:
{
@@ -231,6 +464,12 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@bcoe/v8-coverage@0.2.3":
+ resolution:
+ {
+ integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==,
+ }
+
"@chakra-ui/react@3.24.2":
resolution:
{
@@ -516,16 +755,6 @@ packages:
"@fortawesome/fontawesome-svg-core": ~1 || ~6 || ~7
react: ^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0
- "@hey-api/openapi-ts@0.48.3":
- resolution:
- {
- integrity: sha512-R53Nr4Gicz77icS+RiH0fwHa9A0uFPtzsjC8SBaGwtOel5ZyxeBbayWE6HhE789hp3dok9pegwWncwwOrr4WFA==,
- }
- engines: { node: ^18.0.0 || >=20.0.0 }
- hasBin: true
- peerDependencies:
- typescript: ^5.x
-
"@humanfs/core@0.19.1":
resolution:
{
@@ -573,10 +802,10 @@ packages:
integrity: sha512-p+Zh1sb6EfrfVaS86jlHGQ9HA66fJhV9x5LiE5vCbZtXEHAuhcmUZUdZ4WrFpUBfNalr2OkAJI5AcKEQF+Lebw==,
}
- "@ioredis/commands@1.3.0":
+ "@ioredis/commands@1.3.1":
resolution:
{
- integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==,
+ integrity: sha512-bYtU8avhGIcje3IhvF9aSjsa5URMZBHnwKtOvXsT4sfYy9gppW11gLPT/9oNqlJZD47yPKveQFTAFWpHjKvUoQ==,
}
"@isaacs/cliui@8.0.2":
@@ -586,6 +815,107 @@ packages:
}
engines: { node: ">=12" }
+ "@istanbuljs/load-nyc-config@1.1.0":
+ resolution:
+ {
+ integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==,
+ }
+ engines: { node: ">=8" }
+
+ "@istanbuljs/schema@0.1.3":
+ resolution:
+ {
+ integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==,
+ }
+ engines: { node: ">=8" }
+
+ "@jest/console@30.1.2":
+ resolution:
+ {
+ integrity: sha512-BGMAxj8VRmoD0MoA/jo9alMXSRoqW8KPeqOfEo1ncxnRLatTBCpRoOwlwlEMdudp68Q6WSGwYrrLtTGOh8fLzw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/core@30.1.3":
+ resolution:
+ {
+ integrity: sha512-LIQz7NEDDO1+eyOA2ZmkiAyYvZuo6s1UxD/e2IHldR6D7UYogVq3arTmli07MkENLq6/3JEQjp0mA8rrHHJ8KQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ "@jest/diff-sequences@30.0.1":
+ resolution:
+ {
+ integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/environment@30.1.2":
+ resolution:
+ {
+ integrity: sha512-N8t1Ytw4/mr9uN28OnVf0SYE2dGhaIxOVYcwsf9IInBKjvofAjbFRvedvBBlyTYk2knbJTiEjEJ2PyyDIBnd9w==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/expect-utils@30.1.2":
+ resolution:
+ {
+ integrity: sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/expect@30.1.2":
+ resolution:
+ {
+ integrity: sha512-tyaIExOwQRCxPCGNC05lIjWJztDwk2gPDNSDGg1zitXJJ8dC3++G/CRjE5mb2wQsf89+lsgAgqxxNpDLiCViTA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/fake-timers@30.1.2":
+ resolution:
+ {
+ integrity: sha512-Beljfv9AYkr9K+ETX9tvV61rJTY706BhBUtiaepQHeEGfe0DbpvUA5Z3fomwc5Xkhns6NWrcFDZn+72fLieUnA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/get-type@30.1.0":
+ resolution:
+ {
+ integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/globals@30.1.2":
+ resolution:
+ {
+ integrity: sha512-teNTPZ8yZe3ahbYnvnVRDeOjr+3pu2uiAtNtrEsiMjVPPj+cXd5E/fr8BL7v/T7F31vYdEHrI5cC/2OoO/vM9A==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/pattern@30.0.1":
+ resolution:
+ {
+ integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/reporters@30.1.3":
+ resolution:
+ {
+ integrity: sha512-VWEQmJWfXMOrzdFEOyGjUEOuVXllgZsoPtEHZzfdNz18RmzJ5nlR6kp8hDdY8dDS1yGOXAY7DHT+AOHIPSBV0w==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
"@jest/schemas@29.6.3":
resolution:
{
@@ -593,6 +923,48 @@ packages:
}
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
+ "@jest/schemas@30.0.5":
+ resolution:
+ {
+ integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/snapshot-utils@30.1.2":
+ resolution:
+ {
+ integrity: sha512-vHoMTpimcPSR7OxS2S0V1Cpg8eKDRxucHjoWl5u4RQcnxqQrV3avETiFpl8etn4dqxEGarBeHbIBety/f8mLXw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/source-map@30.0.1":
+ resolution:
+ {
+ integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/test-result@30.1.3":
+ resolution:
+ {
+ integrity: sha512-P9IV8T24D43cNRANPPokn7tZh0FAFnYS2HIfi5vK18CjRkTDR9Y3e1BoEcAJnl4ghZZF4Ecda4M/k41QkvurEQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/test-sequencer@30.1.3":
+ resolution:
+ {
+ integrity: sha512-82J+hzC0qeQIiiZDThh+YUadvshdBswi5nuyXlEmXzrhw5ZQSRHeQ5LpVMD/xc8B3wPePvs6VMzHnntxL+4E3w==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ "@jest/transform@30.1.2":
+ resolution:
+ {
+ integrity: sha512-UYYFGifSgfjujf1Cbd3iU/IQoSd6uwsj8XHj5DSDf5ERDcWMdJOPTkHWXj4U+Z/uMagyOQZ6Vne8C4nRIrCxqA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
"@jest/types@29.6.3":
resolution:
{
@@ -600,6 +972,13 @@ packages:
}
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
+ "@jest/types@30.0.5":
+ resolution:
+ {
+ integrity: sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
"@jridgewell/gen-mapping@0.3.13":
resolution:
{
@@ -631,12 +1010,6 @@ packages:
integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==,
}
- "@jsdevtools/ono@7.1.3":
- resolution:
- {
- integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==,
- }
-
"@mapbox/node-pre-gyp@1.0.11":
resolution:
{
@@ -914,6 +1287,13 @@ packages:
}
engines: { node: ">=14" }
+ "@pkgr/core@0.2.9":
+ resolution:
+ {
+ integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==,
+ }
+ engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 }
+
"@radix-ui/primitive@1.1.3":
resolution:
{
@@ -1198,6 +1578,25 @@ packages:
integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==,
}
+ "@redocly/ajv@8.11.3":
+ resolution:
+ {
+ integrity: sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==,
+ }
+
+ "@redocly/config@0.22.2":
+ resolution:
+ {
+ integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==,
+ }
+
+ "@redocly/openapi-core@1.34.5":
+ resolution:
+ {
+ integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==,
+ }
+ engines: { node: ">=18.17.0", npm: ">=9.5.0" }
+
"@reduxjs/toolkit@2.8.2":
resolution:
{
@@ -1382,6 +1781,24 @@ packages:
integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==,
}
+ "@sinclair/typebox@0.34.41":
+ resolution:
+ {
+ integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==,
+ }
+
+ "@sinonjs/commons@3.0.1":
+ resolution:
+ {
+ integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==,
+ }
+
+ "@sinonjs/fake-timers@13.0.5":
+ resolution:
+ {
+ integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==,
+ }
+
"@socket.io/component-emitter@3.1.2":
resolution:
{
@@ -1418,6 +1835,20 @@ packages:
integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==,
}
+ "@tanstack/query-core@5.85.9":
+ resolution:
+ {
+ integrity: sha512-5fxb9vwyftYE6KFLhhhDyLr8NO75+Wpu7pmTo+TkwKmMX2oxZDoLwcqGP8ItKSpUMwk3urWgQDZfyWr5Jm9LsQ==,
+ }
+
+ "@tanstack/react-query@5.85.9":
+ resolution:
+ {
+ integrity: sha512-2T5zgSpcOZXGkH/UObIbIkGmUPQqZqn7esVQFXLOze622h4spgWf5jmvrqAo9dnI13/hyMcNsF1jsoDcb59nJQ==,
+ }
+ peerDependencies:
+ react: ^18 || ^19
+
"@tootallnate/once@2.0.0":
resolution:
{
@@ -1461,6 +1892,30 @@ packages:
integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==,
}
+ "@types/babel__core@7.20.5":
+ resolution:
+ {
+ integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==,
+ }
+
+ "@types/babel__generator@7.27.0":
+ resolution:
+ {
+ integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==,
+ }
+
+ "@types/babel__template@7.4.4":
+ resolution:
+ {
+ integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==,
+ }
+
+ "@types/babel__traverse@7.28.0":
+ resolution:
+ {
+ integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==,
+ }
+
"@types/debug@4.1.12":
resolution:
{
@@ -1491,6 +1946,13 @@ packages:
integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==,
}
+ "@types/ioredis@5.0.0":
+ resolution:
+ {
+ integrity: sha512-zJbJ3FVE17CNl5KXzdeSPtdltc4tMT3TzC6fxQS0sQngkbFZ6h+0uTafsRqu+eSLIugf6Yb0Ea0SUuRr42Nk9g==,
+ }
+ deprecated: This is a stub types definition. ioredis provides its own type definitions, so you do not need this installed.
+
"@types/istanbul-lib-coverage@2.0.6":
resolution:
{
@@ -1509,6 +1971,12 @@ packages:
integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==,
}
+ "@types/jest@30.0.0":
+ resolution:
+ {
+ integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==,
+ }
+
"@types/json-schema@7.0.15":
resolution:
{
@@ -1575,6 +2043,12 @@ packages:
integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==,
}
+ "@types/stack-utils@2.0.3":
+ resolution:
+ {
+ integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==,
+ }
+
"@types/ua-parser-js@0.7.39":
resolution:
{
@@ -1888,12 +2362,6 @@ packages:
cpu: [x64]
os: [win32]
- "@upstash/redis@1.35.3":
- resolution:
- {
- integrity: sha512-hSjv66NOuahW3MisRGlSgoszU2uONAY2l5Qo3Sae8OT3/Tng9K+2/cBRuyPBX8egwEGcNNCF9+r0V6grNnhL+w==,
- }
-
"@vercel/build-utils@8.4.12":
resolution:
{
@@ -1950,13 +2418,6 @@ packages:
integrity: sha512-IPAVaALuGAzt2apvTtBs5tB+8zZRzn/yG3AGp8dFyCsw/v5YOuk0Q5s8Z3fayLvJbFpjrKtqRNDZzVJBBU3MrQ==,
}
- "@vercel/kv@2.0.0":
- resolution:
- {
- integrity: sha512-zdVrhbzZBYo5d1Hfn4bKtqCeKf0FuzW8rSHauzQVMUgv1+1JOwof2mWcBuI+YMJy8s0G0oqAUfQ7HgUDzb8EbA==,
- }
- engines: { node: ">=14.6" }
-
"@vercel/next@4.3.18":
resolution:
{
@@ -2502,6 +2963,13 @@ packages:
}
engines: { node: ">= 6.0.0" }
+ agent-base@7.1.4:
+ resolution:
+ {
+ integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==,
+ }
+ engines: { node: ">= 14" }
+
ajv@6.12.6:
resolution:
{
@@ -2514,6 +2982,20 @@ packages:
integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==,
}
+ ansi-colors@4.1.3:
+ resolution:
+ {
+ integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==,
+ }
+ engines: { node: ">=6" }
+
+ ansi-escapes@4.3.2:
+ resolution:
+ {
+ integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==,
+ }
+ engines: { node: ">=8" }
+
ansi-regex@5.0.1:
resolution:
{
@@ -2535,6 +3017,13 @@ packages:
}
engines: { node: ">=8" }
+ ansi-styles@5.2.0:
+ resolution:
+ {
+ integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==,
+ }
+ engines: { node: ">=10" }
+
ansi-styles@6.2.1:
resolution:
{
@@ -2587,6 +3076,12 @@ packages:
integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==,
}
+ argparse@1.0.10:
+ resolution:
+ {
+ integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==,
+ }
+
argparse@2.0.1:
resolution:
{
@@ -2758,6 +3253,29 @@ packages:
}
engines: { node: ">= 0.4" }
+ babel-jest@30.1.2:
+ resolution:
+ {
+ integrity: sha512-IQCus1rt9kaSh7PQxLYRY5NmkNrNlU2TpabzwV7T2jljnpdHOcmnYYv8QmE04Li4S3a2Lj8/yXyET5pBarPr6g==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ peerDependencies:
+ "@babel/core": ^7.11.0
+
+ babel-plugin-istanbul@7.0.0:
+ resolution:
+ {
+ integrity: sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==,
+ }
+ engines: { node: ">=12" }
+
+ babel-plugin-jest-hoist@30.0.1:
+ resolution:
+ {
+ integrity: sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
babel-plugin-macros@3.1.0:
resolution:
{
@@ -2765,6 +3283,23 @@ packages:
}
engines: { node: ">=10", npm: ">=6" }
+ babel-preset-current-node-syntax@1.2.0:
+ resolution:
+ {
+ integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==,
+ }
+ peerDependencies:
+ "@babel/core": ^7.0.0 || ^8.0.0-0
+
+ babel-preset-jest@30.0.1:
+ resolution:
+ {
+ integrity: sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ peerDependencies:
+ "@babel/core": ^7.11.0
+
bail@2.0.2:
resolution:
{
@@ -2823,6 +3358,19 @@ packages:
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
hasBin: true
+ bs-logger@0.2.6:
+ resolution:
+ {
+ integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==,
+ }
+ engines: { node: ">= 6" }
+
+ bser@2.1.1:
+ resolution:
+ {
+ integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==,
+ }
+
btoa@1.2.1:
resolution:
{
@@ -2837,6 +3385,12 @@ packages:
integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==,
}
+ buffer-from@1.1.2:
+ resolution:
+ {
+ integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==,
+ }
+
buffer@6.0.3:
resolution:
{
@@ -2857,17 +3411,6 @@ packages:
}
engines: { node: ">= 0.8" }
- c12@1.11.1:
- resolution:
- {
- integrity: sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==,
- }
- peerDependencies:
- magicast: ^0.3.4
- peerDependenciesMeta:
- magicast:
- optional: true
-
call-bind-apply-helpers@1.0.2:
resolution:
{
@@ -2903,12 +3446,19 @@ packages:
}
engines: { node: ">= 6" }
- camelcase@8.0.0:
+ camelcase@5.3.1:
resolution:
{
- integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==,
+ integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==,
}
- engines: { node: ">=16" }
+ engines: { node: ">=6" }
+
+ camelcase@6.3.0:
+ resolution:
+ {
+ integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==,
+ }
+ engines: { node: ">=10" }
caniuse-lite@1.0.30001734:
resolution:
@@ -2936,6 +3486,19 @@ packages:
}
engines: { node: ">=10" }
+ change-case@5.4.4:
+ resolution:
+ {
+ integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==,
+ }
+
+ char-regex@1.0.2:
+ resolution:
+ {
+ integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==,
+ }
+ engines: { node: ">=10" }
+
character-entities-html4@2.1.0:
resolution:
{
@@ -3007,11 +3570,12 @@ packages:
}
engines: { node: ">=8" }
- citty@0.1.6:
+ ci-info@4.3.0:
resolution:
{
- integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==,
+ integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==,
}
+ engines: { node: ">=8" }
cjs-module-lexer@1.2.3:
resolution:
@@ -3019,6 +3583,12 @@ packages:
integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==,
}
+ cjs-module-lexer@2.1.0:
+ resolution:
+ {
+ integrity: sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==,
+ }
+
classnames@2.5.1:
resolution:
{
@@ -3031,6 +3601,13 @@ packages:
integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==,
}
+ cliui@8.0.1:
+ resolution:
+ {
+ integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==,
+ }
+ engines: { node: ">=12" }
+
clsx@2.1.1:
resolution:
{
@@ -3045,12 +3622,25 @@ packages:
}
engines: { node: ">=0.10.0" }
+ co@4.6.0:
+ resolution:
+ {
+ integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==,
+ }
+ engines: { iojs: ">= 1.0.0", node: ">= 0.12.0" }
+
code-block-writer@10.1.1:
resolution:
{
integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==,
}
+ collect-v8-coverage@1.0.2:
+ resolution:
+ {
+ integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==,
+ }
+
color-convert@2.0.1:
resolution:
{
@@ -3071,6 +3661,12 @@ packages:
}
hasBin: true
+ colorette@1.4.0:
+ resolution:
+ {
+ integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==,
+ }
+
combined-stream@1.0.8:
resolution:
{
@@ -3084,13 +3680,6 @@ packages:
integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==,
}
- commander@12.1.0:
- resolution:
- {
- integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==,
- }
- engines: { node: ">=18" }
-
commander@4.1.1:
resolution:
{
@@ -3110,19 +3699,6 @@ packages:
integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==,
}
- confbox@0.1.8:
- resolution:
- {
- integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==,
- }
-
- consola@3.4.2:
- resolution:
- {
- integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==,
- }
- engines: { node: ^14.18.0 || >=16.10.0 }
-
console-control-strings@1.1.0:
resolution:
{
@@ -3149,6 +3725,12 @@ packages:
integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==,
}
+ convert-source-map@2.0.0:
+ resolution:
+ {
+ integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==,
+ }
+
cookie@0.7.2:
resolution:
{
@@ -3270,12 +3852,30 @@ packages:
integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==,
}
+ dedent@1.7.0:
+ resolution:
+ {
+ integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==,
+ }
+ peerDependencies:
+ babel-plugin-macros: ^3.1.0
+ peerDependenciesMeta:
+ babel-plugin-macros:
+ optional: true
+
deep-is@0.1.4:
resolution:
{
integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==,
}
+ deepmerge@4.3.1:
+ resolution:
+ {
+ integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==,
+ }
+ engines: { node: ">=0.10.0" }
+
define-data-property@1.1.4:
resolution:
{
@@ -3290,12 +3890,6 @@ packages:
}
engines: { node: ">= 0.4" }
- defu@6.1.4:
- resolution:
- {
- integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==,
- }
-
delayed-stream@1.0.0:
resolution:
{
@@ -3330,12 +3924,6 @@ packages:
}
engines: { node: ">=6" }
- destr@2.0.5:
- resolution:
- {
- integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==,
- }
-
detect-europe-js@0.1.2:
resolution:
{
@@ -3357,6 +3945,13 @@ packages:
}
engines: { node: ">=8" }
+ detect-newline@3.1.0:
+ resolution:
+ {
+ integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==,
+ }
+ engines: { node: ">=8" }
+
detect-node-es@1.1.0:
resolution:
{
@@ -3413,13 +4008,6 @@ packages:
integrity: sha512-h7g5eduvnLwowJJPkcB5lNzo8vd/Hx4e3I4IOtLpX0qB2wBiuryGLNa61MeFre4b6gMaQIhegMIZ2I8rQCAJwQ==,
}
- dotenv@16.6.1:
- resolution:
- {
- integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==,
- }
- engines: { node: ">=12" }
-
dunder-proto@1.0.1:
resolution:
{
@@ -3447,6 +4035,13 @@ packages:
integrity: sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==,
}
+ emittery@0.13.1:
+ resolution:
+ {
+ integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==,
+ }
+ engines: { node: ">=12" }
+
emoji-regex@8.0.0:
resolution:
{
@@ -3753,6 +4348,13 @@ packages:
}
engines: { node: ">=6" }
+ escape-string-regexp@2.0.0:
+ resolution:
+ {
+ integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==,
+ }
+ engines: { node: ">=8" }
+
escape-string-regexp@4.0.0:
resolution:
{
@@ -3899,6 +4501,14 @@ packages:
}
engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ esprima@4.0.1:
+ resolution:
+ {
+ integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==,
+ }
+ engines: { node: ">=4" }
+ hasBin: true
+
esquery@1.6.0:
resolution:
{
@@ -3973,6 +4583,27 @@ packages:
}
engines: { node: ^8.12.0 || >=9.7.0 }
+ execa@5.1.1:
+ resolution:
+ {
+ integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==,
+ }
+ engines: { node: ">=10" }
+
+ exit-x@0.2.2:
+ resolution:
+ {
+ integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==,
+ }
+ engines: { node: ">= 0.8.0" }
+
+ expect@30.1.2:
+ resolution:
+ {
+ integrity: sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
extend@3.0.2:
resolution:
{
@@ -4022,6 +4653,12 @@ packages:
integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==,
}
+ fb-watchman@2.0.2:
+ resolution:
+ {
+ integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==,
+ }
+
fd-slicer@1.1.0:
resolution:
{
@@ -4065,6 +4702,13 @@ packages:
integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==,
}
+ find-up@4.1.0:
+ resolution:
+ {
+ integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==,
+ }
+ engines: { node: ">=8" }
+
find-up@5.0.0:
resolution:
{
@@ -4213,12 +4857,26 @@ packages:
}
engines: { node: ">= 4" }
+ gensync@1.0.0-beta.2:
+ resolution:
+ {
+ integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==,
+ }
+ engines: { node: ">=6.9.0" }
+
get-browser-rtc@1.1.0:
resolution:
{
integrity: sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==,
}
+ get-caller-file@2.0.5:
+ resolution:
+ {
+ integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==,
+ }
+ engines: { node: 6.* || 8.* || >= 10.* }
+
get-intrinsic@1.3.0:
resolution:
{
@@ -4233,6 +4891,13 @@ packages:
}
engines: { node: ">=6" }
+ get-package-type@0.1.0:
+ resolution:
+ {
+ integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==,
+ }
+ engines: { node: ">=8.0.0" }
+
get-proto@1.0.1:
resolution:
{
@@ -4247,6 +4912,13 @@ packages:
}
engines: { node: ">=8" }
+ get-stream@6.0.1:
+ resolution:
+ {
+ integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==,
+ }
+ engines: { node: ">=10" }
+
get-symbol-description@1.1.0:
resolution:
{
@@ -4260,13 +4932,6 @@ packages:
integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==,
}
- giget@1.2.5:
- resolution:
- {
- integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==,
- }
- hasBin: true
-
glob-parent@5.1.2:
resolution:
{
@@ -4437,6 +5102,12 @@ packages:
integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==,
}
+ html-escaper@2.0.2:
+ resolution:
+ {
+ integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==,
+ }
+
html-url-attributes@3.0.1:
resolution:
{
@@ -4464,6 +5135,13 @@ packages:
}
engines: { node: ">= 6" }
+ https-proxy-agent@7.0.6:
+ resolution:
+ {
+ integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==,
+ }
+ engines: { node: ">= 14" }
+
human-signals@1.1.1:
resolution:
{
@@ -4471,6 +5149,13 @@ packages:
}
engines: { node: ">=8.12.0" }
+ human-signals@2.1.0:
+ resolution:
+ {
+ integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==,
+ }
+ engines: { node: ">=10.17.0" }
+
hyperhtml-style@0.1.3:
resolution:
{
@@ -4529,6 +5214,14 @@ packages:
}
engines: { node: ">=6" }
+ import-local@3.2.0:
+ resolution:
+ {
+ integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==,
+ }
+ engines: { node: ">=8" }
+ hasBin: true
+
imurmurhash@0.1.4:
resolution:
{
@@ -4536,6 +5229,13 @@ packages:
}
engines: { node: ">=0.8.19" }
+ index-to-position@1.1.0:
+ resolution:
+ {
+ integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==,
+ }
+ engines: { node: ">=18" }
+
inflight@1.0.6:
resolution:
{
@@ -4709,6 +5409,13 @@ packages:
}
engines: { node: ">=8" }
+ is-generator-fn@2.1.0:
+ resolution:
+ {
+ integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==,
+ }
+ engines: { node: ">=6" }
+
is-generator-function@1.1.0:
resolution:
{
@@ -4864,6 +5571,41 @@ packages:
integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==,
}
+ istanbul-lib-coverage@3.2.2:
+ resolution:
+ {
+ integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==,
+ }
+ engines: { node: ">=8" }
+
+ istanbul-lib-instrument@6.0.3:
+ resolution:
+ {
+ integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==,
+ }
+ engines: { node: ">=10" }
+
+ istanbul-lib-report@3.0.1:
+ resolution:
+ {
+ integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==,
+ }
+ engines: { node: ">=10" }
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution:
+ {
+ integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==,
+ }
+ engines: { node: ">=10" }
+
+ istanbul-reports@3.2.0:
+ resolution:
+ {
+ integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==,
+ }
+ engines: { node: ">=8" }
+
iterator.prototype@1.1.5:
resolution:
{
@@ -4884,6 +5626,168 @@ packages:
integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==,
}
+ jest-changed-files@30.0.5:
+ resolution:
+ {
+ integrity: sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-circus@30.1.3:
+ resolution:
+ {
+ integrity: sha512-Yf3dnhRON2GJT4RYzM89t/EXIWNxKTpWTL9BfF3+geFetWP4XSvJjiU1vrWplOiUkmq8cHLiwuhz+XuUp9DscA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-cli@30.1.3:
+ resolution:
+ {
+ integrity: sha512-G8E2Ol3OKch1DEeIBl41NP7OiC6LBhfg25Btv+idcusmoUSpqUkbrneMqbW9lVpI/rCKb/uETidb7DNteheuAQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ jest-config@30.1.3:
+ resolution:
+ {
+ integrity: sha512-M/f7gqdQEPgZNA181Myz+GXCe8jXcJsGjCMXUzRj22FIXsZOyHNte84e0exntOvdPaeh9tA0w+B8qlP2fAezfw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ peerDependencies:
+ "@types/node": "*"
+ esbuild-register: ">=3.4.0"
+ ts-node: ">=9.0.0"
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ esbuild-register:
+ optional: true
+ ts-node:
+ optional: true
+
+ jest-diff@30.1.2:
+ resolution:
+ {
+ integrity: sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-docblock@30.0.1:
+ resolution:
+ {
+ integrity: sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-each@30.1.0:
+ resolution:
+ {
+ integrity: sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-environment-node@30.1.2:
+ resolution:
+ {
+ integrity: sha512-w8qBiXtqGWJ9xpJIA98M0EIoq079GOQRQUyse5qg1plShUCQ0Ek1VTTcczqKrn3f24TFAgFtT+4q3aOXvjbsuA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-haste-map@30.1.0:
+ resolution:
+ {
+ integrity: sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-leak-detector@30.1.0:
+ resolution:
+ {
+ integrity: sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-matcher-utils@30.1.2:
+ resolution:
+ {
+ integrity: sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-message-util@30.1.0:
+ resolution:
+ {
+ integrity: sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-mock@30.0.5:
+ resolution:
+ {
+ integrity: sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-pnp-resolver@1.2.3:
+ resolution:
+ {
+ integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==,
+ }
+ engines: { node: ">=6" }
+ peerDependencies:
+ jest-resolve: "*"
+ peerDependenciesMeta:
+ jest-resolve:
+ optional: true
+
+ jest-regex-util@30.0.1:
+ resolution:
+ {
+ integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-resolve-dependencies@30.1.3:
+ resolution:
+ {
+ integrity: sha512-DNfq3WGmuRyHRHfEet+Zm3QOmVFtIarUOQHHryKPc0YL9ROfgWZxl4+aZq/VAzok2SS3gZdniP+dO4zgo59hBg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-resolve@30.1.3:
+ resolution:
+ {
+ integrity: sha512-DI4PtTqzw9GwELFS41sdMK32Ajp3XZQ8iygeDMWkxlRhm7uUTOFSZFVZABFuxr0jvspn8MAYy54NxZCsuCTSOw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-runner@30.1.3:
+ resolution:
+ {
+ integrity: sha512-dd1ORcxQraW44Uz029TtXj85W11yvLpDuIzNOlofrC8GN+SgDlgY4BvyxJiVeuabA1t6idjNbX59jLd2oplOGQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-runtime@30.1.3:
+ resolution:
+ {
+ integrity: sha512-WS8xgjuNSphdIGnleQcJ3AKE4tBKOVP+tKhCD0u+Tb2sBmsU8DxfbBpZX7//+XOz81zVs4eFpJQwBNji2Y07DA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-snapshot@30.1.2:
+ resolution:
+ {
+ integrity: sha512-4q4+6+1c8B6Cy5pGgFvjDy/Pa6VYRiGu0yQafKkJ9u6wQx4G5PqI2QR6nxTl43yy7IWsINwz6oT4o6tD12a8Dg==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
jest-util@29.7.0:
resolution:
{
@@ -4891,6 +5795,27 @@ packages:
}
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
+ jest-util@30.0.5:
+ resolution:
+ {
+ integrity: sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-validate@30.1.0:
+ resolution:
+ {
+ integrity: sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest-watcher@30.1.3:
+ resolution:
+ {
+ integrity: sha512-6jQUZCP1BTL2gvG9E4YF06Ytq4yMb4If6YoQGRR6PpjtqOXSP3sKe2kqwB6SQ+H9DezOfZaSLnmka1NtGm3fCQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
jest-worker@29.7.0:
resolution:
{
@@ -4898,6 +5823,26 @@ packages:
}
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
+ jest-worker@30.1.0:
+ resolution:
+ {
+ integrity: sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
+ jest@30.1.3:
+ resolution:
+ {
+ integrity: sha512-Ry+p2+NLk6u8Agh5yVqELfUJvRfV51hhVBRIB5yZPY7mU0DGBmOuFG5GebZbMbm86cdQNK0fhJuDX8/1YorISQ==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
jiti@1.21.7:
resolution:
{
@@ -4911,12 +5856,26 @@ packages:
integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==,
}
+ js-levenshtein@1.1.6:
+ resolution:
+ {
+ integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==,
+ }
+ engines: { node: ">=0.10.0" }
+
js-tokens@4.0.0:
resolution:
{
integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==,
}
+ js-yaml@3.14.1:
+ resolution:
+ {
+ integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==,
+ }
+ hasBin: true
+
js-yaml@4.1.0:
resolution:
{
@@ -4981,6 +5940,14 @@ packages:
}
hasBin: true
+ json5@2.2.3:
+ resolution:
+ {
+ integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==,
+ }
+ engines: { node: ">=6" }
+ hasBin: true
+
jsonfile@4.0.0:
resolution:
{
@@ -5019,6 +5986,13 @@ packages:
}
engines: { node: ">=0.10" }
+ leven@3.1.0:
+ resolution:
+ {
+ integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==,
+ }
+ engines: { node: ">=6" }
+
levn@0.4.1:
resolution:
{
@@ -5057,6 +6031,13 @@ packages:
integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==,
}
+ locate-path@5.0.0:
+ resolution:
+ {
+ integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==,
+ }
+ engines: { node: ">=8" }
+
locate-path@6.0.0:
resolution:
{
@@ -5076,6 +6057,12 @@ packages:
integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==,
}
+ lodash.memoize@4.1.2:
+ resolution:
+ {
+ integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==,
+ }
+
lodash.merge@4.6.2:
resolution:
{
@@ -5101,6 +6088,12 @@ packages:
integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==,
}
+ lru-cache@5.1.1:
+ resolution:
+ {
+ integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==,
+ }
+
lru-cache@6.0.0:
resolution:
{
@@ -5130,12 +6123,25 @@ packages:
}
engines: { node: ">=8" }
+ make-dir@4.0.0:
+ resolution:
+ {
+ integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==,
+ }
+ engines: { node: ">=10" }
+
make-error@1.3.6:
resolution:
{
integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==,
}
+ makeerror@1.0.12:
+ resolution:
+ {
+ integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==,
+ }
+
math-intrinsics@1.1.0:
resolution:
{
@@ -5460,12 +6466,6 @@ packages:
engines: { node: ">=10" }
hasBin: true
- mlly@1.7.4:
- resolution:
- {
- integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==,
- }
-
mri@1.2.0:
resolution:
{
@@ -5566,24 +6566,12 @@ packages:
sass:
optional: true
- node-abort-controller@3.1.1:
- resolution:
- {
- integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==,
- }
-
node-addon-api@7.1.1:
resolution:
{
integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==,
}
- node-fetch-native@1.6.7:
- resolution:
- {
- integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==,
- }
-
node-fetch@2.6.7:
resolution:
{
@@ -5627,6 +6615,12 @@ packages:
}
hasBin: true
+ node-int64@0.4.0:
+ resolution:
+ {
+ integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==,
+ }
+
node-releases@2.0.19:
resolution:
{
@@ -5690,14 +6684,6 @@ packages:
react-router-dom:
optional: true
- nypm@0.5.4:
- resolution:
- {
- integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==,
- }
- engines: { node: ^14.16.0 || >=16.10.0 }
- hasBin: true
-
oauth@0.9.15:
resolution:
{
@@ -5774,12 +6760,6 @@ packages:
}
engines: { node: ">= 0.4" }
- ohash@1.1.6:
- resolution:
- {
- integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==,
- }
-
oidc-token-hash@5.1.1:
resolution:
{
@@ -5806,6 +6786,36 @@ packages:
}
engines: { node: ">=6" }
+ openapi-fetch@0.14.0:
+ resolution:
+ {
+ integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==,
+ }
+
+ openapi-react-query@0.5.0:
+ resolution:
+ {
+ integrity: sha512-VtyqiamsbWsdSWtXmj/fAR+m9nNxztsof6h8ZIsjRj8c8UR/x9AIwHwd60IqwgymmFwo7qfSJQ1ZzMJrtqjQVg==,
+ }
+ peerDependencies:
+ "@tanstack/react-query": ^5.25.0
+ openapi-fetch: ^0.14.0
+
+ openapi-typescript-helpers@0.0.15:
+ resolution:
+ {
+ integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==,
+ }
+
+ openapi-typescript@7.9.1:
+ resolution:
+ {
+ integrity: sha512-9gJtoY04mk6iPMbToPjPxEAtfXZ0dTsMZtsgUI8YZta0btPPig9DJFP4jlerQD/7QOwYgb0tl+zLUpDf7vb7VA==,
+ }
+ hasBin: true
+ peerDependencies:
+ typescript: ^5.x
+
openid-client@5.7.1:
resolution:
{
@@ -5840,6 +6850,13 @@ packages:
}
engines: { node: ">=8" }
+ p-limit@2.3.0:
+ resolution:
+ {
+ integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==,
+ }
+ engines: { node: ">=6" }
+
p-limit@3.1.0:
resolution:
{
@@ -5847,6 +6864,13 @@ packages:
}
engines: { node: ">=10" }
+ p-locate@4.1.0:
+ resolution:
+ {
+ integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==,
+ }
+ engines: { node: ">=8" }
+
p-locate@5.0.0:
resolution:
{
@@ -5854,6 +6878,13 @@ packages:
}
engines: { node: ">=10" }
+ p-try@2.2.0:
+ resolution:
+ {
+ integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==,
+ }
+ engines: { node: ">=6" }
+
package-json-from-dist@1.0.1:
resolution:
{
@@ -5880,6 +6911,13 @@ packages:
}
engines: { node: ">=8" }
+ parse-json@8.3.0:
+ resolution:
+ {
+ integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==,
+ }
+ engines: { node: ">=18" }
+
parse-ms@2.1.0:
resolution:
{
@@ -5959,30 +6997,12 @@ packages:
}
engines: { node: ">=8" }
- pathe@1.1.2:
- resolution:
- {
- integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==,
- }
-
- pathe@2.0.3:
- resolution:
- {
- integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==,
- }
-
pend@1.2.0:
resolution:
{
integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==,
}
- perfect-debounce@1.0.0:
- resolution:
- {
- integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==,
- }
-
perfect-freehand@1.2.2:
resolution:
{
@@ -6029,11 +7049,19 @@ packages:
}
engines: { node: ">= 6" }
- pkg-types@1.3.1:
+ pkg-dir@4.2.0:
resolution:
{
- integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==,
+ integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==,
}
+ engines: { node: ">=8" }
+
+ pluralize@8.0.0:
+ resolution:
+ {
+ integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==,
+ }
+ engines: { node: ">=4" }
possible-typed-array-names@1.1.0:
resolution:
@@ -6146,6 +7174,13 @@ packages:
integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==,
}
+ pretty-format@30.0.5:
+ resolution:
+ {
+ integrity: sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==,
+ }
+ engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+
pretty-ms@7.0.1:
resolution:
{
@@ -6209,6 +7244,12 @@ packages:
}
engines: { node: ">=6" }
+ pure-rand@7.0.1:
+ resolution:
+ {
+ integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==,
+ }
+
qr.js@0.0.0:
resolution:
{
@@ -6234,12 +7275,6 @@ packages:
}
engines: { node: ">= 0.8" }
- rc9@2.1.2:
- resolution:
- {
- integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==,
- }
-
react-dom@18.3.1:
resolution:
{
@@ -6271,6 +7306,12 @@ packages:
integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==,
}
+ react-is@18.3.1:
+ resolution:
+ {
+ integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==,
+ }
+
react-markdown@9.1.0:
resolution:
{
@@ -6392,13 +7433,6 @@ packages:
}
engines: { node: ">=4" }
- redlock@5.0.0-beta.2:
- resolution:
- {
- integrity: sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==,
- }
- engines: { node: ">=12" }
-
redux-thunk@3.1.0:
resolution:
{
@@ -6439,6 +7473,13 @@ packages:
integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==,
}
+ require-directory@2.1.1:
+ resolution:
+ {
+ integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==,
+ }
+ engines: { node: ">=0.10.0" }
+
require-from-string@2.0.2:
resolution:
{
@@ -6458,6 +7499,13 @@ packages:
integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==,
}
+ resolve-cwd@3.0.0:
+ resolution:
+ {
+ integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==,
+ }
+ engines: { node: ">=8" }
+
resolve-from@4.0.0:
resolution:
{
@@ -6727,6 +7775,13 @@ packages:
integrity: sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==,
}
+ slash@3.0.0:
+ resolution:
+ {
+ integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==,
+ }
+ engines: { node: ">=8" }
+
socket.io-client@4.7.2:
resolution:
{
@@ -6748,6 +7803,12 @@ packages:
}
engines: { node: ">=0.10.0" }
+ source-map-support@0.5.13:
+ resolution:
+ {
+ integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==,
+ }
+
source-map@0.5.7:
resolution:
{
@@ -6768,6 +7829,12 @@ packages:
integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==,
}
+ sprintf-js@1.0.3:
+ resolution:
+ {
+ integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==,
+ }
+
sprintf-js@1.1.3:
resolution:
{
@@ -6780,6 +7847,13 @@ packages:
integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==,
}
+ stack-utils@2.0.6:
+ resolution:
+ {
+ integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==,
+ }
+ engines: { node: ">=10" }
+
stacktrace-parser@0.1.11:
resolution:
{
@@ -6832,6 +7906,13 @@ packages:
}
engines: { node: ">=10.0.0" }
+ string-length@4.0.2:
+ resolution:
+ {
+ integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==,
+ }
+ engines: { node: ">=10" }
+
string-width@4.2.3:
resolution:
{
@@ -6920,6 +8001,13 @@ packages:
}
engines: { node: ">=4" }
+ strip-bom@4.0.0:
+ resolution:
+ {
+ integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==,
+ }
+ engines: { node: ">=8" }
+
strip-final-newline@2.0.0:
resolution:
{
@@ -6976,6 +8064,13 @@ packages:
engines: { node: ">=16 || 14 >=14.17" }
hasBin: true
+ supports-color@10.2.0:
+ resolution:
+ {
+ integrity: sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==,
+ }
+ engines: { node: ">=18" }
+
supports-color@7.2.0:
resolution:
{
@@ -7004,6 +8099,13 @@ packages:
}
engines: { node: ">= 0.4" }
+ synckit@0.11.11:
+ resolution:
+ {
+ integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==,
+ }
+ engines: { node: ^14.18.0 || >=16.0.0 }
+
tailwindcss@3.4.17:
resolution:
{
@@ -7026,6 +8128,13 @@ packages:
}
engines: { node: ">=10" }
+ test-exclude@6.0.0:
+ resolution:
+ {
+ integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==,
+ }
+ engines: { node: ">=8" }
+
thenify-all@1.6.0:
resolution:
{
@@ -7046,12 +8155,6 @@ packages:
}
engines: { node: ">=10" }
- tinyexec@0.3.2:
- resolution:
- {
- integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==,
- }
-
tinyglobby@0.2.14:
resolution:
{
@@ -7059,6 +8162,12 @@ packages:
}
engines: { node: ">=12.0.0" }
+ tmpl@1.0.5:
+ resolution:
+ {
+ integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==,
+ }
+
to-regex-range@5.0.1:
resolution:
{
@@ -7113,6 +8222,36 @@ packages:
integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==,
}
+ ts-jest@29.4.1:
+ resolution:
+ {
+ integrity: sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==,
+ }
+ engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 }
+ hasBin: true
+ peerDependencies:
+ "@babel/core": ">=7.0.0-beta.0 <8"
+ "@jest/transform": ^29.0.0 || ^30.0.0
+ "@jest/types": ^29.0.0 || ^30.0.0
+ babel-jest: ^29.0.0 || ^30.0.0
+ esbuild: "*"
+ jest: ^29.0.0 || ^30.0.0
+ jest-util: ^29.0.0 || ^30.0.0
+ typescript: ">=4.3 <6"
+ peerDependenciesMeta:
+ "@babel/core":
+ optional: true
+ "@jest/transform":
+ optional: true
+ "@jest/types":
+ optional: true
+ babel-jest:
+ optional: true
+ esbuild:
+ optional: true
+ jest-util:
+ optional: true
+
ts-morph@12.0.0:
resolution:
{
@@ -7161,6 +8300,20 @@ packages:
}
engines: { node: ">= 0.8.0" }
+ type-detect@4.0.8:
+ resolution:
+ {
+ integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==,
+ }
+ engines: { node: ">=4" }
+
+ type-fest@0.21.3:
+ resolution:
+ {
+ integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==,
+ }
+ engines: { node: ">=10" }
+
type-fest@0.7.1:
resolution:
{
@@ -7168,6 +8321,13 @@ packages:
}
engines: { node: ">=8" }
+ type-fest@4.41.0:
+ resolution:
+ {
+ integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==,
+ }
+ engines: { node: ">=16" }
+
typed-array-buffer@1.0.3:
resolution:
{
@@ -7244,12 +8404,6 @@ packages:
integrity: sha512-v+Z8Jal+GtmKGtJ34GIQlCJAxrDt9kbjpNsNvYoAXFyr4gNfWlD4uJJuoNNu/0UTVaKvQwHaSU095YDl71lKPw==,
}
- ufo@1.6.1:
- resolution:
- {
- integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==,
- }
-
uglify-js@3.19.3:
resolution:
{
@@ -7289,12 +8443,6 @@ packages:
}
engines: { node: ">= 0.4" }
- uncrypto@0.1.3:
- resolution:
- {
- integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==,
- }
-
undici-types@7.10.0:
resolution:
{
@@ -7386,6 +8534,12 @@ packages:
integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==,
}
+ uri-js-replace@1.0.1:
+ resolution:
+ {
+ integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==,
+ }
+
uri-js@4.4.1:
resolution:
{
@@ -7464,6 +8618,13 @@ packages:
integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==,
}
+ v8-to-istanbul@9.3.0:
+ resolution:
+ {
+ integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==,
+ }
+ engines: { node: ">=10.12.0" }
+
vercel@37.14.0:
resolution:
{
@@ -7484,6 +8645,12 @@ packages:
integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==,
}
+ walker@1.0.8:
+ resolution:
+ {
+ integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==,
+ }
+
wavesurfer.js@7.10.1:
resolution:
{
@@ -7597,6 +8764,13 @@ packages:
integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==,
}
+ write-file-atomic@5.0.1:
+ resolution:
+ {
+ integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==,
+ }
+ engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 }
+
ws@8.17.1:
resolution:
{
@@ -7633,6 +8807,13 @@ packages:
}
engines: { node: ">=0.4.0" }
+ y18n@5.0.8:
+ resolution:
+ {
+ integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==,
+ }
+ engines: { node: ">=10" }
+
yallist@3.1.1:
resolution:
{
@@ -7645,6 +8826,12 @@ packages:
integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==,
}
+ yaml-ast-parser@0.0.43:
+ resolution:
+ {
+ integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==,
+ }
+
yaml@1.10.2:
resolution:
{
@@ -7660,6 +8847,20 @@ packages:
engines: { node: ">= 14.6" }
hasBin: true
+ yargs-parser@21.1.1:
+ resolution:
+ {
+ integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==,
+ }
+ engines: { node: ">=12" }
+
+ yargs@17.7.2:
+ resolution:
+ {
+ integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==,
+ }
+ engines: { node: ">=12" }
+
yauzl-clone@1.0.4:
resolution:
{
@@ -7694,6 +8895,12 @@ packages:
}
engines: { node: ">=10" }
+ zod@4.1.5:
+ resolution:
+ {
+ integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==,
+ }
+
zwitch@2.0.4:
resolution:
{
@@ -7703,11 +8910,10 @@ packages:
snapshots:
"@alloc/quick-lru@5.2.0": {}
- "@apidevtools/json-schema-ref-parser@11.6.4":
+ "@ampproject/remapping@2.3.0":
dependencies:
- "@jsdevtools/ono": 7.1.3
- "@types/json-schema": 7.0.15
- js-yaml: 4.1.0
+ "@jridgewell/gen-mapping": 0.3.13
+ "@jridgewell/trace-mapping": 0.3.30
"@ark-ui/react@5.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
@@ -7779,6 +8985,28 @@ snapshots:
js-tokens: 4.0.0
picocolors: 1.1.1
+ "@babel/compat-data@7.28.0": {}
+
+ "@babel/core@7.28.3":
+ dependencies:
+ "@ampproject/remapping": 2.3.0
+ "@babel/code-frame": 7.27.1
+ "@babel/generator": 7.28.3
+ "@babel/helper-compilation-targets": 7.27.2
+ "@babel/helper-module-transforms": 7.28.3(@babel/core@7.28.3)
+ "@babel/helpers": 7.28.3
+ "@babel/parser": 7.28.3
+ "@babel/template": 7.27.2
+ "@babel/traverse": 7.28.3
+ "@babel/types": 7.28.2
+ convert-source-map: 2.0.0
+ debug: 4.4.1(supports-color@9.4.0)
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
"@babel/generator@7.28.0":
dependencies:
"@babel/parser": 7.28.0
@@ -7787,6 +9015,22 @@ snapshots:
"@jridgewell/trace-mapping": 0.3.30
jsesc: 3.1.0
+ "@babel/generator@7.28.3":
+ dependencies:
+ "@babel/parser": 7.28.3
+ "@babel/types": 7.28.2
+ "@jridgewell/gen-mapping": 0.3.13
+ "@jridgewell/trace-mapping": 0.3.30
+ jsesc: 3.1.0
+
+ "@babel/helper-compilation-targets@7.27.2":
+ dependencies:
+ "@babel/compat-data": 7.28.0
+ "@babel/helper-validator-option": 7.27.1
+ browserslist: 4.25.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
"@babel/helper-globals@7.28.0": {}
"@babel/helper-module-imports@7.27.1":
@@ -7796,14 +9040,121 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ "@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-module-imports": 7.27.1
+ "@babel/helper-validator-identifier": 7.27.1
+ "@babel/traverse": 7.28.3
+ transitivePeerDependencies:
+ - supports-color
+
+ "@babel/helper-plugin-utils@7.27.1": {}
+
"@babel/helper-string-parser@7.27.1": {}
"@babel/helper-validator-identifier@7.27.1": {}
+ "@babel/helper-validator-option@7.27.1": {}
+
+ "@babel/helpers@7.28.3":
+ dependencies:
+ "@babel/template": 7.27.2
+ "@babel/types": 7.28.2
+
"@babel/parser@7.28.0":
dependencies:
"@babel/types": 7.28.2
+ "@babel/parser@7.28.3":
+ dependencies:
+ "@babel/types": 7.28.2
+
+ "@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
+ "@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.3)":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/helper-plugin-utils": 7.27.1
+
"@babel/runtime@7.28.2": {}
"@babel/template@7.27.2":
@@ -7824,11 +9175,25 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ "@babel/traverse@7.28.3":
+ dependencies:
+ "@babel/code-frame": 7.27.1
+ "@babel/generator": 7.28.3
+ "@babel/helper-globals": 7.28.0
+ "@babel/parser": 7.28.3
+ "@babel/template": 7.27.2
+ "@babel/types": 7.28.2
+ debug: 4.4.1(supports-color@9.4.0)
+ transitivePeerDependencies:
+ - supports-color
+
"@babel/types@7.28.2":
dependencies:
"@babel/helper-string-parser": 7.27.1
"@babel/helper-validator-identifier": 7.27.1
+ "@bcoe/v8-coverage@0.2.3": {}
+
"@chakra-ui/react@3.24.2(@emotion/react@11.14.0(@types/react@18.2.20)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
"@ark-ui/react": 5.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -8027,17 +9392,6 @@ snapshots:
prop-types: 15.8.1
react: 18.3.1
- "@hey-api/openapi-ts@0.48.3(typescript@5.9.2)":
- dependencies:
- "@apidevtools/json-schema-ref-parser": 11.6.4
- c12: 1.11.1
- camelcase: 8.0.0
- commander: 12.1.0
- handlebars: 4.7.8
- typescript: 5.9.2
- transitivePeerDependencies:
- - magicast
-
"@humanfs/core@0.19.1": {}
"@humanfs/node@0.16.6":
@@ -8059,7 +9413,7 @@ snapshots:
dependencies:
"@swc/helpers": 0.5.17
- "@ioredis/commands@1.3.0": {}
+ "@ioredis/commands@1.3.1": {}
"@isaacs/cliui@8.0.2":
dependencies:
@@ -8070,10 +9424,189 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
+ "@istanbuljs/load-nyc-config@1.1.0":
+ dependencies:
+ camelcase: 5.3.1
+ find-up: 4.1.0
+ get-package-type: 0.1.0
+ js-yaml: 3.14.1
+ resolve-from: 5.0.0
+
+ "@istanbuljs/schema@0.1.3": {}
+
+ "@jest/console@30.1.2":
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ jest-message-util: 30.1.0
+ jest-util: 30.0.5
+ slash: 3.0.0
+
+ "@jest/core@30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))":
+ dependencies:
+ "@jest/console": 30.1.2
+ "@jest/pattern": 30.0.1
+ "@jest/reporters": 30.1.3
+ "@jest/test-result": 30.1.3
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ ci-info: 4.3.0
+ exit-x: 0.2.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 30.0.5
+ jest-config: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest-haste-map: 30.1.0
+ jest-message-util: 30.1.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.1.3
+ jest-resolve-dependencies: 30.1.3
+ jest-runner: 30.1.3
+ jest-runtime: 30.1.3
+ jest-snapshot: 30.1.2
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+ jest-watcher: 30.1.3
+ micromatch: 4.0.8
+ pretty-format: 30.0.5
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
+ "@jest/diff-sequences@30.0.1": {}
+
+ "@jest/environment@30.1.2":
+ dependencies:
+ "@jest/fake-timers": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ jest-mock: 30.0.5
+
+ "@jest/expect-utils@30.1.2":
+ dependencies:
+ "@jest/get-type": 30.1.0
+
+ "@jest/expect@30.1.2":
+ dependencies:
+ expect: 30.1.2
+ jest-snapshot: 30.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ "@jest/fake-timers@30.1.2":
+ dependencies:
+ "@jest/types": 30.0.5
+ "@sinonjs/fake-timers": 13.0.5
+ "@types/node": 24.2.1
+ jest-message-util: 30.1.0
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+
+ "@jest/get-type@30.1.0": {}
+
+ "@jest/globals@30.1.2":
+ dependencies:
+ "@jest/environment": 30.1.2
+ "@jest/expect": 30.1.2
+ "@jest/types": 30.0.5
+ jest-mock: 30.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ "@jest/pattern@30.0.1":
+ dependencies:
+ "@types/node": 24.2.1
+ jest-regex-util: 30.0.1
+
+ "@jest/reporters@30.1.3":
+ dependencies:
+ "@bcoe/v8-coverage": 0.2.3
+ "@jest/console": 30.1.2
+ "@jest/test-result": 30.1.3
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ "@jridgewell/trace-mapping": 0.3.30
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ collect-v8-coverage: 1.0.2
+ exit-x: 0.2.2
+ glob: 10.4.5
+ graceful-fs: 4.2.11
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 6.0.3
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ jest-message-util: 30.1.0
+ jest-util: 30.0.5
+ jest-worker: 30.1.0
+ slash: 3.0.0
+ string-length: 4.0.2
+ v8-to-istanbul: 9.3.0
+ transitivePeerDependencies:
+ - supports-color
+
"@jest/schemas@29.6.3":
dependencies:
"@sinclair/typebox": 0.27.8
+ "@jest/schemas@30.0.5":
+ dependencies:
+ "@sinclair/typebox": 0.34.41
+
+ "@jest/snapshot-utils@30.1.2":
+ dependencies:
+ "@jest/types": 30.0.5
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ natural-compare: 1.4.0
+
+ "@jest/source-map@30.0.1":
+ dependencies:
+ "@jridgewell/trace-mapping": 0.3.30
+ callsites: 3.1.0
+ graceful-fs: 4.2.11
+
+ "@jest/test-result@30.1.3":
+ dependencies:
+ "@jest/console": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/istanbul-lib-coverage": 2.0.6
+ collect-v8-coverage: 1.0.2
+
+ "@jest/test-sequencer@30.1.3":
+ dependencies:
+ "@jest/test-result": 30.1.3
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.1.0
+ slash: 3.0.0
+
+ "@jest/transform@30.1.2":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@jest/types": 30.0.5
+ "@jridgewell/trace-mapping": 0.3.30
+ babel-plugin-istanbul: 7.0.0
+ chalk: 4.1.2
+ convert-source-map: 2.0.0
+ fast-json-stable-stringify: 2.1.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.1.0
+ jest-regex-util: 30.0.1
+ jest-util: 30.0.5
+ micromatch: 4.0.8
+ pirates: 4.0.7
+ slash: 3.0.0
+ write-file-atomic: 5.0.1
+ transitivePeerDependencies:
+ - supports-color
+
"@jest/types@29.6.3":
dependencies:
"@jest/schemas": 29.6.3
@@ -8083,6 +9616,16 @@ snapshots:
"@types/yargs": 17.0.33
chalk: 4.1.2
+ "@jest/types@30.0.5":
+ dependencies:
+ "@jest/pattern": 30.0.1
+ "@jest/schemas": 30.0.5
+ "@types/istanbul-lib-coverage": 2.0.6
+ "@types/istanbul-reports": 3.0.4
+ "@types/node": 24.2.1
+ "@types/yargs": 17.0.33
+ chalk: 4.1.2
+
"@jridgewell/gen-mapping@0.3.13":
dependencies:
"@jridgewell/sourcemap-codec": 1.5.5
@@ -8102,8 +9645,6 @@ snapshots:
"@jridgewell/resolve-uri": 3.1.2
"@jridgewell/sourcemap-codec": 1.5.5
- "@jsdevtools/ono@7.1.3": {}
-
"@mapbox/node-pre-gyp@1.0.11":
dependencies:
detect-libc: 2.0.4
@@ -8241,6 +9782,8 @@ snapshots:
"@pkgjs/parseargs@0.11.0":
optional: true
+ "@pkgr/core@0.2.9": {}
+
"@radix-ui/primitive@1.1.3": {}
"@radix-ui/react-arrow@1.1.7(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
@@ -8420,6 +9963,29 @@ snapshots:
"@radix-ui/rect@1.1.1": {}
+ "@redocly/ajv@8.11.3":
+ dependencies:
+ fast-deep-equal: 3.1.3
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ uri-js-replace: 1.0.1
+
+ "@redocly/config@0.22.2": {}
+
+ "@redocly/openapi-core@1.34.5(supports-color@10.2.0)":
+ dependencies:
+ "@redocly/ajv": 8.11.3
+ "@redocly/config": 0.22.2
+ colorette: 1.4.0
+ https-proxy-agent: 7.0.6(supports-color@10.2.0)
+ js-levenshtein: 1.1.6
+ js-yaml: 4.1.0
+ minimatch: 5.1.6
+ pluralize: 8.0.0
+ yaml-ast-parser: 0.0.43
+ transitivePeerDependencies:
+ - supports-color
+
"@reduxjs/toolkit@2.8.2(react@18.3.1)":
dependencies:
"@standard-schema/spec": 1.0.0
@@ -8513,7 +10079,7 @@ snapshots:
"@sentry/utils": 7.120.4
localforage: 1.10.0
- "@sentry/nextjs@7.120.4(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)":
+ "@sentry/nextjs@7.120.4(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)":
dependencies:
"@rollup/plugin-commonjs": 24.0.0(rollup@2.79.2)
"@sentry/core": 7.120.4
@@ -8525,7 +10091,7 @@ snapshots:
"@sentry/vercel-edge": 7.120.4
"@sentry/webpack-plugin": 1.21.0
chalk: 3.0.0
- next: 14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
react: 18.3.1
resolve: 1.22.8
rollup: 2.79.2
@@ -8584,6 +10150,16 @@ snapshots:
"@sinclair/typebox@0.27.8": {}
+ "@sinclair/typebox@0.34.41": {}
+
+ "@sinonjs/commons@3.0.1":
+ dependencies:
+ type-detect: 4.0.8
+
+ "@sinonjs/fake-timers@13.0.5":
+ dependencies:
+ "@sinonjs/commons": 3.0.1
+
"@socket.io/component-emitter@3.1.2": {}
"@standard-schema/spec@1.0.0": {}
@@ -8601,6 +10177,13 @@ snapshots:
"@swc/counter": 0.1.3
tslib: 2.8.1
+ "@tanstack/query-core@5.85.9": {}
+
+ "@tanstack/react-query@5.85.9(react@18.3.1)":
+ dependencies:
+ "@tanstack/query-core": 5.85.9
+ react: 18.3.1
+
"@tootallnate/once@2.0.0": {}
"@ts-morph/common@0.11.1":
@@ -8623,6 +10206,27 @@ snapshots:
tslib: 2.8.1
optional: true
+ "@types/babel__core@7.20.5":
+ dependencies:
+ "@babel/parser": 7.28.0
+ "@babel/types": 7.28.2
+ "@types/babel__generator": 7.27.0
+ "@types/babel__template": 7.4.4
+ "@types/babel__traverse": 7.28.0
+
+ "@types/babel__generator@7.27.0":
+ dependencies:
+ "@babel/types": 7.28.2
+
+ "@types/babel__template@7.4.4":
+ dependencies:
+ "@babel/parser": 7.28.0
+ "@babel/types": 7.28.2
+
+ "@types/babel__traverse@7.28.0":
+ dependencies:
+ "@babel/types": 7.28.2
+
"@types/debug@4.1.12":
dependencies:
"@types/ms": 2.1.0
@@ -8639,6 +10243,12 @@ snapshots:
dependencies:
"@types/unist": 3.0.3
+ "@types/ioredis@5.0.0":
+ dependencies:
+ ioredis: 5.7.0
+ transitivePeerDependencies:
+ - supports-color
+
"@types/istanbul-lib-coverage@2.0.6": {}
"@types/istanbul-lib-report@3.0.3":
@@ -8649,6 +10259,11 @@ snapshots:
dependencies:
"@types/istanbul-lib-report": 3.0.3
+ "@types/jest@30.0.0":
+ dependencies:
+ expect: 30.1.2
+ pretty-format: 30.0.5
+
"@types/json-schema@7.0.15": {}
"@types/json5@0.0.29": {}
@@ -8682,6 +10297,8 @@ snapshots:
"@types/scheduler@0.26.0": {}
+ "@types/stack-utils@2.0.3": {}
+
"@types/ua-parser-js@0.7.39": {}
"@types/unist@2.0.11": {}
@@ -8860,10 +10477,6 @@ snapshots:
"@unrs/resolver-binding-win32-x64-msvc@1.11.1":
optional: true
- "@upstash/redis@1.35.3":
- dependencies:
- uncrypto: 0.1.3
-
"@vercel/build-utils@8.4.12": {}
"@vercel/edge-config-fs@0.1.0": {}
@@ -8920,10 +10533,6 @@ snapshots:
"@vercel/static-config": 3.0.0
ts-morph: 12.0.0
- "@vercel/kv@2.0.0":
- dependencies:
- "@upstash/redis": 1.35.3
-
"@vercel/next@4.3.18":
dependencies:
"@vercel/nft": 0.27.3
@@ -9601,6 +11210,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.4: {}
+
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -9615,6 +11226,12 @@ snapshots:
require-from-string: 2.0.2
uri-js: 4.4.1
+ ansi-colors@4.1.3: {}
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
ansi-regex@5.0.1: {}
ansi-regex@6.1.0: {}
@@ -9623,6 +11240,8 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ ansi-styles@5.2.0: {}
+
ansi-styles@6.2.1: {}
any-promise@1.3.0: {}
@@ -9645,6 +11264,10 @@ snapshots:
arg@5.0.2: {}
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
argparse@2.0.1: {}
aria-hidden@1.2.6:
@@ -9771,12 +11394,66 @@ snapshots:
axobject-query@4.1.0: {}
+ babel-jest@30.1.2(@babel/core@7.28.3):
+ dependencies:
+ "@babel/core": 7.28.3
+ "@jest/transform": 30.1.2
+ "@types/babel__core": 7.20.5
+ babel-plugin-istanbul: 7.0.0
+ babel-preset-jest: 30.0.1(@babel/core@7.28.3)
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-istanbul@7.0.0:
+ dependencies:
+ "@babel/helper-plugin-utils": 7.27.1
+ "@istanbuljs/load-nyc-config": 1.1.0
+ "@istanbuljs/schema": 0.1.3
+ istanbul-lib-instrument: 6.0.3
+ test-exclude: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-jest-hoist@30.0.1:
+ dependencies:
+ "@babel/template": 7.27.2
+ "@babel/types": 7.28.2
+ "@types/babel__core": 7.20.5
+
babel-plugin-macros@3.1.0:
dependencies:
"@babel/runtime": 7.28.2
cosmiconfig: 7.1.0
resolve: 1.22.10
+ babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.3):
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/plugin-syntax-async-generators": 7.8.4(@babel/core@7.28.3)
+ "@babel/plugin-syntax-bigint": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-class-properties": 7.12.13(@babel/core@7.28.3)
+ "@babel/plugin-syntax-class-static-block": 7.14.5(@babel/core@7.28.3)
+ "@babel/plugin-syntax-import-attributes": 7.27.1(@babel/core@7.28.3)
+ "@babel/plugin-syntax-import-meta": 7.10.4(@babel/core@7.28.3)
+ "@babel/plugin-syntax-json-strings": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-logical-assignment-operators": 7.10.4(@babel/core@7.28.3)
+ "@babel/plugin-syntax-nullish-coalescing-operator": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-numeric-separator": 7.10.4(@babel/core@7.28.3)
+ "@babel/plugin-syntax-object-rest-spread": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-optional-catch-binding": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-optional-chaining": 7.8.3(@babel/core@7.28.3)
+ "@babel/plugin-syntax-private-property-in-object": 7.14.5(@babel/core@7.28.3)
+ "@babel/plugin-syntax-top-level-await": 7.14.5(@babel/core@7.28.3)
+
+ babel-preset-jest@30.0.1(@babel/core@7.28.3):
+ dependencies:
+ "@babel/core": 7.28.3
+ babel-plugin-jest-hoist: 30.0.1
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.3)
+
bail@2.0.2: {}
balanced-match@1.0.2: {}
@@ -9809,10 +11486,20 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.2)
+ bs-logger@0.2.6:
+ dependencies:
+ fast-json-stable-stringify: 2.1.0
+
+ bser@2.1.1:
+ dependencies:
+ node-int64: 0.4.0
+
btoa@1.2.1: {}
buffer-crc32@0.2.13: {}
+ buffer-from@1.1.2: {}
+
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
@@ -9824,21 +11511,6 @@ snapshots:
bytes@3.1.0: {}
- c12@1.11.1:
- dependencies:
- chokidar: 3.6.0
- confbox: 0.1.8
- defu: 6.1.4
- dotenv: 16.6.1
- giget: 1.2.5
- jiti: 1.21.7
- mlly: 1.7.4
- ohash: 1.1.6
- pathe: 1.1.2
- perfect-debounce: 1.0.0
- pkg-types: 1.3.1
- rc9: 2.1.2
-
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -9860,7 +11532,9 @@ snapshots:
camelcase-css@2.0.1: {}
- camelcase@8.0.0: {}
+ camelcase@5.3.1: {}
+
+ camelcase@6.3.0: {}
caniuse-lite@1.0.30001734: {}
@@ -9876,6 +11550,10 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ change-case@5.4.4: {}
+
+ char-regex@1.0.2: {}
+
character-entities-html4@2.1.0: {}
character-entities-legacy@3.0.0: {}
@@ -9922,22 +11600,32 @@ snapshots:
ci-info@3.9.0: {}
- citty@0.1.6:
- dependencies:
- consola: 3.4.2
+ ci-info@4.3.0: {}
cjs-module-lexer@1.2.3: {}
+ cjs-module-lexer@2.1.0: {}
+
classnames@2.5.1: {}
client-only@0.0.1: {}
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
clsx@2.1.1: {}
cluster-key-slot@1.1.2: {}
+ co@4.6.0: {}
+
code-block-writer@10.1.1: {}
+ collect-v8-coverage@1.0.2: {}
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -9946,24 +11634,20 @@ snapshots:
color-support@1.1.3: {}
+ colorette@1.4.0: {}
+
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
comma-separated-tokens@2.0.3: {}
- commander@12.1.0: {}
-
commander@4.1.1: {}
commondir@1.0.1: {}
concat-map@0.0.1: {}
- confbox@0.1.8: {}
-
- consola@3.4.2: {}
-
console-control-strings@1.1.0: {}
content-type@1.0.4: {}
@@ -9972,6 +11656,8 @@ snapshots:
convert-source-map@1.9.0: {}
+ convert-source-map@2.0.0: {}
+
cookie@0.7.2: {}
cosmiconfig@7.1.0:
@@ -10026,6 +11712,12 @@ snapshots:
dependencies:
ms: 2.1.3
+ debug@4.4.1(supports-color@10.2.0):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 10.2.0
+
debug@4.4.1(supports-color@9.4.0):
dependencies:
ms: 2.1.3
@@ -10036,8 +11728,14 @@ snapshots:
dependencies:
character-entities: 2.0.2
+ dedent@1.7.0(babel-plugin-macros@3.1.0):
+ optionalDependencies:
+ babel-plugin-macros: 3.1.0
+
deep-is@0.1.4: {}
+ deepmerge@4.3.1: {}
+
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
@@ -10050,8 +11748,6 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
- defu@6.1.4: {}
-
delayed-stream@1.0.0: {}
delegates@1.0.0: {}
@@ -10062,8 +11758,6 @@ snapshots:
dequal@2.0.3: {}
- destr@2.0.5: {}
-
detect-europe-js@0.1.2: {}
detect-libc@1.0.3:
@@ -10071,6 +11765,8 @@ snapshots:
detect-libc@2.0.4: {}
+ detect-newline@3.1.0: {}
+
detect-node-es@1.1.0: {}
devlop@1.1.0:
@@ -10103,8 +11799,6 @@ snapshots:
domsanitizer: 0.2.3
umap: 1.0.2
- dotenv@16.6.1: {}
-
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -10127,6 +11821,8 @@ snapshots:
electron-to-chromium@1.5.200: {}
+ emittery@0.13.1: {}
+
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
@@ -10347,6 +12043,8 @@ snapshots:
escalade@3.2.0: {}
+ escape-string-regexp@2.0.0: {}
+
escape-string-regexp@4.0.0: {}
eslint-config-next@14.2.31(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2):
@@ -10534,6 +12232,8 @@ snapshots:
acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 4.2.1
+ esprima@4.0.1: {}
+
esquery@1.6.0:
dependencies:
estraverse: 5.3.0
@@ -10571,6 +12271,29 @@ snapshots:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ exit-x@0.2.2: {}
+
+ expect@30.1.2:
+ dependencies:
+ "@jest/expect-utils": 30.1.2
+ "@jest/get-type": 30.1.0
+ jest-matcher-utils: 30.1.2
+ jest-message-util: 30.1.0
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+
extend@3.0.2: {}
fake-mediastreamtrack@1.2.0:
@@ -10598,6 +12321,10 @@ snapshots:
dependencies:
reusify: 1.1.0
+ fb-watchman@2.0.2:
+ dependencies:
+ bser: 2.1.1
+
fd-slicer@1.1.0:
dependencies:
pend: 1.2.0
@@ -10618,6 +12345,11 @@ snapshots:
find-root@1.1.0: {}
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
@@ -10708,8 +12440,12 @@ snapshots:
generic-pool@3.4.2: {}
+ gensync@1.0.0-beta.2: {}
+
get-browser-rtc@1.1.0: {}
+ get-caller-file@2.0.5: {}
+
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -10725,6 +12461,8 @@ snapshots:
get-nonce@1.0.1: {}
+ get-package-type@0.1.0: {}
+
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
@@ -10734,6 +12472,8 @@ snapshots:
dependencies:
pump: 3.0.3
+ get-stream@6.0.1: {}
+
get-symbol-description@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -10744,16 +12484,6 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
- giget@1.2.5:
- dependencies:
- citty: 0.1.6
- consola: 3.4.2
- defu: 6.1.4
- node-fetch-native: 1.6.7
- nypm: 0.5.4
- pathe: 2.0.3
- tar: 6.2.1
-
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -10885,6 +12615,8 @@ snapshots:
dependencies:
react-is: 16.13.1
+ html-escaper@2.0.2: {}
+
html-url-attributes@3.0.1: {}
http-errors@1.4.0:
@@ -10907,8 +12639,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ https-proxy-agent@7.0.6(supports-color@10.2.0):
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.1(supports-color@10.2.0)
+ transitivePeerDependencies:
+ - supports-color
+
human-signals@1.1.1: {}
+ human-signals@2.1.0: {}
+
hyperhtml-style@0.1.3: {}
iconv-lite@0.4.24:
@@ -10932,8 +12673,15 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-local@3.2.0:
+ dependencies:
+ pkg-dir: 4.2.0
+ resolve-cwd: 3.0.0
+
imurmurhash@0.1.4: {}
+ index-to-position@1.1.0: {}
+
inflight@1.0.6:
dependencies:
once: 1.4.0
@@ -10953,7 +12701,7 @@ snapshots:
ioredis@5.7.0:
dependencies:
- "@ioredis/commands": 1.3.0
+ "@ioredis/commands": 1.3.1
cluster-key-slot: 1.1.2
debug: 4.4.1(supports-color@9.4.0)
denque: 2.1.0
@@ -11043,6 +12791,8 @@ snapshots:
is-fullwidth-code-point@3.0.0: {}
+ is-generator-fn@2.1.0: {}
+
is-generator-function@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -11122,6 +12872,37 @@ snapshots:
isexe@2.0.0: {}
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@6.0.3:
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/parser": 7.28.0
+ "@istanbuljs/schema": 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ "@jridgewell/trace-mapping": 0.3.30
+ debug: 4.4.1(supports-color@9.4.0)
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
iterator.prototype@1.1.5:
dependencies:
define-data-property: 1.1.4
@@ -11143,6 +12924,301 @@ snapshots:
optionalDependencies:
"@pkgjs/parseargs": 0.11.0
+ jest-changed-files@30.0.5:
+ dependencies:
+ execa: 5.1.1
+ jest-util: 30.0.5
+ p-limit: 3.1.0
+
+ jest-circus@30.1.3(babel-plugin-macros@3.1.0):
+ dependencies:
+ "@jest/environment": 30.1.2
+ "@jest/expect": 30.1.2
+ "@jest/test-result": 30.1.3
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ co: 4.6.0
+ dedent: 1.7.0(babel-plugin-macros@3.1.0)
+ is-generator-fn: 2.1.0
+ jest-each: 30.1.0
+ jest-matcher-utils: 30.1.2
+ jest-message-util: 30.1.0
+ jest-runtime: 30.1.3
+ jest-snapshot: 30.1.2
+ jest-util: 30.0.5
+ p-limit: 3.1.0
+ pretty-format: 30.0.5
+ pure-rand: 7.0.1
+ slash: 3.0.0
+ stack-utils: 2.0.6
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-cli@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ dependencies:
+ "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ "@jest/test-result": 30.1.3
+ "@jest/types": 30.0.5
+ chalk: 4.1.2
+ exit-x: 0.2.2
+ import-local: 3.2.0
+ jest-config: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - "@types/node"
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
+ jest-config@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ dependencies:
+ "@babel/core": 7.28.3
+ "@jest/get-type": 30.1.0
+ "@jest/pattern": 30.0.1
+ "@jest/test-sequencer": 30.1.3
+ "@jest/types": 30.0.5
+ babel-jest: 30.1.2(@babel/core@7.28.3)
+ chalk: 4.1.2
+ ci-info: 4.3.0
+ deepmerge: 4.3.1
+ glob: 10.4.5
+ graceful-fs: 4.2.11
+ jest-circus: 30.1.3(babel-plugin-macros@3.1.0)
+ jest-docblock: 30.0.1
+ jest-environment-node: 30.1.2
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.1.3
+ jest-runner: 30.1.3
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+ micromatch: 4.0.8
+ parse-json: 5.2.0
+ pretty-format: 30.0.5
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ "@types/node": 16.18.11
+ ts-node: 10.9.1(@types/node@16.18.11)(typescript@5.9.2)
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-config@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ dependencies:
+ "@babel/core": 7.28.3
+ "@jest/get-type": 30.1.0
+ "@jest/pattern": 30.0.1
+ "@jest/test-sequencer": 30.1.3
+ "@jest/types": 30.0.5
+ babel-jest: 30.1.2(@babel/core@7.28.3)
+ chalk: 4.1.2
+ ci-info: 4.3.0
+ deepmerge: 4.3.1
+ glob: 10.4.5
+ graceful-fs: 4.2.11
+ jest-circus: 30.1.3(babel-plugin-macros@3.1.0)
+ jest-docblock: 30.0.1
+ jest-environment-node: 30.1.2
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.1.3
+ jest-runner: 30.1.3
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+ micromatch: 4.0.8
+ parse-json: 5.2.0
+ pretty-format: 30.0.5
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ "@types/node": 24.2.1
+ ts-node: 10.9.1(@types/node@16.18.11)(typescript@5.9.2)
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-diff@30.1.2:
+ dependencies:
+ "@jest/diff-sequences": 30.0.1
+ "@jest/get-type": 30.1.0
+ chalk: 4.1.2
+ pretty-format: 30.0.5
+
+ jest-docblock@30.0.1:
+ dependencies:
+ detect-newline: 3.1.0
+
+ jest-each@30.1.0:
+ dependencies:
+ "@jest/get-type": 30.1.0
+ "@jest/types": 30.0.5
+ chalk: 4.1.2
+ jest-util: 30.0.5
+ pretty-format: 30.0.5
+
+ jest-environment-node@30.1.2:
+ dependencies:
+ "@jest/environment": 30.1.2
+ "@jest/fake-timers": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+
+ jest-haste-map@30.1.0:
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ anymatch: 3.1.3
+ fb-watchman: 2.0.2
+ graceful-fs: 4.2.11
+ jest-regex-util: 30.0.1
+ jest-util: 30.0.5
+ jest-worker: 30.1.0
+ micromatch: 4.0.8
+ walker: 1.0.8
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ jest-leak-detector@30.1.0:
+ dependencies:
+ "@jest/get-type": 30.1.0
+ pretty-format: 30.0.5
+
+ jest-matcher-utils@30.1.2:
+ dependencies:
+ "@jest/get-type": 30.1.0
+ chalk: 4.1.2
+ jest-diff: 30.1.2
+ pretty-format: 30.0.5
+
+ jest-message-util@30.1.0:
+ dependencies:
+ "@babel/code-frame": 7.27.1
+ "@jest/types": 30.0.5
+ "@types/stack-utils": 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ pretty-format: 30.0.5
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-mock@30.0.5:
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ jest-util: 30.0.5
+
+ jest-pnp-resolver@1.2.3(jest-resolve@30.1.3):
+ optionalDependencies:
+ jest-resolve: 30.1.3
+
+ jest-regex-util@30.0.1: {}
+
+ jest-resolve-dependencies@30.1.3:
+ dependencies:
+ jest-regex-util: 30.0.1
+ jest-snapshot: 30.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-resolve@30.1.3:
+ dependencies:
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.1.0
+ jest-pnp-resolver: 1.2.3(jest-resolve@30.1.3)
+ jest-util: 30.0.5
+ jest-validate: 30.1.0
+ slash: 3.0.0
+ unrs-resolver: 1.11.1
+
+ jest-runner@30.1.3:
+ dependencies:
+ "@jest/console": 30.1.2
+ "@jest/environment": 30.1.2
+ "@jest/test-result": 30.1.3
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ emittery: 0.13.1
+ exit-x: 0.2.2
+ graceful-fs: 4.2.11
+ jest-docblock: 30.0.1
+ jest-environment-node: 30.1.2
+ jest-haste-map: 30.1.0
+ jest-leak-detector: 30.1.0
+ jest-message-util: 30.1.0
+ jest-resolve: 30.1.3
+ jest-runtime: 30.1.3
+ jest-util: 30.0.5
+ jest-watcher: 30.1.3
+ jest-worker: 30.1.0
+ p-limit: 3.1.0
+ source-map-support: 0.5.13
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-runtime@30.1.3:
+ dependencies:
+ "@jest/environment": 30.1.2
+ "@jest/fake-timers": 30.1.2
+ "@jest/globals": 30.1.2
+ "@jest/source-map": 30.0.1
+ "@jest/test-result": 30.1.3
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ cjs-module-lexer: 2.1.0
+ collect-v8-coverage: 1.0.2
+ glob: 10.4.5
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.1.0
+ jest-message-util: 30.1.0
+ jest-mock: 30.0.5
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.1.3
+ jest-snapshot: 30.1.2
+ jest-util: 30.0.5
+ slash: 3.0.0
+ strip-bom: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-snapshot@30.1.2:
+ dependencies:
+ "@babel/core": 7.28.3
+ "@babel/generator": 7.28.0
+ "@babel/plugin-syntax-jsx": 7.27.1(@babel/core@7.28.3)
+ "@babel/plugin-syntax-typescript": 7.27.1(@babel/core@7.28.3)
+ "@babel/types": 7.28.2
+ "@jest/expect-utils": 30.1.2
+ "@jest/get-type": 30.1.0
+ "@jest/snapshot-utils": 30.1.2
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.3)
+ chalk: 4.1.2
+ expect: 30.1.2
+ graceful-fs: 4.2.11
+ jest-diff: 30.1.2
+ jest-matcher-utils: 30.1.2
+ jest-message-util: 30.1.0
+ jest-util: 30.0.5
+ pretty-format: 30.0.5
+ semver: 7.7.2
+ synckit: 0.11.11
+ transitivePeerDependencies:
+ - supports-color
+
jest-util@29.7.0:
dependencies:
"@jest/types": 29.6.3
@@ -11152,6 +13228,35 @@ snapshots:
graceful-fs: 4.2.11
picomatch: 2.3.1
+ jest-util@30.0.5:
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ chalk: 4.1.2
+ ci-info: 4.3.0
+ graceful-fs: 4.2.11
+ picomatch: 4.0.3
+
+ jest-validate@30.1.0:
+ dependencies:
+ "@jest/get-type": 30.1.0
+ "@jest/types": 30.0.5
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ leven: 3.1.0
+ pretty-format: 30.0.5
+
+ jest-watcher@30.1.3:
+ dependencies:
+ "@jest/test-result": 30.1.3
+ "@jest/types": 30.0.5
+ "@types/node": 24.2.1
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ emittery: 0.13.1
+ jest-util: 30.0.5
+ string-length: 4.0.2
+
jest-worker@29.7.0:
dependencies:
"@types/node": 24.2.1
@@ -11159,12 +13264,40 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
+ jest-worker@30.1.0:
+ dependencies:
+ "@types/node": 24.2.1
+ "@ungap/structured-clone": 1.3.0
+ jest-util: 30.0.5
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
+ jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ dependencies:
+ "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ "@jest/types": 30.0.5
+ import-local: 3.2.0
+ jest-cli: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ transitivePeerDependencies:
+ - "@types/node"
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
jiti@1.21.7: {}
jose@4.15.9: {}
+ js-levenshtein@1.1.6: {}
+
js-tokens@4.0.0: {}
+ js-yaml@3.14.1:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
@@ -11192,6 +13325,8 @@ snapshots:
dependencies:
minimist: 1.2.8
+ json5@2.2.3: {}
+
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
@@ -11219,6 +13354,8 @@ snapshots:
dependencies:
language-subtag-registry: 0.3.23
+ leven@3.1.0: {}
+
levn@0.4.1:
dependencies:
prelude-ls: 1.2.1
@@ -11248,6 +13385,10 @@ snapshots:
dependencies:
lie: 3.1.1
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
@@ -11256,6 +13397,8 @@ snapshots:
lodash.isarguments@3.1.0: {}
+ lodash.memoize@4.1.2: {}
+
lodash.merge@4.6.2: {}
longest-streak@3.1.0: {}
@@ -11266,6 +13409,10 @@ snapshots:
lru-cache@10.4.3: {}
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
lru-cache@6.0.0:
dependencies:
yallist: 4.0.0
@@ -11282,8 +13429,16 @@ snapshots:
dependencies:
semver: 6.3.1
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.2
+
make-error@1.3.6: {}
+ makeerror@1.0.12:
+ dependencies:
+ tmpl: 1.0.5
+
math-intrinsics@1.1.0: {}
mdast-util-from-markdown@2.0.2:
@@ -11591,13 +13746,6 @@ snapshots:
mkdirp@1.0.4: {}
- mlly@1.7.4:
- dependencies:
- acorn: 8.15.0
- pathe: 2.0.3
- pkg-types: 1.3.1
- ufo: 1.6.1
-
mri@1.2.0: {}
ms@2.1.1: {}
@@ -11618,13 +13766,13 @@ snapshots:
neo-async@2.6.2: {}
- next-auth@4.24.11(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next-auth@4.24.11(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
"@babel/runtime": 7.28.2
"@panva/hkdf": 1.2.1
cookie: 0.7.2
jose: 4.15.9
- next: 14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth: 0.9.15
openid-client: 5.7.1
preact: 10.27.0
@@ -11638,7 +13786,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
+ next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
dependencies:
"@next/env": 14.2.31
"@swc/helpers": 0.5.5
@@ -11648,7 +13796,7 @@ snapshots:
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- styled-jsx: 5.1.1(react@18.3.1)
+ styled-jsx: 5.1.1(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1)
optionalDependencies:
"@next/swc-darwin-arm64": 14.2.31
"@next/swc-darwin-x64": 14.2.31
@@ -11664,13 +13812,9 @@ snapshots:
- "@babel/core"
- babel-plugin-macros
- node-abort-controller@3.1.1: {}
-
node-addon-api@7.1.1:
optional: true
- node-fetch-native@1.6.7: {}
-
node-fetch@2.6.7:
dependencies:
whatwg-url: 5.0.0
@@ -11685,6 +13829,8 @@ snapshots:
node-gyp-build@4.8.4: {}
+ node-int64@0.4.0: {}
+
node-releases@2.0.19: {}
nopt@5.0.0:
@@ -11706,21 +13852,12 @@ snapshots:
gauge: 3.0.2
set-blocking: 2.0.0
- nuqs@2.4.3(next@14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
+ nuqs@2.4.3(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
dependencies:
mitt: 3.0.1
react: 18.3.1
optionalDependencies:
- next: 14.2.31(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
-
- nypm@0.5.4:
- dependencies:
- citty: 0.1.6
- consola: 3.4.2
- pathe: 2.0.3
- pkg-types: 1.3.1
- tinyexec: 0.3.2
- ufo: 1.6.1
+ next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth@0.9.15: {}
@@ -11770,8 +13907,6 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
- ohash@1.1.6: {}
-
oidc-token-hash@5.1.1: {}
once@1.3.3:
@@ -11786,6 +13921,28 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
+ openapi-fetch@0.14.0:
+ dependencies:
+ openapi-typescript-helpers: 0.0.15
+
+ openapi-react-query@0.5.0(@tanstack/react-query@5.85.9(react@18.3.1))(openapi-fetch@0.14.0):
+ dependencies:
+ "@tanstack/react-query": 5.85.9(react@18.3.1)
+ openapi-fetch: 0.14.0
+ openapi-typescript-helpers: 0.0.15
+
+ openapi-typescript-helpers@0.0.15: {}
+
+ openapi-typescript@7.9.1(typescript@5.9.2):
+ dependencies:
+ "@redocly/openapi-core": 1.34.5(supports-color@10.2.0)
+ ansi-colors: 4.1.3
+ change-case: 5.4.4
+ parse-json: 8.3.0
+ supports-color: 10.2.0
+ typescript: 5.9.2
+ yargs-parser: 21.1.1
+
openid-client@5.7.1:
dependencies:
jose: 4.15.9
@@ -11812,14 +13969,24 @@ snapshots:
p-finally@2.0.1: {}
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
p-locate@5.0.0:
dependencies:
p-limit: 3.1.0
+ p-try@2.2.0: {}
+
package-json-from-dist@1.0.1: {}
parent-module@1.0.1:
@@ -11843,6 +14010,12 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse-json@8.3.0:
+ dependencies:
+ "@babel/code-frame": 7.27.1
+ index-to-position: 1.1.0
+ type-fest: 4.41.0
+
parse-ms@2.1.0: {}
path-browserify@1.0.1: {}
@@ -11875,14 +14048,8 @@ snapshots:
path-type@4.0.0: {}
- pathe@1.1.2: {}
-
- pathe@2.0.3: {}
-
pend@1.2.0: {}
- perfect-debounce@1.0.0: {}
-
perfect-freehand@1.2.2: {}
picocolors@1.0.0: {}
@@ -11897,11 +14064,11 @@ snapshots:
pirates@4.0.7: {}
- pkg-types@1.3.1:
+ pkg-dir@4.2.0:
dependencies:
- confbox: 0.1.8
- mlly: 1.7.4
- pathe: 2.0.3
+ find-up: 4.1.0
+
+ pluralize@8.0.0: {}
possible-typed-array-names@1.1.0: {}
@@ -11962,6 +14129,12 @@ snapshots:
pretty-format@3.8.0: {}
+ pretty-format@30.0.5:
+ dependencies:
+ "@jest/schemas": 30.0.5
+ ansi-styles: 5.2.0
+ react-is: 18.3.1
+
pretty-ms@7.0.1:
dependencies:
parse-ms: 2.1.0
@@ -11993,6 +14166,8 @@ snapshots:
punycode@2.3.1: {}
+ pure-rand@7.0.1: {}
+
qr.js@0.0.0: {}
queue-microtask@1.2.3: {}
@@ -12008,11 +14183,6 @@ snapshots:
iconv-lite: 0.4.24
unpipe: 1.0.0
- rc9@2.1.2:
- dependencies:
- defu: 6.1.4
- destr: 2.0.5
-
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -12031,6 +14201,8 @@ snapshots:
react-is@16.13.1: {}
+ react-is@18.3.1: {}
+
react-markdown@9.1.0(@types/react@18.2.20)(react@18.3.1):
dependencies:
"@types/hast": 3.0.4
@@ -12118,10 +14290,6 @@ snapshots:
dependencies:
redis-errors: 1.2.0
- redlock@5.0.0-beta.2:
- dependencies:
- node-abort-controller: 3.1.1
-
redux-thunk@3.1.0(redux@5.0.1):
dependencies:
redux: 5.0.1
@@ -12165,12 +14333,18 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ require-directory@2.1.1: {}
+
require-from-string@2.0.2: {}
reraf@1.1.1: {}
reselect@5.1.1: {}
+ resolve-cwd@3.0.0:
+ dependencies:
+ resolve-from: 5.0.0
+
resolve-from@4.0.0: {}
resolve-from@5.0.0: {}
@@ -12339,6 +14513,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ slash@3.0.0: {}
+
socket.io-client@4.7.2:
dependencies:
"@socket.io/component-emitter": 3.1.2
@@ -12359,16 +14535,27 @@ snapshots:
source-map-js@1.2.1: {}
+ source-map-support@0.5.13:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
source-map@0.5.7: {}
source-map@0.6.1: {}
space-separated-tokens@2.0.2: {}
+ sprintf-js@1.0.3: {}
+
sprintf-js@1.1.3: {}
stable-hash@0.0.5: {}
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
stacktrace-parser@0.1.11:
dependencies:
type-fest: 0.7.1
@@ -12396,6 +14583,11 @@ snapshots:
streamsearch@1.1.0: {}
+ string-length@4.0.2:
+ dependencies:
+ char-regex: 1.0.2
+ strip-ansi: 6.0.1
+
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -12477,6 +14669,8 @@ snapshots:
strip-bom@3.0.0: {}
+ strip-bom@4.0.0: {}
+
strip-final-newline@2.0.0: {}
strip-json-comments@3.1.1: {}
@@ -12489,10 +14683,13 @@ snapshots:
dependencies:
inline-style-parser: 0.2.4
- styled-jsx@5.1.1(react@18.3.1):
+ styled-jsx@5.1.1(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
+ optionalDependencies:
+ "@babel/core": 7.28.3
+ babel-plugin-macros: 3.1.0
stylis@4.2.0: {}
@@ -12506,6 +14703,8 @@ snapshots:
pirates: 4.0.7
ts-interface-checker: 0.1.13
+ supports-color@10.2.0: {}
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -12518,6 +14717,10 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ synckit@0.11.11:
+ dependencies:
+ "@pkgr/core": 0.2.9
+
tailwindcss@3.4.17(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
dependencies:
"@alloc/quick-lru": 5.2.0
@@ -12564,6 +14767,12 @@ snapshots:
mkdirp: 1.0.4
yallist: 4.0.0
+ test-exclude@6.0.0:
+ dependencies:
+ "@istanbuljs/schema": 0.1.3
+ glob: 7.2.3
+ minimatch: 3.1.2
+
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
@@ -12576,13 +14785,13 @@ snapshots:
dependencies:
convert-hrtime: 3.0.0
- tinyexec@0.3.2: {}
-
tinyglobby@0.2.14:
dependencies:
fdir: 6.4.6(picomatch@4.0.3)
picomatch: 4.0.3
+ tmpl@1.0.5: {}
+
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -12603,6 +14812,26 @@ snapshots:
ts-interface-checker@0.1.13: {}
+ ts-jest@29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)))(typescript@5.9.2):
+ dependencies:
+ bs-logger: 0.2.6
+ fast-json-stable-stringify: 2.1.0
+ handlebars: 4.7.8
+ jest: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ json5: 2.2.3
+ lodash.memoize: 4.1.2
+ make-error: 1.3.6
+ semver: 7.7.2
+ type-fest: 4.41.0
+ typescript: 5.9.2
+ yargs-parser: 21.1.1
+ optionalDependencies:
+ "@babel/core": 7.28.3
+ "@jest/transform": 30.1.2
+ "@jest/types": 30.0.5
+ babel-jest: 30.1.2(@babel/core@7.28.3)
+ jest-util: 30.0.5
+
ts-morph@12.0.0:
dependencies:
"@ts-morph/common": 0.11.1
@@ -12660,8 +14889,14 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
+ type-detect@4.0.8: {}
+
+ type-fest@0.21.3: {}
+
type-fest@0.7.1: {}
+ type-fest@4.41.0: {}
+
typed-array-buffer@1.0.3:
dependencies:
call-bound: 1.0.4
@@ -12717,8 +14952,6 @@ snapshots:
udomdiff@1.1.2: {}
- ufo@1.6.1: {}
-
uglify-js@3.19.3:
optional: true
@@ -12739,8 +14972,6 @@ snapshots:
has-symbols: 1.1.0
which-boxed-primitive: 1.1.1
- uncrypto@0.1.3: {}
-
undici-types@7.10.0: {}
undici@5.28.4:
@@ -12818,6 +15049,8 @@ snapshots:
uqr@0.1.2: {}
+ uri-js-replace@1.0.1: {}
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@@ -12853,6 +15086,12 @@ snapshots:
v8-compile-cache-lib@3.0.1: {}
+ v8-to-istanbul@9.3.0:
+ dependencies:
+ "@jridgewell/trace-mapping": 0.3.30
+ "@types/istanbul-lib-coverage": 2.0.6
+ convert-source-map: 2.0.0
+
vercel@37.14.0:
dependencies:
"@vercel/build-utils": 8.4.12
@@ -12883,6 +15122,10 @@ snapshots:
"@types/unist": 3.0.3
vfile-message: 4.0.3
+ walker@1.0.8:
+ dependencies:
+ makeerror: 1.0.12
+
wavesurfer.js@7.10.1: {}
web-vitals@0.2.4: {}
@@ -12967,6 +15210,11 @@ snapshots:
wrappy@1.0.2: {}
+ write-file-atomic@5.0.1:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 4.1.0
+
ws@8.17.1: {}
xdg-app-paths@5.1.0:
@@ -12979,14 +15227,30 @@ snapshots:
xmlhttprequest-ssl@2.0.0: {}
+ y18n@5.0.8: {}
+
yallist@3.1.1: {}
yallist@4.0.0: {}
+ yaml-ast-parser@0.0.43: {}
+
yaml@1.10.2: {}
yaml@2.8.1: {}
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
yauzl-clone@1.0.4:
dependencies:
events-intercept: 2.0.0
@@ -13005,4 +15269,6 @@ snapshots:
yocto-queue@0.1.0: {}
+ zod@4.1.5: {}
+
zwitch@2.0.4: {}
diff --git a/www/public/service-worker.js b/www/public/service-worker.js
index 109561d5..e798e369 100644
--- a/www/public/service-worker.js
+++ b/www/public/service-worker.js
@@ -1,4 +1,4 @@
-let authToken = ""; // Variable to store the token
+let authToken = null;
self.addEventListener("message", (event) => {
if (event.data && event.data.type === "SET_AUTH_TOKEN") {
From 08d88ec349f38b0d13e0fa4cb73486c8dfd31836 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Fri, 5 Sep 2025 18:39:32 -0400
Subject: [PATCH 04/20] fix: kv use tls explicit (#610)
Co-authored-by: Igor Loskutov
---
www/app/lib/redisClient.ts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/www/app/lib/redisClient.ts b/www/app/lib/redisClient.ts
index 1be36538..753a0561 100644
--- a/www/app/lib/redisClient.ts
+++ b/www/app/lib/redisClient.ts
@@ -3,6 +3,10 @@ import { isBuildPhase } from "./next";
export type RedisClient = Pick;
+const KV_USE_TLS = process.env.KV_USE_TLS
+ ? process.env.KV_USE_TLS === "true"
+ : undefined;
+
const getRedisClient = (): RedisClient => {
const redisUrl = process.env.KV_URL;
if (!redisUrl) {
@@ -11,6 +15,11 @@ const getRedisClient = (): RedisClient => {
const redis = new Redis(redisUrl, {
maxRetriesPerRequest: 3,
lazyConnect: true,
+ ...(KV_USE_TLS === true
+ ? {
+ tls: {},
+ }
+ : {}),
});
redis.on("error", (error) => {
From 7f5a4c9ddc7fd098860c8bdda2ca3b57f63ded2f Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Fri, 5 Sep 2025 23:03:24 -0400
Subject: [PATCH 05/20] fix: token refresh locking (#613)
* fix: kv use tls explicit
* fix: token refresh locking
* remove logs
* compile fix
* compile fix
---------
Co-authored-by: Igor Loskutov
---
www/app/lib/AuthProvider.tsx | 8 ++-
www/app/lib/SessionAutoRefresh.tsx | 22 +++----
www/app/lib/authBackend.ts | 101 +++++++++++++++++++----------
www/app/lib/redisClient.ts | 47 ++++++++++----
www/app/lib/redisTokenCache.ts | 6 +-
www/app/lib/useUserName.ts | 3 +-
www/app/lib/utils.ts | 11 ++++
www/package.json | 1 +
www/pnpm-lock.yaml | 22 +++++++
9 files changed, 159 insertions(+), 62 deletions(-)
diff --git a/www/app/lib/AuthProvider.tsx b/www/app/lib/AuthProvider.tsx
index 96f49f87..6c09926b 100644
--- a/www/app/lib/AuthProvider.tsx
+++ b/www/app/lib/AuthProvider.tsx
@@ -8,10 +8,11 @@ import { assertCustomSession, CustomSession } from "./types";
import { Session } from "next-auth";
import { SessionAutoRefresh } from "./SessionAutoRefresh";
import { REFRESH_ACCESS_TOKEN_ERROR } from "./auth";
+import { assertExists } from "./utils";
type AuthContextType = (
| { status: "loading" }
- | { status: "refreshing" }
+ | { status: "refreshing"; user: CustomSession["user"] }
| { status: "unauthenticated"; error?: string }
| {
status: "authenticated";
@@ -41,7 +42,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
return { status };
}
case true: {
- return { status: "refreshing" as const };
+ return {
+ status: "refreshing" as const,
+ user: assertExists(customSession).user,
+ };
}
default: {
const _: never = sessionIsHere;
diff --git a/www/app/lib/SessionAutoRefresh.tsx b/www/app/lib/SessionAutoRefresh.tsx
index fd29367f..3729db8c 100644
--- a/www/app/lib/SessionAutoRefresh.tsx
+++ b/www/app/lib/SessionAutoRefresh.tsx
@@ -15,6 +15,7 @@ const REFRESH_BEFORE = REFRESH_ACCESS_TOKEN_BEFORE;
export function SessionAutoRefresh({ children }) {
const auth = useAuth();
+
const accessTokenExpires =
auth.status === "authenticated" ? auth.accessTokenExpires : null;
@@ -23,17 +24,16 @@ export function SessionAutoRefresh({ children }) {
// and not too slow (debuggable)
const INTERVAL_REFRESH_MS = 5000;
const interval = setInterval(() => {
- if (accessTokenExpires !== null) {
- const timeLeft = accessTokenExpires - Date.now();
- if (timeLeft < REFRESH_BEFORE) {
- auth
- .update()
- .then(() => {})
- .catch((e) => {
- // note: 401 won't be considered error here
- console.error("error refreshing auth token", e);
- });
- }
+ if (accessTokenExpires === null) return;
+ const timeLeft = accessTokenExpires - Date.now();
+ if (timeLeft < REFRESH_BEFORE) {
+ auth
+ .update()
+ .then(() => {})
+ .catch((e) => {
+ // note: 401 won't be considered error here
+ console.error("error refreshing auth token", e);
+ });
}
}, INTERVAL_REFRESH_MS);
diff --git a/www/app/lib/authBackend.ts b/www/app/lib/authBackend.ts
index af93b274..0b48f613 100644
--- a/www/app/lib/authBackend.ts
+++ b/www/app/lib/authBackend.ts
@@ -2,7 +2,11 @@ import { AuthOptions } from "next-auth";
import AuthentikProvider from "next-auth/providers/authentik";
import type { JWT } from "next-auth/jwt";
import { JWTWithAccessToken, CustomSession } from "./types";
-import { assertExists, assertExistsAndNonEmptyString } from "./utils";
+import {
+ assertExists,
+ assertExistsAndNonEmptyString,
+ assertNotExists,
+} from "./utils";
import {
REFRESH_ACCESS_TOKEN_BEFORE,
REFRESH_ACCESS_TOKEN_ERROR,
@@ -12,14 +16,10 @@ import {
setTokenCache,
deleteTokenCache,
} from "./redisTokenCache";
-import { tokenCacheRedis } from "./redisClient";
+import { tokenCacheRedis, redlock } from "./redisClient";
import { isBuildPhase } from "./next";
-// REFRESH_ACCESS_TOKEN_BEFORE because refresh is based on access token expiration (imagine we cache it 30 days)
const TOKEN_CACHE_TTL = REFRESH_ACCESS_TOKEN_BEFORE;
-
-const refreshLocks = new Map>();
-
const CLIENT_ID = !isBuildPhase
? assertExistsAndNonEmptyString(process.env.AUTHENTIK_CLIENT_ID)
: "noop";
@@ -45,31 +45,48 @@ export const authOptions: AuthOptions = {
},
callbacks: {
async jwt({ token, account, user }) {
- const KEY = `token:${token.sub}`;
+ if (account && !account.access_token) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ }
if (account && user) {
// called only on first login
// XXX account.expires_in used in example is not defined for authentik backend, but expires_at is
- const expiresAtS = assertExists(account.expires_at);
- const expiresAtMs = expiresAtS * 1000;
- if (!account.access_token) {
- await deleteTokenCache(tokenCacheRedis, KEY);
- } else {
+ if (account.access_token) {
+ const expiresAtS = assertExists(account.expires_at);
+ const expiresAtMs = expiresAtS * 1000;
const jwtToken: JWTWithAccessToken = {
...token,
accessToken: account.access_token,
accessTokenExpires: expiresAtMs,
refreshToken: account.refresh_token,
};
- await setTokenCache(tokenCacheRedis, KEY, {
- token: jwtToken,
- timestamp: Date.now(),
- });
- return jwtToken;
+ if (jwtToken.error) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ } else {
+ assertNotExists(
+ jwtToken.error,
+ `panic! trying to cache token with error in jwt: ${jwtToken.error}`,
+ );
+ await setTokenCache(tokenCacheRedis, `token:${token.sub}`, {
+ token: jwtToken,
+ timestamp: Date.now(),
+ });
+ return jwtToken;
+ }
}
}
- const currentToken = await getTokenCache(tokenCacheRedis, KEY);
+ const currentToken = await getTokenCache(
+ tokenCacheRedis,
+ `token:${token.sub}`,
+ );
+ console.debug(
+ "currentToken from cache",
+ JSON.stringify(currentToken, null, 2),
+ "will be returned?",
+ currentToken && Date.now() < currentToken.token.accessTokenExpires,
+ );
if (currentToken && Date.now() < currentToken.token.accessTokenExpires) {
return currentToken.token;
}
@@ -97,20 +114,22 @@ export const authOptions: AuthOptions = {
async function lockedRefreshAccessToken(
token: JWT,
): Promise {
- const lockKey = `${token.sub}-refresh`;
+ const lockKey = `${token.sub}-lock`;
- const existingRefresh = refreshLocks.get(lockKey);
- if (existingRefresh) {
- return await existingRefresh;
- }
-
- const refreshPromise = (async () => {
- try {
+ return redlock
+ .using([lockKey], 10000, async () => {
const cached = await getTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ if (cached)
+ console.debug(
+ "received cached token. to delete?",
+ Date.now() - cached.timestamp > TOKEN_CACHE_TTL,
+ );
+ else console.debug("no cached token received");
if (cached) {
if (Date.now() - cached.timestamp > TOKEN_CACHE_TTL) {
await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
} else if (Date.now() < cached.token.accessTokenExpires) {
+ console.debug("returning cached token", cached.token);
return cached.token;
}
}
@@ -118,19 +137,35 @@ async function lockedRefreshAccessToken(
const currentToken = cached?.token || (token as JWTWithAccessToken);
const newToken = await refreshAccessToken(currentToken);
+ console.debug("current token during refresh", currentToken);
+ console.debug("new token during refresh", newToken);
+
+ if (newToken.error) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ return newToken;
+ }
+
+ assertNotExists(
+ newToken.error,
+ `panic! trying to cache token with error during refresh: ${newToken.error}`,
+ );
await setTokenCache(tokenCacheRedis, `token:${token.sub}`, {
token: newToken,
timestamp: Date.now(),
});
return newToken;
- } finally {
- setTimeout(() => refreshLocks.delete(lockKey), 100);
- }
- })();
-
- refreshLocks.set(lockKey, refreshPromise);
- return refreshPromise;
+ })
+ .catch((e) => {
+ console.error("error refreshing token", e);
+ deleteTokenCache(tokenCacheRedis, `token:${token.sub}`).catch((e) => {
+ console.error("error deleting errored token", e);
+ });
+ return {
+ ...token,
+ error: REFRESH_ACCESS_TOKEN_ERROR,
+ } as JWTWithAccessToken;
+ });
}
async function refreshAccessToken(token: JWT): Promise {
diff --git a/www/app/lib/redisClient.ts b/www/app/lib/redisClient.ts
index 753a0561..aeb3595b 100644
--- a/www/app/lib/redisClient.ts
+++ b/www/app/lib/redisClient.ts
@@ -1,20 +1,29 @@
import Redis from "ioredis";
import { isBuildPhase } from "./next";
+import Redlock, { ResourceLockedError } from "redlock";
export type RedisClient = Pick;
-
+export type RedlockClient = {
+ using: (
+ keys: string | string[],
+ ttl: number,
+ cb: () => Promise,
+ ) => Promise;
+};
const KV_USE_TLS = process.env.KV_USE_TLS
? process.env.KV_USE_TLS === "true"
: undefined;
+let redisClient: Redis | null = null;
+
const getRedisClient = (): RedisClient => {
+ if (redisClient) return redisClient;
const redisUrl = process.env.KV_URL;
if (!redisUrl) {
throw new Error("KV_URL environment variable is required");
}
- const redis = new Redis(redisUrl, {
+ redisClient = new Redis(redisUrl, {
maxRetriesPerRequest: 3,
- lazyConnect: true,
...(KV_USE_TLS === true
? {
tls: {},
@@ -22,18 +31,11 @@ const getRedisClient = (): RedisClient => {
: {}),
});
- redis.on("error", (error) => {
+ redisClient.on("error", (error) => {
console.error("Redis error:", error);
});
- // not necessary but will indicate redis config errors by failfast at startup
- // happens only once; after that connection is allowed to die and the lib is assumed to be able to restore it eventually
- redis.connect().catch((e) => {
- console.error("Failed to connect to Redis:", e);
- process.exit(1);
- });
-
- return redis;
+ return redisClient;
};
// next.js buildtime usage - we want to isolate next.js "build" time concepts here
@@ -52,4 +54,25 @@ const noopClient: RedisClient = (() => {
del: noopDel,
};
})();
+
+const noopRedlock: RedlockClient = {
+ using: (resource: string | string[], ttl: number, cb: () => Promise) =>
+ cb(),
+};
+
+export const redlock: RedlockClient = isBuildPhase
+ ? noopRedlock
+ : (() => {
+ const r = new Redlock([getRedisClient()], {});
+ r.on("error", (error) => {
+ if (error instanceof ResourceLockedError) {
+ return;
+ }
+
+ // Log all other errors.
+ console.error(error);
+ });
+ return r;
+ })();
+
export const tokenCacheRedis = isBuildPhase ? noopClient : getRedisClient();
diff --git a/www/app/lib/redisTokenCache.ts b/www/app/lib/redisTokenCache.ts
index 4fa4e304..a8b720ef 100644
--- a/www/app/lib/redisTokenCache.ts
+++ b/www/app/lib/redisTokenCache.ts
@@ -9,7 +9,6 @@ const TokenCacheEntrySchema = z.object({
accessToken: z.string(),
accessTokenExpires: z.number(),
refreshToken: z.string().optional(),
- error: z.string().optional(),
}),
timestamp: z.number(),
});
@@ -46,14 +45,15 @@ export async function getTokenCache(
}
}
+const TTL_SECONDS = 30 * 24 * 60 * 60;
+
export async function setTokenCache(
redis: KV,
key: string,
value: TokenCacheEntry,
): Promise {
const encodedValue = TokenCacheEntryCodec.encode(value);
- const ttlSeconds = Math.floor(REFRESH_ACCESS_TOKEN_BEFORE / 1000);
- await redis.setex(key, ttlSeconds, encodedValue);
+ await redis.setex(key, TTL_SECONDS, encodedValue);
}
export async function deleteTokenCache(redis: KV, key: string): Promise {
diff --git a/www/app/lib/useUserName.ts b/www/app/lib/useUserName.ts
index 80814281..46850176 100644
--- a/www/app/lib/useUserName.ts
+++ b/www/app/lib/useUserName.ts
@@ -2,6 +2,7 @@ import { useAuth } from "./AuthProvider";
export const useUserName = (): string | null | undefined => {
const auth = useAuth();
- if (auth.status !== "authenticated") return undefined;
+ if (auth.status !== "authenticated" && auth.status !== "refreshing")
+ return undefined;
return auth.user?.name || null;
};
diff --git a/www/app/lib/utils.ts b/www/app/lib/utils.ts
index 122ab234..8e8651ff 100644
--- a/www/app/lib/utils.ts
+++ b/www/app/lib/utils.ts
@@ -158,6 +158,17 @@ export const assertExists = (
return value;
};
+export const assertNotExists = (
+ value: T | null | undefined,
+ err?: string,
+): void => {
+ if (value !== null && value !== undefined) {
+ throw new Error(
+ `Assertion failed: ${err ?? "value is not null or undefined"}`,
+ );
+ }
+};
+
export const assertExistsAndNonEmptyString = (
value: string | null | undefined,
): NonEmptyString =>
diff --git a/www/package.json b/www/package.json
index b7511147..27e30a5f 100644
--- a/www/package.json
+++ b/www/package.json
@@ -45,6 +45,7 @@
"react-markdown": "^9.0.0",
"react-qr-code": "^2.0.12",
"react-select-search": "^4.1.7",
+ "redlock": "5.0.0-beta.2",
"sass": "^1.63.6",
"simple-peer": "^9.11.1",
"tailwindcss": "^3.3.2",
diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml
index 14b42c55..f4346855 100644
--- a/www/pnpm-lock.yaml
+++ b/www/pnpm-lock.yaml
@@ -106,6 +106,9 @@ importers:
react-select-search:
specifier: ^4.1.7
version: 4.1.8(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ redlock:
+ specifier: 5.0.0-beta.2
+ version: 5.0.0-beta.2
sass:
specifier: ^1.63.6
version: 1.90.0
@@ -6566,6 +6569,12 @@ packages:
sass:
optional: true
+ node-abort-controller@3.1.1:
+ resolution:
+ {
+ integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==,
+ }
+
node-addon-api@7.1.1:
resolution:
{
@@ -7433,6 +7442,13 @@ packages:
}
engines: { node: ">=4" }
+ redlock@5.0.0-beta.2:
+ resolution:
+ {
+ integrity: sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==,
+ }
+ engines: { node: ">=12" }
+
redux-thunk@3.1.0:
resolution:
{
@@ -13812,6 +13828,8 @@ snapshots:
- "@babel/core"
- babel-plugin-macros
+ node-abort-controller@3.1.1: {}
+
node-addon-api@7.1.1:
optional: true
@@ -14290,6 +14308,10 @@ snapshots:
dependencies:
redis-errors: 1.2.0
+ redlock@5.0.0-beta.2:
+ dependencies:
+ node-abort-controller: 3.1.1
+
redux-thunk@3.1.0(redux@5.0.1):
dependencies:
redux: 5.0.1
From 02a3938822f7167125bc8a3a250eda6ea850f273 Mon Sep 17 00:00:00 2001
From: Mathieu Virbel
Date: Fri, 5 Sep 2025 22:50:10 -0600
Subject: [PATCH 06/20] chore(main): release 0.9.0 (#603)
---
CHANGELOG.md | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 433691e9..987a6579 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## [0.9.0](https://github.com/Monadical-SAS/reflector/compare/v0.8.2...v0.9.0) (2025-09-06)
+
+
+### Features
+
+* frontend openapi react query ([#606](https://github.com/Monadical-SAS/reflector/issues/606)) ([c4d2825](https://github.com/Monadical-SAS/reflector/commit/c4d2825c81f81ad8835629fbf6ea8c7383f8c31b))
+
+
+### Bug Fixes
+
+* align whisper transcriber api with parakeet ([#602](https://github.com/Monadical-SAS/reflector/issues/602)) ([0663700](https://github.com/Monadical-SAS/reflector/commit/0663700a615a4af69a03c96c410f049e23ec9443))
+* kv use tls explicit ([#610](https://github.com/Monadical-SAS/reflector/issues/610)) ([08d88ec](https://github.com/Monadical-SAS/reflector/commit/08d88ec349f38b0d13e0fa4cb73486c8dfd31836))
+* source kind for file processing ([#601](https://github.com/Monadical-SAS/reflector/issues/601)) ([dc82f8b](https://github.com/Monadical-SAS/reflector/commit/dc82f8bb3bdf3ab3d4088e592a30fd63907319e1))
+* token refresh locking ([#613](https://github.com/Monadical-SAS/reflector/issues/613)) ([7f5a4c9](https://github.com/Monadical-SAS/reflector/commit/7f5a4c9ddc7fd098860c8bdda2ca3b57f63ded2f))
+
## [0.8.2](https://github.com/Monadical-SAS/reflector/compare/v0.8.1...v0.8.2) (2025-08-29)
From 5a5b3233820df9536da75e87ce6184a983d4713a Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Mon, 8 Sep 2025 10:40:18 -0400
Subject: [PATCH 07/20] fix: sync backend and frontend token refresh logic
(#614)
* sync backend and frontend token refresh logic
* return react strict mode
---------
Co-authored-by: Igor Loskutov
---
www/app/lib/SessionAutoRefresh.tsx | 7 ++-----
www/app/lib/auth.ts | 5 +++++
www/app/lib/authBackend.ts | 11 ++++++++---
3 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/www/app/lib/SessionAutoRefresh.tsx b/www/app/lib/SessionAutoRefresh.tsx
index 3729db8c..6b26077d 100644
--- a/www/app/lib/SessionAutoRefresh.tsx
+++ b/www/app/lib/SessionAutoRefresh.tsx
@@ -9,9 +9,7 @@
import { useEffect } from "react";
import { useAuth } from "./AuthProvider";
-import { REFRESH_ACCESS_TOKEN_BEFORE } from "./auth";
-
-const REFRESH_BEFORE = REFRESH_ACCESS_TOKEN_BEFORE;
+import { shouldRefreshToken } from "./auth";
export function SessionAutoRefresh({ children }) {
const auth = useAuth();
@@ -25,8 +23,7 @@ export function SessionAutoRefresh({ children }) {
const INTERVAL_REFRESH_MS = 5000;
const interval = setInterval(() => {
if (accessTokenExpires === null) return;
- const timeLeft = accessTokenExpires - Date.now();
- if (timeLeft < REFRESH_BEFORE) {
+ if (shouldRefreshToken(accessTokenExpires)) {
auth
.update()
.then(() => {})
diff --git a/www/app/lib/auth.ts b/www/app/lib/auth.ts
index f6e60513..c83db264 100644
--- a/www/app/lib/auth.ts
+++ b/www/app/lib/auth.ts
@@ -2,6 +2,11 @@ export const REFRESH_ACCESS_TOKEN_ERROR = "RefreshAccessTokenError" as const;
// 4 min is 1 min less than default authentic value. here we assume that authentic won't be set to access tokens < 4 min
export const REFRESH_ACCESS_TOKEN_BEFORE = 4 * 60 * 1000;
+export const shouldRefreshToken = (accessTokenExpires: number): boolean => {
+ const timeLeft = accessTokenExpires - Date.now();
+ return timeLeft < REFRESH_ACCESS_TOKEN_BEFORE;
+};
+
export const LOGIN_REQUIRED_PAGES = [
"/transcripts/[!new]",
"/browse(.*)",
diff --git a/www/app/lib/authBackend.ts b/www/app/lib/authBackend.ts
index 0b48f613..06bddff2 100644
--- a/www/app/lib/authBackend.ts
+++ b/www/app/lib/authBackend.ts
@@ -10,6 +10,7 @@ import {
import {
REFRESH_ACCESS_TOKEN_BEFORE,
REFRESH_ACCESS_TOKEN_ERROR,
+ shouldRefreshToken,
} from "./auth";
import {
getTokenCache,
@@ -85,9 +86,13 @@ export const authOptions: AuthOptions = {
"currentToken from cache",
JSON.stringify(currentToken, null, 2),
"will be returned?",
- currentToken && Date.now() < currentToken.token.accessTokenExpires,
+ currentToken &&
+ !shouldRefreshToken(currentToken.token.accessTokenExpires),
);
- if (currentToken && Date.now() < currentToken.token.accessTokenExpires) {
+ if (
+ currentToken &&
+ !shouldRefreshToken(currentToken.token.accessTokenExpires)
+ ) {
return currentToken.token;
}
@@ -128,7 +133,7 @@ async function lockedRefreshAccessToken(
if (cached) {
if (Date.now() - cached.timestamp > TOKEN_CACHE_TTL) {
await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
- } else if (Date.now() < cached.token.accessTokenExpires) {
+ } else if (!shouldRefreshToken(cached.token.accessTokenExpires)) {
console.debug("returning cached token", cached.token);
return cached.token;
}
From f81fe9948a9237b3e0001b2d8ca84f54d76878f9 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Tue, 9 Sep 2025 10:50:29 -0400
Subject: [PATCH 08/20] fix: anonymous users transcript permissions (#621)
* fix: public transcript visibility
* fix: transcript permissions frontend
* dead code removal
* chore: remove unused code
* fix search tests
* fix search tests
---------
Co-authored-by: Igor Loskutov
---
server/reflector/db/search.py | 4 +-
server/reflector/views/rooms.py | 12 ++--
server/reflector/views/transcripts.py | 2 -
server/tests/test_search.py | 16 ++---
server/tests/test_search_long_summary.py | 4 +-
.../_components/TranscriptStatusIcon.tsx | 3 +-
.../[transcriptId]/_components/TopicList.tsx | 3 +-
.../[transcriptId]/correct/page.tsx | 12 ++--
.../(app)/transcripts/[transcriptId]/page.tsx | 35 ++++++----
.../[transcriptId]/record/page.tsx | 13 ++--
.../[transcriptId]/upload/page.tsx | 16 ++---
www/app/(app)/transcripts/recorder.tsx | 3 +-
www/app/(app)/transcripts/useTranscript.ts | 69 -------------------
www/app/(app)/transcripts/useWebSockets.ts | 4 +-
www/app/(app)/transcripts/webSocketTypes.ts | 3 +-
www/app/lib/apiHooks.ts | 16 ++---
www/app/lib/transcript.ts | 5 ++
www/app/reflector-api.d.ts | 13 +++-
18 files changed, 90 insertions(+), 143 deletions(-)
delete mode 100644 www/app/(app)/transcripts/useTranscript.ts
create mode 100644 www/app/lib/transcript.ts
diff --git a/server/reflector/db/search.py b/server/reflector/db/search.py
index 66a25ccf..caa21c65 100644
--- a/server/reflector/db/search.py
+++ b/server/reflector/db/search.py
@@ -23,7 +23,7 @@ from pydantic import (
from reflector.db import get_database
from reflector.db.rooms import rooms
-from reflector.db.transcripts import SourceKind, transcripts
+from reflector.db.transcripts import SourceKind, TranscriptStatus, transcripts
from reflector.db.utils import is_postgresql
from reflector.logger import logger
from reflector.utils.string import NonEmptyString, try_parse_non_empty_string
@@ -161,7 +161,7 @@ class SearchResult(BaseModel):
room_name: str | None = None
source_kind: SourceKind
created_at: datetime
- status: str = Field(..., min_length=1)
+ status: TranscriptStatus = Field(..., min_length=1)
rank: float = Field(..., ge=0, le=1)
duration: NonNegativeFloat | None = Field(..., description="Duration in seconds")
search_snippets: list[str] = Field(
diff --git a/server/reflector/views/rooms.py b/server/reflector/views/rooms.py
index cc00f3c0..38b611d6 100644
--- a/server/reflector/views/rooms.py
+++ b/server/reflector/views/rooms.py
@@ -215,14 +215,10 @@ async def rooms_create_meeting(
except (asyncpg.exceptions.UniqueViolationError, sqlite3.IntegrityError):
# Another request already created a meeting for this room
# Log this race condition occurrence
- logger.info(
- "Race condition detected for room %s - fetching existing meeting",
- room.name,
- )
logger.warning(
- "Whereby meeting %s was created but not used (resource leak) for room %s",
- whereby_meeting["meetingId"],
+ "Race condition detected for room %s and meeting %s - fetching existing meeting",
room.name,
+ whereby_meeting["meetingId"],
)
# Fetch the meeting that was created by the other request
@@ -232,7 +228,9 @@ async def rooms_create_meeting(
if meeting is None:
# Edge case: meeting was created but expired/deleted between checks
logger.error(
- "Meeting disappeared after race condition for room %s", room.name
+ "Meeting disappeared after race condition for room %s",
+ room.name,
+ exc_info=True,
)
raise HTTPException(
status_code=503, detail="Unable to join meeting - please try again"
diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py
index 9acfcbf8..ed2445ae 100644
--- a/server/reflector/views/transcripts.py
+++ b/server/reflector/views/transcripts.py
@@ -350,8 +350,6 @@ async def transcript_update(
transcript = await transcripts_controller.get_by_id_for_http(
transcript_id, user_id=user_id
)
- if not transcript:
- raise HTTPException(status_code=404, detail="Transcript not found")
values = info.dict(exclude_unset=True)
updated_transcript = await transcripts_controller.update(transcript, values)
return updated_transcript
diff --git a/server/tests/test_search.py b/server/tests/test_search.py
index 0f5c8923..82890080 100644
--- a/server/tests/test_search.py
+++ b/server/tests/test_search.py
@@ -58,7 +58,7 @@ async def test_empty_transcript_title_only_match():
"id": test_id,
"name": "Empty Transcript",
"title": "Empty Meeting",
- "status": "completed",
+ "status": "ended",
"locked": False,
"duration": 0.0,
"created_at": datetime.now(timezone.utc),
@@ -109,7 +109,7 @@ async def test_search_with_long_summary():
"id": test_id,
"name": "Test Long Summary",
"title": "Regular Meeting",
- "status": "completed",
+ "status": "ended",
"locked": False,
"duration": 1800.0,
"created_at": datetime.now(timezone.utc),
@@ -165,7 +165,7 @@ async def test_postgresql_search_with_data():
"id": test_id,
"name": "Test Search Transcript",
"title": "Engineering Planning Meeting Q4 2024",
- "status": "completed",
+ "status": "ended",
"locked": False,
"duration": 1800.0,
"created_at": datetime.now(timezone.utc),
@@ -221,7 +221,7 @@ We need to implement PostgreSQL tsvector for better performance.""",
test_result = next((r for r in results if r.id == test_id), None)
if test_result:
assert test_result.title == "Engineering Planning Meeting Q4 2024"
- assert test_result.status == "completed"
+ assert test_result.status == "ended"
assert test_result.duration == 1800.0
assert 0 <= test_result.rank <= 1, "Rank should be normalized to 0-1"
@@ -268,7 +268,7 @@ def mock_db_result():
"title": "Test Transcript",
"created_at": datetime(2024, 6, 15, tzinfo=timezone.utc),
"duration": 3600.0,
- "status": "completed",
+ "status": "ended",
"user_id": "test-user",
"room_id": "room1",
"source_kind": SourceKind.LIVE,
@@ -433,7 +433,7 @@ class TestSearchResultModel:
room_id="room-456",
source_kind=SourceKind.ROOM,
created_at=datetime(2024, 6, 15, tzinfo=timezone.utc),
- status="completed",
+ status="ended",
rank=0.85,
duration=1800.5,
search_snippets=["snippet 1", "snippet 2"],
@@ -443,7 +443,7 @@ class TestSearchResultModel:
assert result.title == "Test Title"
assert result.user_id == "user-123"
assert result.room_id == "room-456"
- assert result.status == "completed"
+ assert result.status == "ended"
assert result.rank == 0.85
assert result.duration == 1800.5
assert len(result.search_snippets) == 2
@@ -474,7 +474,7 @@ class TestSearchResultModel:
id="test-id",
source_kind=SourceKind.LIVE,
created_at=datetime(2024, 6, 15, 12, 30, 45, tzinfo=timezone.utc),
- status="completed",
+ status="ended",
rank=0.9,
duration=None,
search_snippets=[],
diff --git a/server/tests/test_search_long_summary.py b/server/tests/test_search_long_summary.py
index 8857778b..3f911a99 100644
--- a/server/tests/test_search_long_summary.py
+++ b/server/tests/test_search_long_summary.py
@@ -25,7 +25,7 @@ async def test_long_summary_snippet_prioritization():
"id": test_id,
"name": "Test Snippet Priority",
"title": "Meeting About Projects",
- "status": "completed",
+ "status": "ended",
"locked": False,
"duration": 1800.0,
"created_at": datetime.now(timezone.utc),
@@ -106,7 +106,7 @@ async def test_long_summary_only_search():
"id": test_id,
"name": "Test Long Only",
"title": "Standard Meeting",
- "status": "completed",
+ "status": "ended",
"locked": False,
"duration": 1800.0,
"created_at": datetime.now(timezone.utc),
diff --git a/www/app/(app)/browse/_components/TranscriptStatusIcon.tsx b/www/app/(app)/browse/_components/TranscriptStatusIcon.tsx
index 0eebadc8..20164993 100644
--- a/www/app/(app)/browse/_components/TranscriptStatusIcon.tsx
+++ b/www/app/(app)/browse/_components/TranscriptStatusIcon.tsx
@@ -7,9 +7,10 @@ import {
FaMicrophone,
FaGear,
} from "react-icons/fa6";
+import { TranscriptStatus } from "../../../lib/transcript";
interface TranscriptStatusIconProps {
- status: string;
+ status: TranscriptStatus;
}
export default function TranscriptStatusIcon({
diff --git a/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx b/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
index 1f5d1588..534f0c0a 100644
--- a/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
@@ -5,6 +5,7 @@ import useParticipants from "../../useParticipants";
import { Box, Flex, Text, Accordion } from "@chakra-ui/react";
import { featureEnabled } from "../../../../domainContext";
import { TopicItem } from "./TopicItem";
+import { TranscriptStatus } from "../../../../lib/transcript";
type TopicListProps = {
topics: Topic[];
@@ -14,7 +15,7 @@ type TopicListProps = {
];
autoscroll: boolean;
transcriptId: string;
- status: string;
+ status: TranscriptStatus | null;
currentTranscriptText: any;
};
diff --git a/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
index c885ca6e..1c7705f4 100644
--- a/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
@@ -9,8 +9,10 @@ import ParticipantList from "./participantList";
import type { components } from "../../../../reflector-api";
type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
import { SelectedText, selectedTextIsTimeSlice } from "./types";
-import { useTranscriptUpdate } from "../../../../lib/apiHooks";
-import useTranscript from "../../useTranscript";
+import {
+ useTranscriptGet,
+ useTranscriptUpdate,
+} from "../../../../lib/apiHooks";
import { useError } from "../../../../(errors)/errorContext";
import { useRouter } from "next/navigation";
import { Box, Grid } from "@chakra-ui/react";
@@ -25,7 +27,7 @@ export default function TranscriptCorrect({
params: { transcriptId },
}: TranscriptCorrect) {
const updateTranscriptMutation = useTranscriptUpdate();
- const transcript = useTranscript(transcriptId);
+ const transcript = useTranscriptGet(transcriptId);
const stateCurrentTopic = useState();
const [currentTopic, _sct] = stateCurrentTopic;
const stateSelectedText = useState();
@@ -36,7 +38,7 @@ export default function TranscriptCorrect({
const router = useRouter();
const markAsDone = async () => {
- if (transcript.response && !transcript.response.reviewed) {
+ if (transcript.data && !transcript.data.reviewed) {
try {
await updateTranscriptMutation.mutateAsync({
params: {
@@ -114,7 +116,7 @@ export default function TranscriptCorrect({
}}
/>
- {transcript.response && !transcript.response?.reviewed && (
+ {transcript.data && !transcript.data?.reviewed && (
;
}
@@ -86,7 +91,7 @@ export default function TranscriptDetails(details: TranscriptDetails) {
useActiveTopic={useActiveTopic}
waveform={waveform.waveform}
media={mp3.media}
- mediaDuration={transcript.response?.duration || null}
+ mediaDuration={transcript.data?.duration || null}
/>
) : !mp3.loading && (waveform.error || mp3.error) ? (
@@ -116,10 +121,10 @@ export default function TranscriptDetails(details: TranscriptDetails) {
{
- transcript.reload();
+ transcript.refetch().then(() => {});
}}
/>
@@ -136,23 +141,23 @@ export default function TranscriptDetails(details: TranscriptDetails) {
useActiveTopic={useActiveTopic}
autoscroll={false}
transcriptId={transcriptId}
- status={transcript.response?.status}
+ status={transcript.data?.status || null}
currentTranscriptText=""
/>
- {transcript.response && topics.topics ? (
+ {transcript.data && topics.topics ? (
<>
{
- transcript.reload();
+ onUpdate={() => {
+ transcript.refetch();
}}
/>
>
) : (
- {transcript.response.status == "processing" ? (
+ {transcript?.data?.status == "processing" ? (
Loading Transcript
) : (
diff --git a/www/app/(app)/transcripts/[transcriptId]/record/page.tsx b/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
index 8f6634b0..0dc26c6d 100644
--- a/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
@@ -2,7 +2,6 @@
import { useEffect, useState } from "react";
import Recorder from "../../recorder";
import { TopicList } from "../_components/TopicList";
-import useTranscript from "../../useTranscript";
import { useWebSockets } from "../../useWebSockets";
import { Topic } from "../../webSocketTypes";
import { lockWakeState, releaseWakeState } from "../../../../lib/wakeLock";
@@ -11,6 +10,8 @@ import useMp3 from "../../useMp3";
import WaveformLoading from "../../waveformLoading";
import { Box, Text, Grid, Heading, VStack, Flex } from "@chakra-ui/react";
import LiveTrancription from "../../liveTranscription";
+import { useTranscriptGet } from "../../../../lib/apiHooks";
+import { TranscriptStatus } from "../../../../lib/transcript";
type TranscriptDetails = {
params: {
@@ -19,7 +20,7 @@ type TranscriptDetails = {
};
const TranscriptRecord = (details: TranscriptDetails) => {
- const transcript = useTranscript(details.params.transcriptId);
+ const transcript = useTranscriptGet(details.params.transcriptId);
const [transcriptStarted, setTranscriptStarted] = useState(false);
const useActiveTopic = useState(null);
@@ -29,8 +30,8 @@ const TranscriptRecord = (details: TranscriptDetails) => {
const router = useRouter();
- const [status, setStatus] = useState(
- webSockets.status.value || transcript.response?.status || "idle",
+ const [status, setStatus] = useState(
+ webSockets.status?.value || transcript.data?.status || "idle",
);
useEffect(() => {
@@ -41,7 +42,7 @@ const TranscriptRecord = (details: TranscriptDetails) => {
useEffect(() => {
//TODO HANDLE ERROR STATUS BETTER
const newStatus =
- webSockets.status.value || transcript.response?.status || "idle";
+ webSockets.status?.value || transcript.data?.status || "idle";
setStatus(newStatus);
if (newStatus && (newStatus == "ended" || newStatus == "error")) {
console.log(newStatus, "redirecting");
@@ -49,7 +50,7 @@ const TranscriptRecord = (details: TranscriptDetails) => {
const newUrl = "/transcripts/" + details.params.transcriptId;
router.replace(newUrl);
}
- }, [webSockets.status.value, transcript.response?.status]);
+ }, [webSockets.status?.value, transcript.data?.status]);
useEffect(() => {
if (webSockets.waveform && webSockets.waveform) mp3.getNow();
diff --git a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
index 567272ff..844d05e9 100644
--- a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
@@ -1,12 +1,12 @@
"use client";
import { useEffect, useState } from "react";
-import useTranscript from "../../useTranscript";
import { useWebSockets } from "../../useWebSockets";
import { lockWakeState, releaseWakeState } from "../../../../lib/wakeLock";
import { useRouter } from "next/navigation";
import useMp3 from "../../useMp3";
import { Center, VStack, Text, Heading, Button } from "@chakra-ui/react";
import FileUploadButton from "../../fileUploadButton";
+import { useTranscriptGet } from "../../../../lib/apiHooks";
type TranscriptUpload = {
params: {
@@ -15,7 +15,7 @@ type TranscriptUpload = {
};
const TranscriptUpload = (details: TranscriptUpload) => {
- const transcript = useTranscript(details.params.transcriptId);
+ const transcript = useTranscriptGet(details.params.transcriptId);
const [transcriptStarted, setTranscriptStarted] = useState(false);
const webSockets = useWebSockets(details.params.transcriptId);
@@ -25,13 +25,13 @@ const TranscriptUpload = (details: TranscriptUpload) => {
const router = useRouter();
const [status_, setStatus] = useState(
- webSockets.status.value || transcript.response?.status || "idle",
+ webSockets.status?.value || transcript.data?.status || "idle",
);
// status is obviously done if we have transcript
const status =
- !transcript.loading && transcript.response?.status === "ended"
- ? transcript.response?.status
+ !transcript.isLoading && transcript.data?.status === "ended"
+ ? transcript.data?.status
: status_;
useEffect(() => {
@@ -43,9 +43,9 @@ const TranscriptUpload = (details: TranscriptUpload) => {
//TODO HANDLE ERROR STATUS BETTER
// TODO deprecate webSockets.status.value / depend on transcript.response?.status from query lib
const newStatus =
- transcript.response?.status === "ended"
+ transcript.data?.status === "ended"
? "ended"
- : webSockets.status.value || transcript.response?.status || "idle";
+ : webSockets.status?.value || transcript.data?.status || "idle";
setStatus(newStatus);
if (newStatus && (newStatus == "ended" || newStatus == "error")) {
console.log(newStatus, "redirecting");
@@ -53,7 +53,7 @@ const TranscriptUpload = (details: TranscriptUpload) => {
const newUrl = "/transcripts/" + details.params.transcriptId;
router.replace(newUrl);
}
- }, [webSockets.status.value, transcript.response?.status]);
+ }, [webSockets.status?.value, transcript.data?.status]);
useEffect(() => {
if (webSockets.waveform && webSockets.waveform) mp3.getNow();
diff --git a/www/app/(app)/transcripts/recorder.tsx b/www/app/(app)/transcripts/recorder.tsx
index 2a81395a..1cf68c39 100644
--- a/www/app/(app)/transcripts/recorder.tsx
+++ b/www/app/(app)/transcripts/recorder.tsx
@@ -11,10 +11,11 @@ import useAudioDevice from "./useAudioDevice";
import { Box, Flex, IconButton, Menu, RadioGroup } from "@chakra-ui/react";
import { LuScreenShare, LuMic, LuPlay, LuCircleStop } from "react-icons/lu";
import { RECORD_A_MEETING_URL } from "../../api/urls";
+import { TranscriptStatus } from "../../lib/transcript";
type RecorderProps = {
transcriptId: string;
- status: string;
+ status: TranscriptStatus;
};
export default function Recorder(props: RecorderProps) {
diff --git a/www/app/(app)/transcripts/useTranscript.ts b/www/app/(app)/transcripts/useTranscript.ts
deleted file mode 100644
index 3e56fb9e..00000000
--- a/www/app/(app)/transcripts/useTranscript.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import type { components } from "../../reflector-api";
-import { useTranscriptGet } from "../../lib/apiHooks";
-
-type GetTranscript = components["schemas"]["GetTranscript"];
-
-type ErrorTranscript = {
- error: Error;
- loading: false;
- response: null;
- reload: () => void;
-};
-
-type LoadingTranscript = {
- response: null;
- loading: true;
- error: false;
- reload: () => void;
-};
-
-type SuccessTranscript = {
- response: GetTranscript;
- loading: false;
- error: null;
- reload: () => void;
-};
-
-const useTranscript = (
- id: string | null,
-): ErrorTranscript | LoadingTranscript | SuccessTranscript => {
- const { data, isLoading, error, refetch } = useTranscriptGet(id);
-
- // Map to the expected return format
- if (isLoading) {
- return {
- response: null,
- loading: true,
- error: false,
- reload: refetch,
- };
- }
-
- if (error) {
- return {
- error: error as Error,
- loading: false,
- response: null,
- reload: refetch,
- };
- }
-
- // Check if data is undefined or null
- if (!data) {
- return {
- response: null,
- loading: true,
- error: false,
- reload: refetch,
- };
- }
-
- return {
- response: data,
- loading: false,
- error: null,
- reload: refetch,
- };
-};
-
-export default useTranscript;
diff --git a/www/app/(app)/transcripts/useWebSockets.ts b/www/app/(app)/transcripts/useWebSockets.ts
index 2b3205c4..f3b009c0 100644
--- a/www/app/(app)/transcripts/useWebSockets.ts
+++ b/www/app/(app)/transcripts/useWebSockets.ts
@@ -16,7 +16,7 @@ export type UseWebSockets = {
title: string;
topics: Topic[];
finalSummary: FinalSummary;
- status: Status;
+ status: Status | null;
waveform: AudioWaveform | null;
duration: number | null;
};
@@ -34,7 +34,7 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
const [finalSummary, setFinalSummary] = useState({
summary: "",
});
- const [status, setStatus] = useState({ value: "" });
+ const [status, setStatus] = useState(null);
const { setError } = useError();
const { websocket_url: websocketUrl } = useContext(DomainContext);
diff --git a/www/app/(app)/transcripts/webSocketTypes.ts b/www/app/(app)/transcripts/webSocketTypes.ts
index 4ec98946..5422cc24 100644
--- a/www/app/(app)/transcripts/webSocketTypes.ts
+++ b/www/app/(app)/transcripts/webSocketTypes.ts
@@ -1,4 +1,5 @@
import type { components } from "../../reflector-api";
+import type { TranscriptStatus } from "../../lib/transcript";
type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"];
@@ -13,7 +14,7 @@ export type FinalSummary = {
};
export type Status = {
- value: string;
+ value: TranscriptStatus;
};
export type TranslatedTopic = {
diff --git a/www/app/lib/apiHooks.ts b/www/app/lib/apiHooks.ts
index 94d84c9b..3b5eed2b 100644
--- a/www/app/lib/apiHooks.ts
+++ b/www/app/lib/apiHooks.ts
@@ -96,8 +96,6 @@ export function useTranscriptProcess() {
}
export function useTranscriptGet(transcriptId: string | null) {
- const { isAuthenticated } = useAuthReady();
-
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}",
@@ -109,7 +107,7 @@ export function useTranscriptGet(transcriptId: string | null) {
},
},
{
- enabled: !!transcriptId && isAuthenticated,
+ enabled: !!transcriptId,
},
);
}
@@ -292,18 +290,16 @@ export function useTranscriptUploadAudio() {
}
export function useTranscriptWaveform(transcriptId: string | null) {
- const { isAuthenticated } = useAuthReady();
-
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}/audio/waveform",
{
params: {
- path: { transcript_id: transcriptId || "" },
+ path: { transcript_id: transcriptId! },
},
},
{
- enabled: !!transcriptId && isAuthenticated,
+ enabled: !!transcriptId,
},
);
}
@@ -316,7 +312,7 @@ export function useTranscriptMP3(transcriptId: string | null) {
"/v1/transcripts/{transcript_id}/audio/mp3",
{
params: {
- path: { transcript_id: transcriptId || "" },
+ path: { transcript_id: transcriptId! },
},
},
{
@@ -326,8 +322,6 @@ export function useTranscriptMP3(transcriptId: string | null) {
}
export function useTranscriptTopics(transcriptId: string | null) {
- const { isAuthenticated } = useAuthReady();
-
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}/topics",
@@ -337,7 +331,7 @@ export function useTranscriptTopics(transcriptId: string | null) {
},
},
{
- enabled: !!transcriptId && isAuthenticated,
+ enabled: !!transcriptId,
},
);
}
diff --git a/www/app/lib/transcript.ts b/www/app/lib/transcript.ts
new file mode 100644
index 00000000..d1fd8b3d
--- /dev/null
+++ b/www/app/lib/transcript.ts
@@ -0,0 +1,5 @@
+import { components } from "../reflector-api";
+
+type ApiTranscriptStatus = components["schemas"]["GetTranscript"]["status"];
+
+export type TranscriptStatus = ApiTranscriptStatus;
diff --git a/www/app/reflector-api.d.ts b/www/app/reflector-api.d.ts
index 8a2cadb0..2b92f4d4 100644
--- a/www/app/reflector-api.d.ts
+++ b/www/app/reflector-api.d.ts
@@ -926,8 +926,17 @@ export interface components {
source_kind: components["schemas"]["SourceKind"];
/** Created At */
created_at: string;
- /** Status */
- status: string;
+ /**
+ * Status
+ * @enum {string}
+ */
+ status:
+ | "idle"
+ | "uploaded"
+ | "recording"
+ | "processing"
+ | "error"
+ | "ended";
/** Rank */
rank: number;
/**
From cde99ca2716f84ba26798f289047732f0448742e Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Tue, 9 Sep 2025 15:48:07 -0400
Subject: [PATCH 09/20] fix: auth post (#624)
Co-authored-by: Igor Loskutov
---
server/reflector/db/meetings.py | 18 ------------------
www/app/lib/apiClient.tsx | 17 +++++------------
2 files changed, 5 insertions(+), 30 deletions(-)
diff --git a/server/reflector/db/meetings.py b/server/reflector/db/meetings.py
index 85178351..bb7366b1 100644
--- a/server/reflector/db/meetings.py
+++ b/server/reflector/db/meetings.py
@@ -2,7 +2,6 @@ from datetime import datetime
from typing import Literal
import sqlalchemy as sa
-from fastapi import HTTPException
from pydantic import BaseModel, Field
from reflector.db import get_database, metadata
@@ -178,23 +177,6 @@ class MeetingController:
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 get_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
-
async def update_meeting(self, meeting_id: str, **kwargs):
query = meetings.update().where(meetings.c.id == meeting_id).values(**kwargs)
await get_database().execute(query)
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index cd97e151..133db7c3 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -2,12 +2,6 @@
import createClient from "openapi-fetch";
import type { paths } from "../reflector-api";
-import {
- queryOptions,
- useMutation,
- useQuery,
- useSuspenseQuery,
-} from "@tanstack/react-query";
import createFetchClient from "openapi-react-query";
import { assertExistsAndNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";
@@ -16,16 +10,11 @@ const API_URL = !isBuildPhase
? assertExistsAndNonEmptyString(process.env.NEXT_PUBLIC_API_URL)
: "http://localhost";
-// Create the base openapi-fetch client with a default URL
-// The actual URL will be set via middleware in AuthProvider
export const client = createClient({
baseUrl: API_URL,
});
-export const $api = createFetchClient(client);
-
-let currentAuthToken: string | null | undefined = null;
-
+// has to be called BEFORE $api is created with createFetchClient(client) or onRequest doesn't fire [at least for POST]
client.use({
onRequest({ request }) {
if (currentAuthToken) {
@@ -44,6 +33,10 @@ client.use({
},
});
+export const $api = createFetchClient(client);
+
+let currentAuthToken: string | null | undefined = null;
+
// the function contract: lightweight, idempotent
export const configureApiAuth = (token: string | null | undefined) => {
currentAuthToken = token;
From 3b85ff3bdf4fb053b103070646811bc990c0e70a Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Tue, 9 Sep 2025 16:27:46 -0400
Subject: [PATCH 10/20] fix: auth post (#626)
Co-authored-by: Igor Loskutov
---
www/app/lib/AuthProvider.tsx | 1 +
www/app/lib/apiClient.tsx | 26 ++++++++++++++++++++++----
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/www/app/lib/AuthProvider.tsx b/www/app/lib/AuthProvider.tsx
index 6c09926b..67c440da 100644
--- a/www/app/lib/AuthProvider.tsx
+++ b/www/app/lib/AuthProvider.tsx
@@ -88,6 +88,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
};
// not useEffect, we need it ASAP
+ // apparently, still no guarantee this code runs before mutations are fired
configureApiAuth(
contextValue.status === "authenticated" ? contextValue.accessToken : null,
);
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index 133db7c3..4bedaebe 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -14,9 +14,27 @@ export const client = createClient({
baseUrl: API_URL,
});
-// has to be called BEFORE $api is created with createFetchClient(client) or onRequest doesn't fire [at least for POST]
+const waitForAuthTokenDefinitivePresenceOrAbscence = async () => {
+ let tries = 0;
+ let time = 0;
+ const STEP = 100;
+ while (currentAuthToken === undefined) {
+ await new Promise((resolve) => setTimeout(resolve, STEP));
+ time += STEP;
+ tries++;
+ // most likely first try is more than enough, if it's more there's already something weird happens
+ if (tries > 10) {
+ // even when there's no auth assumed at all, we probably should explicitly call configureApiAuth(null)
+ throw new Error(
+ `Could not get auth token definitive presence/absence in ${time}ms. not calling configureApiAuth?`,
+ );
+ }
+ }
+};
+
client.use({
- onRequest({ request }) {
+ async onRequest({ request }) {
+ await waitForAuthTokenDefinitivePresenceOrAbscence();
if (currentAuthToken) {
request.headers.set("Authorization", `Bearer ${currentAuthToken}`);
}
@@ -35,9 +53,9 @@ client.use({
export const $api = createFetchClient(client);
-let currentAuthToken: string | null | undefined = null;
+let currentAuthToken: string | null | undefined = undefined;
// the function contract: lightweight, idempotent
-export const configureApiAuth = (token: string | null | undefined) => {
+export const configureApiAuth = (token: string | null) => {
currentAuthToken = token;
};
From 962038ee3f2a555dc3c03856be0e4409456e0996 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Tue, 9 Sep 2025 16:46:57 -0400
Subject: [PATCH 11/20] fix: auth post (#627)
Co-authored-by: Igor Loskutov
---
www/app/lib/AuthProvider.tsx | 6 +++++-
www/app/lib/apiClient.tsx | 4 +++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/www/app/lib/AuthProvider.tsx b/www/app/lib/AuthProvider.tsx
index 67c440da..1a8ebea6 100644
--- a/www/app/lib/AuthProvider.tsx
+++ b/www/app/lib/AuthProvider.tsx
@@ -90,7 +90,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
// not useEffect, we need it ASAP
// apparently, still no guarantee this code runs before mutations are fired
configureApiAuth(
- contextValue.status === "authenticated" ? contextValue.accessToken : null,
+ contextValue.status === "authenticated"
+ ? contextValue.accessToken
+ : contextValue.status === "loading"
+ ? undefined
+ : null,
);
return (
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index 4bedaebe..4b4ca6a0 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -56,6 +56,8 @@ export const $api = createFetchClient(client);
let currentAuthToken: string | null | undefined = undefined;
// the function contract: lightweight, idempotent
-export const configureApiAuth = (token: string | null) => {
+export const configureApiAuth = (token: string | null | undefined) => {
+ // watch only for the initial loading; "reloading" state assumes token presence/absence
+ if (token === undefined && currentAuthToken !== undefined) return;
currentAuthToken = token;
};
From fc363bd49b17b075e64f9186e5e0185abc325ea7 Mon Sep 17 00:00:00 2001
From: Mathieu Virbel
Date: Wed, 10 Sep 2025 08:15:47 -0600
Subject: [PATCH 12/20] fix: missing follow_redirects=True on modal endpoint
(#630)
---
server/reflector/processors/file_diarization_modal.py | 1 +
server/reflector/processors/file_transcript_modal.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/server/reflector/processors/file_diarization_modal.py b/server/reflector/processors/file_diarization_modal.py
index 518f444e..8865063d 100644
--- a/server/reflector/processors/file_diarization_modal.py
+++ b/server/reflector/processors/file_diarization_modal.py
@@ -47,6 +47,7 @@ class FileDiarizationModalProcessor(FileDiarizationProcessor):
"audio_file_url": data.audio_url,
"timestamp": 0,
},
+ follow_redirects=True,
)
response.raise_for_status()
diarization_data = response.json()["diarization"]
diff --git a/server/reflector/processors/file_transcript_modal.py b/server/reflector/processors/file_transcript_modal.py
index b99cf806..82250b6c 100644
--- a/server/reflector/processors/file_transcript_modal.py
+++ b/server/reflector/processors/file_transcript_modal.py
@@ -54,6 +54,7 @@ class FileTranscriptModalProcessor(FileTranscriptProcessor):
"language": data.language,
"batch": True,
},
+ follow_redirects=True,
)
response.raise_for_status()
result = response.json()
From 369ecdff13f3862d926a9c0b87df52c9d94c4dde Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Thu, 11 Sep 2025 11:20:41 -0400
Subject: [PATCH 13/20] feat: replace nextjs-config with environment variables
(#632)
* chore: remove nextjs-config
* build fix
* update readme
* explicit nextjs env vars + remove feature-unrelated things and obsolete vars from config
* full config removal
* remove force-dynamic from pages
* compile fix
* restore claude-deleted tests
* better .env.example
---------
Co-authored-by: Igor Loskutov
---
CLAUDE.md | 1 -
README.md | 36 +-
www/.env.example | 34 +
www/.gitignore | 1 -
www/app/(app)/layout.tsx | 10 +-
.../[transcriptId]/_components/TopicList.tsx | 3 +-
www/app/(app)/transcripts/new/page.tsx | 6 +-
www/app/(app)/transcripts/shareAndPrivacy.tsx | 3 +-
www/app/(app)/transcripts/shareLink.tsx | 3 +-
www/app/(app)/transcripts/shareZulip.tsx | 3 +-
www/app/(app)/transcripts/useMp3.ts | 9 +-
www/app/(app)/transcripts/useWebSockets.ts | 10 +-
www/app/domainContext.tsx | 49 -
www/app/layout.tsx | 22 +-
www/app/lib/apiClient.tsx | 6 +-
www/app/lib/auth.ts | 2 +
www/app/lib/edgeConfig.ts | 54 -
www/app/lib/features.ts | 55 +
www/app/lib/types.ts | 4 +
www/app/lib/utils.ts | 3 +-
www/app/providers.tsx | 10 +-
www/config-template.ts | 13 -
www/middleware.ts | 11 +-
www/package.json | 4 +-
www/pnpm-lock.yaml | 2562 ++++-------------
25 files changed, 755 insertions(+), 2159 deletions(-)
create mode 100644 www/.env.example
delete mode 100644 www/app/domainContext.tsx
delete mode 100644 www/app/lib/edgeConfig.ts
create mode 100644 www/app/lib/features.ts
delete mode 100644 www/config-template.ts
diff --git a/CLAUDE.md b/CLAUDE.md
index 14c58e42..22a99171 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -66,7 +66,6 @@ pnpm install
# Copy configuration templates
cp .env_template .env
-cp config-template.ts config.ts
```
**Development:**
diff --git a/README.md b/README.md
index 497dd5b5..ebb91fcb 100644
--- a/README.md
+++ b/README.md
@@ -99,11 +99,10 @@ Start with `cd www`.
```bash
pnpm install
-cp .env_template .env
-cp config-template.ts config.ts
+cp .env.example .env
```
-Then, fill in the environment variables in `.env` and the configuration in `config.ts` as needed. If you are unsure on how to proceed, ask in Zulip.
+Then, fill in the environment variables in `.env` as needed. If you are unsure on how to proceed, ask in Zulip.
**Run in development mode**
@@ -168,3 +167,34 @@ You can manually process an audio file by calling the process tool:
```bash
uv run python -m reflector.tools.process path/to/audio.wav
```
+
+
+## Feature Flags
+
+Reflector uses environment variable-based feature flags to control application functionality. These flags allow you to enable or disable features without code changes.
+
+### Available Feature Flags
+
+| Feature Flag | Environment Variable |
+|-------------|---------------------|
+| `requireLogin` | `NEXT_PUBLIC_FEATURE_REQUIRE_LOGIN` |
+| `privacy` | `NEXT_PUBLIC_FEATURE_PRIVACY` |
+| `browse` | `NEXT_PUBLIC_FEATURE_BROWSE` |
+| `sendToZulip` | `NEXT_PUBLIC_FEATURE_SEND_TO_ZULIP` |
+| `rooms` | `NEXT_PUBLIC_FEATURE_ROOMS` |
+
+### Setting Feature Flags
+
+Feature flags are controlled via environment variables using the pattern `NEXT_PUBLIC_FEATURE_{FEATURE_NAME}` where `{FEATURE_NAME}` is the SCREAMING_SNAKE_CASE version of the feature name.
+
+**Examples:**
+```bash
+# Enable user authentication requirement
+NEXT_PUBLIC_FEATURE_REQUIRE_LOGIN=true
+
+# Disable browse functionality
+NEXT_PUBLIC_FEATURE_BROWSE=false
+
+# Enable Zulip integration
+NEXT_PUBLIC_FEATURE_SEND_TO_ZULIP=true
+```
diff --git a/www/.env.example b/www/.env.example
new file mode 100644
index 00000000..77017d91
--- /dev/null
+++ b/www/.env.example
@@ -0,0 +1,34 @@
+# Environment
+ENVIRONMENT=development
+NEXT_PUBLIC_ENV=development
+
+# Site Configuration
+NEXT_PUBLIC_SITE_URL=http://localhost:3000
+
+# Nextauth envs
+# not used in app code but in lib code
+NEXTAUTH_URL=http://localhost:3000
+NEXTAUTH_SECRET=your-nextauth-secret-here
+# / Nextauth envs
+
+# Authentication (Authentik OAuth/OIDC)
+AUTHENTIK_ISSUER=https://authentik.example.com/application/o/reflector
+AUTHENTIK_REFRESH_TOKEN_URL=https://authentik.example.com/application/o/token/
+AUTHENTIK_CLIENT_ID=your-client-id-here
+AUTHENTIK_CLIENT_SECRET=your-client-secret-here
+
+# Feature Flags
+# NEXT_PUBLIC_FEATURE_REQUIRE_LOGIN=true
+# NEXT_PUBLIC_FEATURE_PRIVACY=false
+# NEXT_PUBLIC_FEATURE_BROWSE=true
+# NEXT_PUBLIC_FEATURE_SEND_TO_ZULIP=true
+# NEXT_PUBLIC_FEATURE_ROOMS=true
+
+# API URLs
+NEXT_PUBLIC_API_URL=http://127.0.0.1:1250
+NEXT_PUBLIC_WEBSOCKET_URL=ws://127.0.0.1:1250
+NEXT_PUBLIC_AUTH_CALLBACK_URL=http://localhost:3000/auth-callback
+
+# Sentry
+# SENTRY_DSN=https://your-dsn@sentry.io/project-id
+# SENTRY_IGNORE_API_RESOLUTION_ERROR=1
\ No newline at end of file
diff --git a/www/.gitignore b/www/.gitignore
index c0ad8c1e..9acefbb2 100644
--- a/www/.gitignore
+++ b/www/.gitignore
@@ -40,7 +40,6 @@ next-env.d.ts
# Sentry Auth Token
.sentryclirc
-config.ts
# openapi logs
openapi-ts-error-*.log
diff --git a/www/app/(app)/layout.tsx b/www/app/(app)/layout.tsx
index 801be28f..8bca1df6 100644
--- a/www/app/(app)/layout.tsx
+++ b/www/app/(app)/layout.tsx
@@ -1,5 +1,5 @@
import { Container, Flex, Link } from "@chakra-ui/react";
-import { getConfig } from "../lib/edgeConfig";
+import { featureEnabled } from "../lib/features";
import NextLink from "next/link";
import Image from "next/image";
import UserInfo from "../(auth)/userInfo";
@@ -11,8 +11,6 @@ export default async function AppLayout({
}: {
children: React.ReactNode;
}) {
- const config = await getConfig();
- const { requireLogin, privacy, browse, rooms } = config.features;
return (
Create
- {browse ? (
+ {featureEnabled("browse") ? (
<>
·
@@ -68,7 +66,7 @@ export default async function AppLayout({
) : (
<>>
)}
- {rooms ? (
+ {featureEnabled("rooms") ? (
<>
·
@@ -78,7 +76,7 @@ export default async function AppLayout({
) : (
<>>
)}
- {requireLogin ? (
+ {featureEnabled("requireLogin") ? (
<>
·
diff --git a/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx b/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
index 534f0c0a..fdf3db41 100644
--- a/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/_components/TopicList.tsx
@@ -3,10 +3,11 @@ import ScrollToBottom from "../../scrollToBottom";
import { Topic } from "../../webSocketTypes";
import useParticipants from "../../useParticipants";
import { Box, Flex, Text, Accordion } from "@chakra-ui/react";
-import { featureEnabled } from "../../../../domainContext";
import { TopicItem } from "./TopicItem";
import { TranscriptStatus } from "../../../../lib/transcript";
+import { featureEnabled } from "../../../../lib/features";
+
type TopicListProps = {
topics: Topic[];
useActiveTopic: [
diff --git a/www/app/(app)/transcripts/new/page.tsx b/www/app/(app)/transcripts/new/page.tsx
index 0410bd97..8953e994 100644
--- a/www/app/(app)/transcripts/new/page.tsx
+++ b/www/app/(app)/transcripts/new/page.tsx
@@ -9,7 +9,6 @@ import { useRouter } from "next/navigation";
import useCreateTranscript from "../createTranscript";
import SelectSearch from "react-select-search";
import { supportedLanguages } from "../../../supportedLanguages";
-import { featureEnabled } from "../../../domainContext";
import {
Flex,
Box,
@@ -21,10 +20,9 @@ import {
Spacer,
} from "@chakra-ui/react";
import { useAuth } from "../../../lib/AuthProvider";
-import type { components } from "../../../reflector-api";
+import { featureEnabled } from "../../../lib/features";
const TranscriptCreate = () => {
- const isClient = typeof window !== "undefined";
const router = useRouter();
const auth = useAuth();
const isAuthenticated = auth.status === "authenticated";
@@ -176,7 +174,7 @@ const TranscriptCreate = () => {
placeholder="Choose your language"
/>
- {isClient && !loading ? (
+ {!loading ? (
permissionOk ? (
) : permissionDenied ? (
diff --git a/www/app/(app)/transcripts/shareAndPrivacy.tsx b/www/app/(app)/transcripts/shareAndPrivacy.tsx
index a53c93e3..8580015d 100644
--- a/www/app/(app)/transcripts/shareAndPrivacy.tsx
+++ b/www/app/(app)/transcripts/shareAndPrivacy.tsx
@@ -1,5 +1,4 @@
import { useEffect, useState } from "react";
-import { featureEnabled } from "../../domainContext";
import { ShareMode, toShareMode } from "../../lib/shareMode";
import type { components } from "../../reflector-api";
@@ -24,6 +23,8 @@ import ShareCopy from "./shareCopy";
import ShareZulip from "./shareZulip";
import { useAuth } from "../../lib/AuthProvider";
+import { featureEnabled } from "../../lib/features";
+
type ShareAndPrivacyProps = {
finalSummaryRef: any;
transcriptResponse: GetTranscript;
diff --git a/www/app/(app)/transcripts/shareLink.tsx b/www/app/(app)/transcripts/shareLink.tsx
index 7ea55f5e..ee7a01bf 100644
--- a/www/app/(app)/transcripts/shareLink.tsx
+++ b/www/app/(app)/transcripts/shareLink.tsx
@@ -1,8 +1,9 @@
import React, { useState, useRef, useEffect, use } from "react";
-import { featureEnabled } from "../../domainContext";
import { Button, Flex, Input, Text } from "@chakra-ui/react";
import QRCode from "react-qr-code";
+import { featureEnabled } from "../../lib/features";
+
type ShareLinkProps = {
transcriptId: string;
};
diff --git a/www/app/(app)/transcripts/shareZulip.tsx b/www/app/(app)/transcripts/shareZulip.tsx
index 62ce1b2c..5cee16c1 100644
--- a/www/app/(app)/transcripts/shareZulip.tsx
+++ b/www/app/(app)/transcripts/shareZulip.tsx
@@ -1,5 +1,4 @@
import { useState, useEffect, useMemo } from "react";
-import { featureEnabled } from "../../domainContext";
import type { components } from "../../reflector-api";
type GetTranscript = components["schemas"]["GetTranscript"];
@@ -25,6 +24,8 @@ import {
useTranscriptPostToZulip,
} from "../../lib/apiHooks";
+import { featureEnabled } from "../../lib/features";
+
type ShareZulipProps = {
transcriptResponse: GetTranscript;
topicsResponse: GetTranscriptTopic[];
diff --git a/www/app/(app)/transcripts/useMp3.ts b/www/app/(app)/transcripts/useMp3.ts
index 223a9a4a..cc0635ec 100644
--- a/www/app/(app)/transcripts/useMp3.ts
+++ b/www/app/(app)/transcripts/useMp3.ts
@@ -1,7 +1,7 @@
-import { useContext, useEffect, useState } from "react";
-import { DomainContext } from "../../domainContext";
+import { useEffect, useState } from "react";
import { useTranscriptGet } from "../../lib/apiHooks";
import { useAuth } from "../../lib/AuthProvider";
+import { API_URL } from "../../lib/apiClient";
export type Mp3Response = {
media: HTMLMediaElement | null;
@@ -19,7 +19,6 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
null,
);
const [audioDeleted, setAudioDeleted] = useState(null);
- const { api_url } = useContext(DomainContext);
const auth = useAuth();
const accessTokenInfo =
auth.status === "authenticated" ? auth.accessToken : null;
@@ -78,7 +77,7 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
// Audio is not deleted, proceed to load it
audioElement = document.createElement("audio");
- audioElement.src = `${api_url}/v1/transcripts/${transcriptId}/audio/mp3`;
+ audioElement.src = `${API_URL}/v1/transcripts/${transcriptId}/audio/mp3`;
audioElement.crossOrigin = "anonymous";
audioElement.preload = "auto";
@@ -110,7 +109,7 @@ const useMp3 = (transcriptId: string, waiting?: boolean): Mp3Response => {
if (handleError) audioElement.removeEventListener("error", handleError);
}
};
- }, [transcriptId, transcript, later, api_url]);
+ }, [transcriptId, transcript, later]);
const getNow = () => {
setLater(false);
diff --git a/www/app/(app)/transcripts/useWebSockets.ts b/www/app/(app)/transcripts/useWebSockets.ts
index f3b009c0..09426061 100644
--- a/www/app/(app)/transcripts/useWebSockets.ts
+++ b/www/app/(app)/transcripts/useWebSockets.ts
@@ -1,13 +1,12 @@
-import { useContext, useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { Topic, FinalSummary, Status } from "./webSocketTypes";
import { useError } from "../../(errors)/errorContext";
-import { DomainContext } from "../../domainContext";
import type { components } from "../../reflector-api";
type AudioWaveform = components["schemas"]["AudioWaveform"];
type GetTranscriptSegmentTopic =
components["schemas"]["GetTranscriptSegmentTopic"];
import { useQueryClient } from "@tanstack/react-query";
-import { $api } from "../../lib/apiClient";
+import { $api, WEBSOCKET_URL } from "../../lib/apiClient";
export type UseWebSockets = {
transcriptTextLive: string;
@@ -37,7 +36,6 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
const [status, setStatus] = useState(null);
const { setError } = useError();
- const { websocket_url: websocketUrl } = useContext(DomainContext);
const queryClient = useQueryClient();
const [accumulatedText, setAccumulatedText] = useState("");
@@ -328,7 +326,7 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
if (!transcriptId) return;
- const url = `${websocketUrl}/v1/transcripts/${transcriptId}/events`;
+ const url = `${WEBSOCKET_URL}/v1/transcripts/${transcriptId}/events`;
let ws = new WebSocket(url);
ws.onopen = () => {
@@ -494,7 +492,7 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
return () => {
ws.close();
};
- }, [transcriptId, websocketUrl]);
+ }, [transcriptId]);
return {
transcriptTextLive,
diff --git a/www/app/domainContext.tsx b/www/app/domainContext.tsx
deleted file mode 100644
index 7e415f1c..00000000
--- a/www/app/domainContext.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-"use client";
-import { createContext, useContext, useEffect, useState } from "react";
-import { DomainConfig } from "./lib/edgeConfig";
-
-type DomainContextType = Omit;
-
-export const DomainContext = createContext({
- features: {
- requireLogin: false,
- privacy: true,
- browse: false,
- sendToZulip: false,
- },
- api_url: "",
- websocket_url: "",
-});
-
-export const DomainContextProvider = ({
- config,
- children,
-}: {
- config: DomainConfig;
- children: any;
-}) => {
- const [context, setContext] = useState();
-
- useEffect(() => {
- if (!config) return;
- const { auth_callback_url, ...others } = config;
- setContext(others);
- }, [config]);
-
- if (!context) return;
-
- return (
- {children}
- );
-};
-
-// Get feature config client-side with
-export const featureEnabled = (
- featureName: "requireLogin" | "privacy" | "browse" | "sendToZulip",
-) => {
- const context = useContext(DomainContext);
-
- return context.features[featureName] as boolean | undefined;
-};
-
-// Get config server-side (out of react) : see lib/edgeConfig.
diff --git a/www/app/layout.tsx b/www/app/layout.tsx
index 62175be9..93fb15e9 100644
--- a/www/app/layout.tsx
+++ b/www/app/layout.tsx
@@ -3,9 +3,7 @@ import { Metadata, Viewport } from "next";
import { Poppins } from "next/font/google";
import { ErrorProvider } from "./(errors)/errorContext";
import ErrorMessage from "./(errors)/errorMessage";
-import { DomainContextProvider } from "./domainContext";
import { RecordingConsentProvider } from "./recordingConsentContext";
-import { getConfig } from "./lib/edgeConfig";
import { ErrorBoundary } from "@sentry/nextjs";
import { Providers } from "./providers";
@@ -68,21 +66,17 @@ export default async function RootLayout({
}: {
children: React.ReactNode;
}) {
- const config = await getConfig();
-
return (
-
-
- "something went really wrong"}>
-
-
- {children}
-
-
-
-
+
+ "something went really wrong"}>
+
+
+ {children}
+
+
+
);
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index 4b4ca6a0..95051913 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -6,10 +6,14 @@ import createFetchClient from "openapi-react-query";
import { assertExistsAndNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";
-const API_URL = !isBuildPhase
+export const API_URL = !isBuildPhase
? assertExistsAndNonEmptyString(process.env.NEXT_PUBLIC_API_URL)
: "http://localhost";
+// TODO decide strict validation or not
+export const WEBSOCKET_URL =
+ process.env.NEXT_PUBLIC_WEBSOCKET_URL || "ws://127.0.0.1:1250";
+
export const client = createClient({
baseUrl: API_URL,
});
diff --git a/www/app/lib/auth.ts b/www/app/lib/auth.ts
index c83db264..e562eaed 100644
--- a/www/app/lib/auth.ts
+++ b/www/app/lib/auth.ts
@@ -1,3 +1,5 @@
+import { assertExistsAndNonEmptyString } from "./utils";
+
export const REFRESH_ACCESS_TOKEN_ERROR = "RefreshAccessTokenError" as const;
// 4 min is 1 min less than default authentic value. here we assume that authentic won't be set to access tokens < 4 min
export const REFRESH_ACCESS_TOKEN_BEFORE = 4 * 60 * 1000;
diff --git a/www/app/lib/edgeConfig.ts b/www/app/lib/edgeConfig.ts
deleted file mode 100644
index f234a2cf..00000000
--- a/www/app/lib/edgeConfig.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { get } from "@vercel/edge-config";
-import { isBuildPhase } from "./next";
-
-type EdgeConfig = {
- [domainWithDash: string]: {
- features: {
- [featureName in
- | "requireLogin"
- | "privacy"
- | "browse"
- | "sendToZulip"]: boolean;
- };
- auth_callback_url: string;
- websocket_url: string;
- api_url: string;
- };
-};
-
-export type DomainConfig = EdgeConfig["domainWithDash"];
-
-// Edge config main keys can only be alphanumeric and _ or -
-export function edgeKeyToDomain(key: string) {
- return key.replaceAll("_", ".");
-}
-
-export function edgeDomainToKey(domain: string) {
- return domain.replaceAll(".", "_");
-}
-
-// get edge config server-side (prefer DomainContext when available), domain is the hostname
-export async function getConfig() {
- if (process.env.NEXT_PUBLIC_ENV === "development") {
- try {
- return require("../../config").localConfig;
- } catch (e) {
- // next build() WILL try to execute the require above even if conditionally protected
- // but thank god it at least runs catch{} block properly
- if (!isBuildPhase) throw new Error(e);
- return require("../../config-template").localConfig;
- }
- }
-
- const domain = new URL(process.env.NEXT_PUBLIC_SITE_URL!).hostname;
- let config = await get(edgeDomainToKey(domain));
-
- if (typeof config !== "object") {
- console.warn("No config for this domain, falling back to default");
- config = await get(edgeDomainToKey("default"));
- }
-
- if (typeof config !== "object") throw Error("Error fetching config");
-
- return config as DomainConfig;
-}
diff --git a/www/app/lib/features.ts b/www/app/lib/features.ts
new file mode 100644
index 00000000..86452ae7
--- /dev/null
+++ b/www/app/lib/features.ts
@@ -0,0 +1,55 @@
+export const FEATURES = [
+ "requireLogin",
+ "privacy",
+ "browse",
+ "sendToZulip",
+ "rooms",
+] as const;
+
+export type FeatureName = (typeof FEATURES)[number];
+
+export type Features = Readonly>;
+
+export const DEFAULT_FEATURES: Features = {
+ requireLogin: false,
+ privacy: true,
+ browse: false,
+ sendToZulip: false,
+ rooms: false,
+} as const;
+
+function parseBooleanEnv(
+ value: string | undefined,
+ defaultValue: boolean = false,
+): boolean {
+ if (!value) return defaultValue;
+ return value.toLowerCase() === "true";
+}
+
+// WARNING: keep process.env.* as-is, next.js won't see them if you generate dynamically
+const features: Features = {
+ requireLogin: parseBooleanEnv(
+ process.env.NEXT_PUBLIC_FEATURE_REQUIRE_LOGIN,
+ DEFAULT_FEATURES.requireLogin,
+ ),
+ privacy: parseBooleanEnv(
+ process.env.NEXT_PUBLIC_FEATURE_PRIVACY,
+ DEFAULT_FEATURES.privacy,
+ ),
+ browse: parseBooleanEnv(
+ process.env.NEXT_PUBLIC_FEATURE_BROWSE,
+ DEFAULT_FEATURES.browse,
+ ),
+ sendToZulip: parseBooleanEnv(
+ process.env.NEXT_PUBLIC_FEATURE_SEND_TO_ZULIP,
+ DEFAULT_FEATURES.sendToZulip,
+ ),
+ rooms: parseBooleanEnv(
+ process.env.NEXT_PUBLIC_FEATURE_ROOMS,
+ DEFAULT_FEATURES.rooms,
+ ),
+};
+
+export const featureEnabled = (featureName: FeatureName): boolean => {
+ return features[featureName];
+};
diff --git a/www/app/lib/types.ts b/www/app/lib/types.ts
index 0576e186..af5625ec 100644
--- a/www/app/lib/types.ts
+++ b/www/app/lib/types.ts
@@ -72,3 +72,7 @@ export const assertCustomSession = (s: S): CustomSession => {
// no other checks for now
return r as CustomSession;
};
+
+export type Mutable = {
+ -readonly [P in keyof T]: T[P];
+};
diff --git a/www/app/lib/utils.ts b/www/app/lib/utils.ts
index 8e8651ff..11939cdb 100644
--- a/www/app/lib/utils.ts
+++ b/www/app/lib/utils.ts
@@ -171,5 +171,6 @@ export const assertNotExists = (
export const assertExistsAndNonEmptyString = (
value: string | null | undefined,
+ err?: string,
): NonEmptyString =>
- parseNonEmptyString(assertExists(value, "Expected non-empty string"));
+ parseNonEmptyString(assertExists(value, err || "Expected non-empty string"));
diff --git a/www/app/providers.tsx b/www/app/providers.tsx
index 2e3b78eb..020112ac 100644
--- a/www/app/providers.tsx
+++ b/www/app/providers.tsx
@@ -2,8 +2,8 @@
import { ChakraProvider } from "@chakra-ui/react";
import system from "./styles/theme";
+import dynamic from "next/dynamic";
-import { WherebyProvider } from "@whereby.com/browser-sdk/react";
import { Toaster } from "./components/ui/toaster";
import { NuqsAdapter } from "nuqs/adapters/next/app";
import { QueryClientProvider } from "@tanstack/react-query";
@@ -11,6 +11,14 @@ import { queryClient } from "./lib/queryClient";
import { AuthProvider } from "./lib/AuthProvider";
import { SessionProvider as SessionProviderNextAuth } from "next-auth/react";
+const WherebyProvider = dynamic(
+ () =>
+ import("@whereby.com/browser-sdk/react").then((mod) => ({
+ default: mod.WherebyProvider,
+ })),
+ { ssr: false },
+);
+
export function Providers({ children }: { children: React.ReactNode }) {
return (
diff --git a/www/config-template.ts b/www/config-template.ts
deleted file mode 100644
index e8d4c01c..00000000
--- a/www/config-template.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export const localConfig = {
- features: {
- requireLogin: true,
- privacy: true,
- browse: true,
- sendToZulip: true,
- rooms: true,
- },
- api_url: "http://127.0.0.1:1250",
- websocket_url: "ws://127.0.0.1:1250",
- auth_callback_url: "http://localhost:3000/auth-callback",
- zulip_streams: "", // Find the value on zulip
-};
diff --git a/www/middleware.ts b/www/middleware.ts
index 2b60d715..7f487cd2 100644
--- a/www/middleware.ts
+++ b/www/middleware.ts
@@ -1,5 +1,5 @@
import { withAuth } from "next-auth/middleware";
-import { getConfig } from "./app/lib/edgeConfig";
+import { featureEnabled } from "./app/lib/features";
import { NextResponse } from "next/server";
import { PROTECTED_PAGES } from "./app/lib/auth";
@@ -19,13 +19,12 @@ export const config = {
export default withAuth(
async function middleware(request) {
- const config = await getConfig();
const pathname = request.nextUrl.pathname;
// feature-flags protected paths
if (
- (!config.features.browse && pathname.startsWith("/browse")) ||
- (!config.features.rooms && pathname.startsWith("/rooms"))
+ (!featureEnabled("browse") && pathname.startsWith("/browse")) ||
+ (!featureEnabled("rooms") && pathname.startsWith("/rooms"))
) {
return NextResponse.redirect(request.nextUrl.origin);
}
@@ -33,10 +32,8 @@ export default withAuth(
{
callbacks: {
async authorized({ req, token }) {
- const config = await getConfig();
-
if (
- config.features.requireLogin &&
+ featureEnabled("requireLogin") &&
PROTECTED_PAGES.test(req.nextUrl.pathname)
) {
return !!token;
diff --git a/www/package.json b/www/package.json
index 27e30a5f..e55be4f0 100644
--- a/www/package.json
+++ b/www/package.json
@@ -20,7 +20,6 @@
"@sentry/nextjs": "^7.77.0",
"@tanstack/react-query": "^5.85.9",
"@types/ioredis": "^5.0.0",
- "@vercel/edge-config": "^0.4.1",
"@whereby.com/browser-sdk": "^3.3.4",
"autoprefixer": "10.4.20",
"axios": "^1.8.2",
@@ -63,8 +62,7 @@
"jest": "^30.1.3",
"openapi-typescript": "^7.9.1",
"prettier": "^3.0.0",
- "ts-jest": "^29.4.1",
- "vercel": "^37.3.0"
+ "ts-jest": "^29.4.1"
},
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
}
diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml
index f4346855..cf9351d4 100644
--- a/www/pnpm-lock.yaml
+++ b/www/pnpm-lock.yaml
@@ -24,16 +24,13 @@ importers:
version: 0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@18.3.1)
"@sentry/nextjs":
specifier: ^7.77.0
- version: 7.120.4(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ version: 7.77.0(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)
"@tanstack/react-query":
specifier: ^5.85.9
version: 5.85.9(react@18.3.1)
"@types/ioredis":
specifier: ^5.0.0
version: 5.0.0
- "@vercel/edge-config":
- specifier: ^0.4.1
- version: 0.4.1
"@whereby.com/browser-sdk":
specifier: ^3.3.4
version: 3.13.1(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -63,16 +60,16 @@ importers:
version: 0.525.0(react@18.3.1)
next:
specifier: ^14.2.30
- version: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ version: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
next-auth:
specifier: ^4.24.7
- version: 4.24.11(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 4.24.11(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nuqs:
specifier: ^2.4.3
- version: 2.4.3(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ version: 2.4.3(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
openapi-fetch:
specifier: ^0.14.0
version: 0.14.0
@@ -117,7 +114,7 @@ importers:
version: 9.11.1
tailwindcss:
specifier: ^3.3.2
- version: 3.4.17(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ version: 3.4.17(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
typescript:
specifier: ^5.1.6
version: 5.9.2
@@ -136,7 +133,7 @@ importers:
version: 18.2.20
jest:
specifier: ^30.1.3
- version: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ version: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
openapi-typescript:
specifier: ^7.9.1
version: 7.9.1(typescript@5.9.2)
@@ -145,10 +142,7 @@ importers:
version: 3.6.2
ts-jest:
specifier: ^29.4.1
- version: 29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)))(typescript@5.9.2)
- vercel:
- specifier: ^37.3.0
- version: 37.14.0
+ version: 29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)))(typescript@5.9.2)
packages:
"@alloc/quick-lru@5.2.0":
@@ -490,41 +484,6 @@ packages:
}
engines: { node: ">=12" }
- "@edge-runtime/format@2.2.1":
- resolution:
- {
- integrity: sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g==,
- }
- engines: { node: ">=16" }
-
- "@edge-runtime/node-utils@2.3.0":
- resolution:
- {
- integrity: sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ==,
- }
- engines: { node: ">=16" }
-
- "@edge-runtime/ponyfill@2.4.2":
- resolution:
- {
- integrity: sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA==,
- }
- engines: { node: ">=16" }
-
- "@edge-runtime/primitives@4.1.0":
- resolution:
- {
- integrity: sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==,
- }
- engines: { node: ">=16" }
-
- "@edge-runtime/vm@3.2.0":
- resolution:
- {
- integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==,
- }
- engines: { node: ">=16" }
-
"@emnapi/core@1.4.5":
resolution:
{
@@ -688,13 +647,6 @@ packages:
}
engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
- "@fastify/busboy@2.1.1":
- resolution:
- {
- integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==,
- }
- engines: { node: ">=14" }
-
"@floating-ui/core@1.7.3":
resolution:
{
@@ -995,6 +947,12 @@ packages:
}
engines: { node: ">=6.0.0" }
+ "@jridgewell/source-map@0.3.11":
+ resolution:
+ {
+ integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==,
+ }
+
"@jridgewell/sourcemap-codec@1.5.5":
resolution:
{
@@ -1013,13 +971,6 @@ packages:
integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==,
}
- "@mapbox/node-pre-gyp@1.0.11":
- resolution:
- {
- integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==,
- }
- hasBin: true
-
"@napi-rs/wasm-runtime@0.2.12":
resolution:
{
@@ -1147,6 +1098,13 @@ packages:
}
engines: { node: ">=12.4.0" }
+ "@opentelemetry/api@1.9.0":
+ resolution:
+ {
+ integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==,
+ }
+ engines: { node: ">=8.0.0" }
+
"@pandacss/is-valid-prop@0.54.0":
resolution:
{
@@ -1626,13 +1584,6 @@ packages:
rollup:
optional: true
- "@rollup/pluginutils@4.2.1":
- resolution:
- {
- integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==,
- }
- engines: { node: ">= 8.0.0" }
-
"@rollup/pluginutils@5.2.0":
resolution:
{
@@ -1657,31 +1608,17 @@ packages:
integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==,
}
- "@sentry-internal/feedback@7.120.4":
+ "@sentry-internal/tracing@7.77.0":
resolution:
{
- integrity: sha512-eSwgvTdrh03zYYaI6UVOjI9p4VmKg6+c2+CBQfRZX++6wwnCVsNv7XF7WUIpVGBAkJ0N2oapjQmCzJKGKBRWQg==,
- }
- engines: { node: ">=12" }
-
- "@sentry-internal/replay-canvas@7.120.4":
- resolution:
- {
- integrity: sha512-2+W4CgUL1VzrPjArbTid4WhKh7HH21vREVilZdvffQPVwOEpgNTPAb69loQuTlhJVveh9hWTj2nE5UXLbLP+AA==,
- }
- engines: { node: ">=12" }
-
- "@sentry-internal/tracing@7.120.4":
- resolution:
- {
- integrity: sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==,
+ integrity: sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==,
}
engines: { node: ">=8" }
- "@sentry/browser@7.120.4":
+ "@sentry/browser@7.77.0":
resolution:
{
- integrity: sha512-ymlNtIPG6HAKzM/JXpWVGCzCNufZNADfy+O/olZuVJW5Be1DtOFyRnBvz0LeKbmxJbXb2lX/XMhuen6PXPdoQw==,
+ integrity: sha512-nJ2KDZD90H8jcPx9BysQLiQW+w7k7kISCWeRjrEMJzjtge32dmHA8G4stlUTRIQugy5F+73cOayWShceFP7QJQ==,
}
engines: { node: ">=8" }
@@ -1693,24 +1630,24 @@ packages:
engines: { node: ">= 8" }
hasBin: true
- "@sentry/core@7.120.4":
+ "@sentry/core@7.77.0":
resolution:
{
- integrity: sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==,
+ integrity: sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==,
}
engines: { node: ">=8" }
- "@sentry/integrations@7.120.4":
+ "@sentry/integrations@7.77.0":
resolution:
{
- integrity: sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==,
+ integrity: sha512-P055qXgBHeZNKnnVEs5eZYLdy6P49Zr77A1aWJuNih/EenzMy922GOeGy2mF6XYrn1YJSjEwsNMNsQkcvMTK8Q==,
}
engines: { node: ">=8" }
- "@sentry/nextjs@7.120.4":
+ "@sentry/nextjs@7.77.0":
resolution:
{
- integrity: sha512-1wtyDP1uiVvYqaJyCgXfP69eqyDgJrd6lERAVd4WqXNVEIs4vBT8oxfPQz6gxG2SJJUiTyQRjubMxuEc7dPoGQ==,
+ integrity: sha512-8tYPBt5luFjrng1sAMJqNjM9sq80q0jbt6yariADU9hEr7Zk8YqFaOI2/Q6yn9dZ6XyytIRtLEo54kk2AO94xw==,
}
engines: { node: ">=8" }
peerDependencies:
@@ -1721,63 +1658,57 @@ packages:
webpack:
optional: true
- "@sentry/node@7.120.4":
+ "@sentry/node@7.77.0":
resolution:
{
- integrity: sha512-qq3wZAXXj2SRWhqErnGCSJKUhPSlZ+RGnCZjhfjHpP49KNpcd9YdPTIUsFMgeyjdh6Ew6aVCv23g1hTP0CHpYw==,
+ integrity: sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g==,
}
engines: { node: ">=8" }
- "@sentry/react@7.120.4":
+ "@sentry/react@7.77.0":
resolution:
{
- integrity: sha512-Pj1MSezEncE+5riuwsk8peMncuz5HR72Yr1/RdZhMZvUxoxAR/tkwD3aPcK6ddQJTagd2TGwhdr9SHuDLtONew==,
+ integrity: sha512-Q+htKzib5em0MdaQZMmPomaswaU3xhcVqmLi2CxqQypSjbYgBPPd+DuhrXKoWYLDDkkbY2uyfe4Lp3yLRWeXYw==,
}
engines: { node: ">=8" }
peerDependencies:
react: 15.x || 16.x || 17.x || 18.x
- "@sentry/replay@7.120.4":
+ "@sentry/replay@7.77.0":
resolution:
{
- integrity: sha512-FW8sPenNFfnO/K7sncsSTX4rIVak9j7VUiLIagJrcqZIC7d1dInFNjy8CdVJUlyz3Y3TOgIl3L3+ZpjfyMnaZg==,
+ integrity: sha512-M9Ik2J5ekl+C1Och3wzLRZVaRGK33BlnBwfwf3qKjgLDwfKW+1YkwDfTHbc2b74RowkJbOVNcp4m8ptlehlSaQ==,
}
engines: { node: ">=12" }
- "@sentry/types@7.120.4":
+ "@sentry/types@7.77.0":
resolution:
{
- integrity: sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==,
+ integrity: sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==,
}
engines: { node: ">=8" }
- "@sentry/utils@7.120.4":
+ "@sentry/utils@7.77.0":
resolution:
{
- integrity: sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==,
+ integrity: sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==,
}
engines: { node: ">=8" }
- "@sentry/vercel-edge@7.120.4":
+ "@sentry/vercel-edge@7.77.0":
resolution:
{
- integrity: sha512-wZMnF7Rt2IBfStQTVDhjShEtLcsH1WNc7YVgzoibuIeRDrEmyx/MFIsru2BkhWnz7m0TRnWXxA40cH+6VZsf5w==,
+ integrity: sha512-ffddPCgxVeAccPYuH5sooZeHBqDuJ9OIhIRYKoDi4TvmwAzWo58zzZWhRpkHqHgIQdQvhLVZ5F+FSQVWnYSOkw==,
}
engines: { node: ">=8" }
- "@sentry/webpack-plugin@1.21.0":
+ "@sentry/webpack-plugin@1.20.0":
resolution:
{
- integrity: sha512-x0PYIMWcsTauqxgl7vWUY6sANl+XGKtx7DCVnnY7aOIIlIna0jChTAPANTfA2QrK+VK+4I/4JxatCEZBnXh3Og==,
+ integrity: sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==,
}
engines: { node: ">= 8" }
- "@sinclair/typebox@0.25.24":
- resolution:
- {
- integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==,
- }
-
"@sinclair/typebox@0.27.8":
resolution:
{
@@ -1852,19 +1783,6 @@ packages:
peerDependencies:
react: ^18 || ^19
- "@tootallnate/once@2.0.0":
- resolution:
- {
- integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==,
- }
- engines: { node: ">= 10" }
-
- "@ts-morph/common@0.11.1":
- resolution:
- {
- integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==,
- }
-
"@tsconfig/node10@1.0.11":
resolution:
{
@@ -1925,6 +1843,18 @@ packages:
integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==,
}
+ "@types/eslint-scope@3.7.7":
+ resolution:
+ {
+ integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==,
+ }
+
+ "@types/eslint@9.6.1":
+ resolution:
+ {
+ integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==,
+ }
+
"@types/estree-jsx@1.0.5":
resolution:
{
@@ -2010,12 +1940,6 @@ packages:
integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==,
}
- "@types/node@16.18.11":
- resolution:
- {
- integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==,
- }
-
"@types/node@24.2.1":
resolution:
{
@@ -2365,122 +2289,94 @@ packages:
cpu: [x64]
os: [win32]
- "@vercel/build-utils@8.4.12":
+ "@webassemblyjs/ast@1.14.1":
resolution:
{
- integrity: sha512-pIH0b965wJhd1otROVPndfZenPKFVoYSaRjtSKVOT/oNBT13ifq86UVjb5ZjoVfqUI2TtSTP+68kBqLPeoq30g==,
+ integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==,
}
- "@vercel/edge-config-fs@0.1.0":
+ "@webassemblyjs/floating-point-hex-parser@1.13.2":
resolution:
{
- integrity: sha512-NRIBwfcS0bUoUbRWlNGetqjvLSwgYH/BqKqDN7vK1g32p7dN96k0712COgaz6VFizAm9b0g6IG6hR6+hc0KCPg==,
+ integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==,
}
- "@vercel/edge-config@0.4.1":
+ "@webassemblyjs/helper-api-error@1.13.2":
resolution:
{
- integrity: sha512-4Mc3H7lE+x4RrL17nY8CWeEorvJHbkNbQTy9p8H1tO7y11WeKj5xeZSr07wNgfWInKXDUwj5FZ3qd/jIzjPxug==,
- }
- engines: { node: ">=14.6" }
-
- "@vercel/error-utils@2.0.2":
- resolution:
- {
- integrity: sha512-Sj0LFafGpYr6pfCqrQ82X6ukRl5qpmVrHM/191kNYFqkkB9YkjlMAj6QcEsvCG259x4QZ7Tya++0AB85NDPbKQ==,
+ integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==,
}
- "@vercel/fun@1.1.0":
+ "@webassemblyjs/helper-buffer@1.14.1":
resolution:
{
- integrity: sha512-SpuPAo+MlAYMtcMcC0plx7Tv4Mp7SQhJJj1iIENlOnABL24kxHpL09XLQMGzZIzIW7upR8c3edwgfpRtp+dhVw==,
- }
- engines: { node: ">= 10" }
-
- "@vercel/gatsby-plugin-vercel-analytics@1.0.11":
- resolution:
- {
- integrity: sha512-iTEA0vY6RBPuEzkwUTVzSHDATo1aF6bdLLspI68mQ/BTbi5UQEGjpjyzdKOVcSYApDtFU6M6vypZ1t4vIEnHvw==,
+ integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==,
}
- "@vercel/gatsby-plugin-vercel-builder@2.0.56":
+ "@webassemblyjs/helper-numbers@1.13.2":
resolution:
{
- integrity: sha512-SZM8k/YcOcfk2p1cSZOuSK37CDBJtF/WiEr8CemDI/MBbXM4aC2StfzDd0F0cK/2rExpSA9lTAE9ia3w+cDS9w==,
+ integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==,
}
- "@vercel/go@3.2.0":
+ "@webassemblyjs/helper-wasm-bytecode@1.13.2":
resolution:
{
- integrity: sha512-zUCBoh57x1OEtw+TKdRhSQciqERrpDxLlPeBOYawUCC5uKjsBjhdq0U21+NGz2LcRUaYyYYGMw6BzqVaig9u1g==,
+ integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==,
}
- "@vercel/hydrogen@1.0.9":
+ "@webassemblyjs/helper-wasm-section@1.14.1":
resolution:
{
- integrity: sha512-IPAVaALuGAzt2apvTtBs5tB+8zZRzn/yG3AGp8dFyCsw/v5YOuk0Q5s8Z3fayLvJbFpjrKtqRNDZzVJBBU3MrQ==,
+ integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==,
}
- "@vercel/next@4.3.18":
+ "@webassemblyjs/ieee754@1.13.2":
resolution:
{
- integrity: sha512-ih6++AA7/NCcLkMpdsDhr/folMlAKsU1sYUoyOjq4rYE9sSapELtgxls0CArv4ehE2Tt4YwoxBISnKPZKK5SSA==,
+ integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==,
}
- "@vercel/nft@0.27.3":
+ "@webassemblyjs/leb128@1.13.2":
resolution:
{
- integrity: sha512-oySTdDSzUAFDXpsSLk9Q943o+/Yu/+TCFxnehpFQEf/3khi2stMpTHPVNwFdvZq/Z4Ky93lE+MGHpXCRpMkSCA==,
- }
- engines: { node: ">=16" }
- hasBin: true
-
- "@vercel/node@3.2.24":
- resolution:
- {
- integrity: sha512-KEm50YBmcfRNOw5NfdcqMI4BkP4+5TD9kRwAByHHlIZXLj1NTTknvMF+69sHBYzwpK/SUZIkeo7jTrtcl4g+RQ==,
+ integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==,
}
- "@vercel/python@4.3.1":
+ "@webassemblyjs/utf8@1.13.2":
resolution:
{
- integrity: sha512-pWRApBwUsAQJS8oZ7eKMiaBGbYJO71qw2CZqDFvkTj34FNBZtNIUcWSmqGfJJY5m2pU/9wt8z1CnKIyT9dstog==,
+ integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==,
}
- "@vercel/redwood@2.1.8":
+ "@webassemblyjs/wasm-edit@1.14.1":
resolution:
{
- integrity: sha512-qBUBqIDxPEYnxRh3tsvTaPMtBkyK/D2tt9tBugNPe0OeYnMCMXVj9SJYbxiDI2GzAEFUZn4Poh63CZtXMDb9Tg==,
+ integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==,
}
- "@vercel/remix-builder@2.2.13":
+ "@webassemblyjs/wasm-gen@1.14.1":
resolution:
{
- integrity: sha512-TenVtvfERodSwUjm0rzjz3v00Drd0FUXLWnwdwnv7VLgqmX2FW/2+1byhmPhJicMp3Eybl52GvF2/KbBkNo95w==,
+ integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==,
}
- "@vercel/routing-utils@3.1.0":
+ "@webassemblyjs/wasm-opt@1.14.1":
resolution:
{
- integrity: sha512-Ci5xTjVTJY/JLZXpCXpLehMft97i9fH34nu9PGav6DtwkVUF6TOPX86U0W0niQjMZ5n6/ZP0BwcJK2LOozKaGw==,
+ integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==,
}
- "@vercel/ruby@2.1.0":
+ "@webassemblyjs/wasm-parser@1.14.1":
resolution:
{
- integrity: sha512-UZYwlSEEfVnfzTmgkD+kxex9/gkZGt7unOWNyWFN7V/ZnZSsGBUgv6hXLnwejdRi3EztgRQEBd1kUKlXdIeC0Q==,
+ integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==,
}
- "@vercel/static-build@2.5.34":
+ "@webassemblyjs/wast-printer@1.14.1":
resolution:
{
- integrity: sha512-4RL60ghhBufs/45j6J9zQzMpt8JmUhp/4+xE8RxO80n6qTlc/oERKrWxzeXLEGF32whSHsB+ROJt0Ytytoz2Tw==,
- }
-
- "@vercel/static-config@3.0.0":
- resolution:
- {
- integrity: sha512-2qtvcBJ1bGY0dYGYh3iM7yGKkk971FujLEDXzuW5wcZsPr1GSEjO/w2iSr3qve6nDDtBImsGoDEnus5FI4+fIw==,
+ integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==,
}
"@whereby.com/browser-sdk@3.13.1":
@@ -2505,6 +2401,18 @@ packages:
}
engines: { node: ">=16.0.0" }
+ "@xtuc/ieee754@1.2.0":
+ resolution:
+ {
+ integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==,
+ }
+
+ "@xtuc/long@4.2.2":
+ resolution:
+ {
+ integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==,
+ }
+
"@zag-js/accordion@1.21.0":
resolution:
{
@@ -2922,19 +2830,14 @@ packages:
integrity: sha512-yI/CZizbk387TdkDCy9Uc4l53uaeQuWAIJESrmAwwq6yMNbHZ2dm5+1NHdZr/guES5TgyJa/BYJsNJeCsCfesg==,
}
- abbrev@1.1.1:
+ acorn-import-phases@1.0.4:
resolution:
{
- integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==,
- }
-
- acorn-import-attributes@1.9.5:
- resolution:
- {
- integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==,
+ integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==,
}
+ engines: { node: ">=10.13.0" }
peerDependencies:
- acorn: ^8
+ acorn: ^8.14.0
acorn-jsx@5.3.2:
resolution:
@@ -2973,16 +2876,35 @@ packages:
}
engines: { node: ">= 14" }
+ ajv-formats@2.1.1:
+ resolution:
+ {
+ integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==,
+ }
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
+ ajv-keywords@5.1.0:
+ resolution:
+ {
+ integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==,
+ }
+ peerDependencies:
+ ajv: ^8.8.2
+
ajv@6.12.6:
resolution:
{
integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==,
}
- ajv@8.6.3:
+ ajv@8.17.1:
resolution:
{
- integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==,
+ integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==,
}
ansi-colors@4.1.3:
@@ -3047,26 +2969,6 @@ packages:
}
engines: { node: ">= 8" }
- aproba@2.1.0:
- resolution:
- {
- integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==,
- }
-
- are-we-there-yet@2.0.0:
- resolution:
- {
- integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==,
- }
- engines: { node: ">=10" }
- deprecated: This package is no longer supported.
-
- arg@4.1.0:
- resolution:
- {
- integrity: sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==,
- }
-
arg@4.1.3:
resolution:
{
@@ -3174,32 +3076,6 @@ packages:
}
engines: { node: ">= 0.4" }
- async-listen@1.2.0:
- resolution:
- {
- integrity: sha512-CcEtRh/oc9Jc4uWeUwdpG/+Mb2YUHKmdaTf0gUr7Wa+bfp4xx70HOb3RuSTJMvqKNB1TkdTfjLdrcz2X4rkkZA==,
- }
-
- async-listen@3.0.0:
- resolution:
- {
- integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==,
- }
- engines: { node: ">= 14" }
-
- async-listen@3.0.1:
- resolution:
- {
- integrity: sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==,
- }
- engines: { node: ">= 14" }
-
- async-sema@3.1.1:
- resolution:
- {
- integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==,
- }
-
asynckit@0.4.0:
resolution:
{
@@ -3328,12 +3204,6 @@ packages:
}
engines: { node: ">=8" }
- bindings@1.5.0:
- resolution:
- {
- integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==,
- }
-
brace-expansion@1.1.12:
resolution:
{
@@ -3382,12 +3252,6 @@ packages:
engines: { node: ">= 0.4.0" }
hasBin: true
- buffer-crc32@0.2.13:
- resolution:
- {
- integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==,
- }
-
buffer-from@1.1.2:
resolution:
{
@@ -3407,13 +3271,6 @@ packages:
}
engines: { node: ">=10.16.0" }
- bytes@3.1.0:
- resolution:
- {
- integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==,
- }
- engines: { node: ">= 0.8" }
-
call-bind-apply-helpers@1.0.2:
resolution:
{
@@ -3532,13 +3389,6 @@ packages:
integrity: sha512-LuLBA6r4aS/4B7pvOqmT4Bi+GKnNNC/V18K0zDTRFjAxNeUzGsr0wmsOfFhFH7fGjwdx6GX6wyIQBkUhFox2Pw==,
}
- chokidar@3.3.1:
- resolution:
- {
- integrity: sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==,
- }
- engines: { node: ">= 8.10.0" }
-
chokidar@3.6.0:
resolution:
{
@@ -3553,18 +3403,12 @@ packages:
}
engines: { node: ">= 14.16.0" }
- chownr@1.1.4:
+ chrome-trace-event@1.0.4:
resolution:
{
- integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==,
+ integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==,
}
-
- chownr@2.0.0:
- resolution:
- {
- integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==,
- }
- engines: { node: ">=10" }
+ engines: { node: ">=6.0" }
ci-info@3.9.0:
resolution:
@@ -3580,12 +3424,6 @@ packages:
}
engines: { node: ">=8" }
- cjs-module-lexer@1.2.3:
- resolution:
- {
- integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==,
- }
-
cjs-module-lexer@2.1.0:
resolution:
{
@@ -3632,12 +3470,6 @@ packages:
}
engines: { iojs: ">= 1.0.0", node: ">= 0.12.0" }
- code-block-writer@10.1.1:
- resolution:
- {
- integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==,
- }
-
collect-v8-coverage@1.0.2:
resolution:
{
@@ -3657,13 +3489,6 @@ packages:
integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==,
}
- color-support@1.1.3:
- resolution:
- {
- integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==,
- }
- hasBin: true
-
colorette@1.4.0:
resolution:
{
@@ -3683,6 +3508,12 @@ packages:
integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==,
}
+ commander@2.20.3:
+ resolution:
+ {
+ integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==,
+ }
+
commander@4.1.1:
resolution:
{
@@ -3702,26 +3533,6 @@ packages:
integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==,
}
- console-control-strings@1.1.0:
- resolution:
- {
- integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==,
- }
-
- content-type@1.0.4:
- resolution:
- {
- integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==,
- }
- engines: { node: ">= 0.6" }
-
- convert-hrtime@3.0.0:
- resolution:
- {
- integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==,
- }
- engines: { node: ">=8" }
-
convert-source-map@1.9.0:
resolution:
{
@@ -3813,18 +3624,6 @@ packages:
supports-color:
optional: true
- debug@4.1.1:
- resolution:
- {
- integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==,
- }
- deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
- peerDependencies:
- supports-color: "*"
- peerDependenciesMeta:
- supports-color:
- optional: true
-
debug@4.3.7:
resolution:
{
@@ -3900,12 +3699,6 @@ packages:
}
engines: { node: ">=0.4.0" }
- delegates@1.0.0:
- resolution:
- {
- integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==,
- }
-
denque@2.1.0:
resolution:
{
@@ -3913,13 +3706,6 @@ packages:
}
engines: { node: ">=0.10" }
- depd@1.1.2:
- resolution:
- {
- integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==,
- }
- engines: { node: ">= 0.6" }
-
dequal@2.0.3:
resolution:
{
@@ -3941,13 +3727,6 @@ packages:
engines: { node: ">=0.10" }
hasBin: true
- detect-libc@2.0.4:
- resolution:
- {
- integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==,
- }
- engines: { node: ">=8" }
-
detect-newline@3.1.0:
resolution:
{
@@ -4024,14 +3803,6 @@ packages:
integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==,
}
- edge-runtime@2.5.9:
- resolution:
- {
- integrity: sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg==,
- }
- engines: { node: ">=16" }
- hasBin: true
-
electron-to-chromium@1.5.200:
resolution:
{
@@ -4057,18 +3828,6 @@ packages:
integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==,
}
- end-of-stream@1.1.0:
- resolution:
- {
- integrity: sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ==,
- }
-
- end-of-stream@1.4.5:
- resolution:
- {
- integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==,
- }
-
engine.io-client@6.5.4:
resolution:
{
@@ -4082,6 +3841,13 @@ packages:
}
engines: { node: ">=10.0.0" }
+ enhanced-resolve@5.18.3:
+ resolution:
+ {
+ integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==,
+ }
+ engines: { node: ">=10.13.0" }
+
err-code@3.0.1:
resolution:
{
@@ -4122,10 +3888,10 @@ packages:
}
engines: { node: ">= 0.4" }
- es-module-lexer@1.4.1:
+ es-module-lexer@1.7.0:
resolution:
{
- integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==,
+ integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==,
}
es-object-atoms@1.1.1:
@@ -4156,194 +3922,6 @@ packages:
}
engines: { node: ">= 0.4" }
- esbuild-android-64@0.14.47:
- resolution:
- {
- integrity: sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [android]
-
- esbuild-android-arm64@0.14.47:
- resolution:
- {
- integrity: sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==,
- }
- engines: { node: ">=12" }
- cpu: [arm64]
- os: [android]
-
- esbuild-darwin-64@0.14.47:
- resolution:
- {
- integrity: sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [darwin]
-
- esbuild-darwin-arm64@0.14.47:
- resolution:
- {
- integrity: sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==,
- }
- engines: { node: ">=12" }
- cpu: [arm64]
- os: [darwin]
-
- esbuild-freebsd-64@0.14.47:
- resolution:
- {
- integrity: sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [freebsd]
-
- esbuild-freebsd-arm64@0.14.47:
- resolution:
- {
- integrity: sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==,
- }
- engines: { node: ">=12" }
- cpu: [arm64]
- os: [freebsd]
-
- esbuild-linux-32@0.14.47:
- resolution:
- {
- integrity: sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==,
- }
- engines: { node: ">=12" }
- cpu: [ia32]
- os: [linux]
-
- esbuild-linux-64@0.14.47:
- resolution:
- {
- integrity: sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [linux]
-
- esbuild-linux-arm64@0.14.47:
- resolution:
- {
- integrity: sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==,
- }
- engines: { node: ">=12" }
- cpu: [arm64]
- os: [linux]
-
- esbuild-linux-arm@0.14.47:
- resolution:
- {
- integrity: sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==,
- }
- engines: { node: ">=12" }
- cpu: [arm]
- os: [linux]
-
- esbuild-linux-mips64le@0.14.47:
- resolution:
- {
- integrity: sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==,
- }
- engines: { node: ">=12" }
- cpu: [mips64el]
- os: [linux]
-
- esbuild-linux-ppc64le@0.14.47:
- resolution:
- {
- integrity: sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==,
- }
- engines: { node: ">=12" }
- cpu: [ppc64]
- os: [linux]
-
- esbuild-linux-riscv64@0.14.47:
- resolution:
- {
- integrity: sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==,
- }
- engines: { node: ">=12" }
- cpu: [riscv64]
- os: [linux]
-
- esbuild-linux-s390x@0.14.47:
- resolution:
- {
- integrity: sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==,
- }
- engines: { node: ">=12" }
- cpu: [s390x]
- os: [linux]
-
- esbuild-netbsd-64@0.14.47:
- resolution:
- {
- integrity: sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [netbsd]
-
- esbuild-openbsd-64@0.14.47:
- resolution:
- {
- integrity: sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [openbsd]
-
- esbuild-sunos-64@0.14.47:
- resolution:
- {
- integrity: sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [sunos]
-
- esbuild-windows-32@0.14.47:
- resolution:
- {
- integrity: sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==,
- }
- engines: { node: ">=12" }
- cpu: [ia32]
- os: [win32]
-
- esbuild-windows-64@0.14.47:
- resolution:
- {
- integrity: sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==,
- }
- engines: { node: ">=12" }
- cpu: [x64]
- os: [win32]
-
- esbuild-windows-arm64@0.14.47:
- resolution:
- {
- integrity: sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==,
- }
- engines: { node: ">=12" }
- cpu: [arm64]
- os: [win32]
-
- esbuild@0.14.47:
- resolution:
- {
- integrity: sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==,
- }
- engines: { node: ">=12" }
- hasBin: true
-
escalade@3.2.0:
resolution:
{
@@ -4463,6 +4041,13 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+ eslint-scope@5.1.1:
+ resolution:
+ {
+ integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==,
+ }
+ engines: { node: ">=8.0.0" }
+
eslint-scope@8.4.0:
resolution:
{
@@ -4526,6 +4111,13 @@ packages:
}
engines: { node: ">=4.0" }
+ estraverse@4.3.0:
+ resolution:
+ {
+ integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==,
+ }
+ engines: { node: ">=4.0" }
+
estraverse@5.3.0:
resolution:
{
@@ -4552,13 +4144,6 @@ packages:
}
engines: { node: ">=0.10.0" }
- etag@1.8.1:
- resolution:
- {
- integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==,
- }
- engines: { node: ">= 0.6" }
-
event-target-shim@6.0.2:
resolution:
{
@@ -4566,12 +4151,6 @@ packages:
}
engines: { node: ">=10.13.0" }
- events-intercept@2.0.0:
- resolution:
- {
- integrity: sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q==,
- }
-
events@3.3.0:
resolution:
{
@@ -4579,13 +4158,6 @@ packages:
}
engines: { node: ">=0.8.x" }
- execa@3.2.0:
- resolution:
- {
- integrity: sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw==,
- }
- engines: { node: ^8.12.0 || >=9.7.0 }
-
execa@5.1.1:
resolution:
{
@@ -4650,6 +4222,12 @@ packages:
integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==,
}
+ fast-uri@3.1.0:
+ resolution:
+ {
+ integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==,
+ }
+
fastq@1.19.1:
resolution:
{
@@ -4662,12 +4240,6 @@ packages:
integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==,
}
- fd-slicer@1.1.0:
- resolution:
- {
- integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==,
- }
-
fdir@6.4.6:
resolution:
{
@@ -4686,12 +4258,6 @@ packages:
}
engines: { node: ">=16.0.0" }
- file-uri-to-path@1.0.0:
- resolution:
- {
- integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==,
- }
-
fill-range@7.1.1:
resolution:
{
@@ -4777,47 +4343,12 @@ packages:
integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==,
}
- fs-extra@11.1.0:
- resolution:
- {
- integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==,
- }
- engines: { node: ">=14.14" }
-
- fs-extra@8.1.0:
- resolution:
- {
- integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==,
- }
- engines: { node: ">=6 <7 || >=8" }
-
- fs-minipass@1.2.7:
- resolution:
- {
- integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==,
- }
-
- fs-minipass@2.1.0:
- resolution:
- {
- integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==,
- }
- engines: { node: ">= 8" }
-
fs.realpath@1.0.0:
resolution:
{
integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==,
}
- fsevents@2.1.3:
- resolution:
- {
- integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==,
- }
- engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 }
- os: [darwin]
-
fsevents@2.3.3:
resolution:
{
@@ -4845,21 +4376,6 @@ packages:
integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==,
}
- gauge@3.0.2:
- resolution:
- {
- integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==,
- }
- engines: { node: ">=10" }
- deprecated: This package is no longer supported.
-
- generic-pool@3.4.2:
- resolution:
- {
- integrity: sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag==,
- }
- engines: { node: ">= 4" }
-
gensync@1.0.0-beta.2:
resolution:
{
@@ -4908,13 +4424,6 @@ packages:
}
engines: { node: ">= 0.4" }
- get-stream@5.2.0:
- resolution:
- {
- integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==,
- }
- engines: { node: ">=8" }
-
get-stream@6.0.1:
resolution:
{
@@ -4949,6 +4458,12 @@ packages:
}
engines: { node: ">=10.13.0" }
+ glob-to-regexp@0.4.1:
+ resolution:
+ {
+ integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==,
+ }
+
glob@10.3.10:
resolution:
{
@@ -5068,12 +4583,6 @@ packages:
}
engines: { node: ">= 0.4" }
- has-unicode@2.0.1:
- resolution:
- {
- integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==,
- }
-
hasown@2.0.2:
resolution:
{
@@ -5117,20 +4626,6 @@ packages:
integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==,
}
- http-errors@1.4.0:
- resolution:
- {
- integrity: sha512-oLjPqve1tuOl5aRhv8GK5eHpqP1C9fb+Ol+XTLjKfLltE44zdDbEdjPSbU7Ch5rSNsVFqZn97SrMmZLdu1/YMw==,
- }
- engines: { node: ">= 0.6" }
-
- http-errors@1.7.3:
- resolution:
- {
- integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==,
- }
- engines: { node: ">= 0.6" }
-
https-proxy-agent@5.0.1:
resolution:
{
@@ -5145,13 +4640,6 @@ packages:
}
engines: { node: ">= 14" }
- human-signals@1.1.1:
- resolution:
- {
- integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==,
- }
- engines: { node: ">=8.12.0" }
-
human-signals@2.1.0:
resolution:
{
@@ -5165,13 +4653,6 @@ packages:
integrity: sha512-IvLy8MzHTSJ0fDpSzrb8rcdnla6yROEmNBSxInEMyIFu2DQkbmpadTf6B4fHvnytN6iHL2gGwpe5/jHL3wMi+A==,
}
- iconv-lite@0.4.24:
- resolution:
- {
- integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==,
- }
- engines: { node: ">=0.10.0" }
-
ieee754@1.2.1:
resolution:
{
@@ -5246,12 +4727,6 @@ packages:
}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
- inherits@2.0.1:
- resolution:
- {
- integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==,
- }
-
inherits@2.0.4:
resolution:
{
@@ -5556,12 +5031,6 @@ packages:
}
engines: { node: ">= 0.4" }
- isarray@0.0.1:
- resolution:
- {
- integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==,
- }
-
isarray@2.0.5:
resolution:
{
@@ -5819,6 +5288,13 @@ packages:
}
engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
+ jest-worker@27.5.1:
+ resolution:
+ {
+ integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==,
+ }
+ engines: { node: ">= 10.13.0" }
+
jest-worker@29.7.0:
resolution:
{
@@ -5912,12 +5388,6 @@ packages:
integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==,
}
- json-schema-to-ts@1.6.4:
- resolution:
- {
- integrity: sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==,
- }
-
json-schema-traverse@0.4.1:
resolution:
{
@@ -5951,18 +5421,6 @@ packages:
engines: { node: ">=6" }
hasBin: true
- jsonfile@4.0.0:
- resolution:
- {
- integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==,
- }
-
- jsonfile@6.2.0:
- resolution:
- {
- integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==,
- }
-
jsx-ast-utils@3.3.5:
resolution:
{
@@ -6028,6 +5486,13 @@ packages:
integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==,
}
+ loader-runner@4.3.0:
+ resolution:
+ {
+ integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==,
+ }
+ engines: { node: ">=6.11.5" }
+
localforage@1.10.0:
resolution:
{
@@ -6119,13 +5584,6 @@ packages:
}
engines: { node: ">=12" }
- make-dir@3.1.0:
- resolution:
- {
- integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==,
- }
- engines: { node: ">=8" }
-
make-dir@4.0.0:
resolution:
{
@@ -6220,14 +5678,6 @@ packages:
}
engines: { node: ">= 8" }
- micro@9.3.5-canary.3:
- resolution:
- {
- integrity: sha512-viYIo9PefV+w9dvoIBh1gI44Mvx1BOk67B4BpC2QK77qdY0xZF0Q+vWLt/BII6cLkIc8rLmSIcJaB/OrXXKe1g==,
- }
- engines: { node: ">= 8.0.0" }
- hasBin: true
-
micromark-core-commonmark@2.0.3:
resolution:
{
@@ -6408,26 +5858,6 @@ packages:
integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==,
}
- minipass@2.9.0:
- resolution:
- {
- integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==,
- }
-
- minipass@3.3.6:
- resolution:
- {
- integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==,
- }
- engines: { node: ">=8" }
-
- minipass@5.0.0:
- resolution:
- {
- integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==,
- }
- engines: { node: ">=8" }
-
minipass@7.1.2:
resolution:
{
@@ -6435,19 +5865,6 @@ packages:
}
engines: { node: ">=16 || 14 >=14.17" }
- minizlib@1.3.3:
- resolution:
- {
- integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==,
- }
-
- minizlib@2.1.2:
- resolution:
- {
- integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==,
- }
- engines: { node: ">= 8" }
-
mitt@3.0.1:
resolution:
{
@@ -6461,27 +5878,6 @@ packages:
}
hasBin: true
- mkdirp@1.0.4:
- resolution:
- {
- integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==,
- }
- engines: { node: ">=10" }
- hasBin: true
-
- mri@1.2.0:
- resolution:
- {
- integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==,
- }
- engines: { node: ">=4" }
-
- ms@2.1.1:
- resolution:
- {
- integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==,
- }
-
ms@2.1.3:
resolution:
{
@@ -6581,30 +5977,6 @@ packages:
integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==,
}
- node-fetch@2.6.7:
- resolution:
- {
- integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==,
- }
- engines: { node: 4.x || >=6.0.0 }
- peerDependencies:
- encoding: ^0.1.0
- peerDependenciesMeta:
- encoding:
- optional: true
-
- node-fetch@2.6.9:
- resolution:
- {
- integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==,
- }
- engines: { node: 4.x || >=6.0.0 }
- peerDependencies:
- encoding: ^0.1.0
- peerDependenciesMeta:
- encoding:
- optional: true
-
node-fetch@2.7.0:
resolution:
{
@@ -6617,13 +5989,6 @@ packages:
encoding:
optional: true
- node-gyp-build@4.8.4:
- resolution:
- {
- integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==,
- }
- hasBin: true
-
node-int64@0.4.0:
resolution:
{
@@ -6636,14 +6001,6 @@ packages:
integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==,
}
- nopt@5.0.0:
- resolution:
- {
- integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==,
- }
- engines: { node: ">=6" }
- hasBin: true
-
normalize-path@3.0.0:
resolution:
{
@@ -6665,13 +6022,6 @@ packages:
}
engines: { node: ">=8" }
- npmlog@5.0.1:
- resolution:
- {
- integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==,
- }
- deprecated: This package is no longer supported.
-
nuqs@2.4.3:
resolution:
{
@@ -6776,12 +6126,6 @@ packages:
}
engines: { node: ^10.13.0 || >=12.0.0 }
- once@1.3.3:
- resolution:
- {
- integrity: sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==,
- }
-
once@1.4.0:
resolution:
{
@@ -6838,13 +6182,6 @@ packages:
}
engines: { node: ">= 0.8.0" }
- os-paths@4.4.0:
- resolution:
- {
- integrity: sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==,
- }
- engines: { node: ">= 6.0" }
-
own-keys@1.0.1:
resolution:
{
@@ -6852,13 +6189,6 @@ packages:
}
engines: { node: ">= 0.4" }
- p-finally@2.0.1:
- resolution:
- {
- integrity: sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==,
- }
- engines: { node: ">=8" }
-
p-limit@2.3.0:
resolution:
{
@@ -6927,19 +6257,6 @@ packages:
}
engines: { node: ">=18" }
- parse-ms@2.1.0:
- resolution:
- {
- integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==,
- }
- engines: { node: ">=6" }
-
- path-browserify@1.0.1:
- resolution:
- {
- integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==,
- }
-
path-exists@4.0.0:
resolution:
{
@@ -6961,13 +6278,6 @@ packages:
}
engines: { node: ">=8" }
- path-match@1.2.4:
- resolution:
- {
- integrity: sha512-UWlehEdqu36jmh4h5CWJ7tARp1OEVKGHKm6+dg9qMq5RKUTV5WJrGgaZ3dN2m7WFAXDbjlHzvJvL/IUpy84Ktw==,
- }
- deprecated: This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions
-
path-parse@1.0.7:
resolution:
{
@@ -6981,24 +6291,6 @@ packages:
}
engines: { node: ">=16 || 14 >=14.18" }
- path-to-regexp@1.9.0:
- resolution:
- {
- integrity: sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==,
- }
-
- path-to-regexp@6.1.0:
- resolution:
- {
- integrity: sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==,
- }
-
- path-to-regexp@6.2.1:
- resolution:
- {
- integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==,
- }
-
path-type@4.0.0:
resolution:
{
@@ -7006,24 +6298,12 @@ packages:
}
engines: { node: ">=8" }
- pend@1.2.0:
- resolution:
- {
- integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==,
- }
-
perfect-freehand@1.2.2:
resolution:
{
integrity: sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==,
}
- picocolors@1.0.0:
- resolution:
- {
- integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==,
- }
-
picocolors@1.1.1:
resolution:
{
@@ -7190,13 +6470,6 @@ packages:
}
engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 }
- pretty-ms@7.0.1:
- resolution:
- {
- integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==,
- }
- engines: { node: ">=10" }
-
progress@2.0.3:
resolution:
{
@@ -7204,12 +6477,6 @@ packages:
}
engines: { node: ">=0.4.0" }
- promisepipe@3.0.0:
- resolution:
- {
- integrity: sha512-V6TbZDJ/ZswevgkDNpGt/YqNCiZP9ASfgU+p83uJE6NrGtvSGoOcHLiDCqkMs2+yg7F5qHdLV8d0aS8O26G/KA==,
- }
-
prop-types@15.8.1:
resolution:
{
@@ -7240,12 +6507,6 @@ packages:
integrity: sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==,
}
- pump@3.0.3:
- resolution:
- {
- integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==,
- }
-
punycode@2.3.1:
resolution:
{
@@ -7277,13 +6538,6 @@ packages:
integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==,
}
- raw-body@2.4.1:
- resolution:
- {
- integrity: sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==,
- }
- engines: { node: ">= 0.8" }
-
react-dom@18.3.1:
resolution:
{
@@ -7407,13 +6661,6 @@ packages:
}
engines: { node: ">= 6" }
- readdirp@3.3.0:
- resolution:
- {
- integrity: sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==,
- }
- engines: { node: ">=8.10.0" }
-
readdirp@3.6.0:
resolution:
{
@@ -7571,18 +6818,10 @@ packages:
}
engines: { iojs: ">=1.0.0", node: ">=0.10.0" }
- rimraf@3.0.2:
+ rollup@2.78.0:
resolution:
{
- integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==,
- }
- deprecated: Rimraf versions prior to v4 are no longer supported
- hasBin: true
-
- rollup@2.79.2:
- resolution:
- {
- integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==,
+ integrity: sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==,
}
engines: { node: ">=10.0.0" }
hasBin: true
@@ -7634,12 +6873,6 @@ packages:
}
engines: { node: ">= 0.4" }
- safer-buffer@2.1.2:
- resolution:
- {
- integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==,
- }
-
sass@1.90.0:
resolution:
{
@@ -7654,6 +6887,13 @@ packages:
integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==,
}
+ schema-utils@4.3.2:
+ resolution:
+ {
+ integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==,
+ }
+ engines: { node: ">= 10.13.0" }
+
sdp-transform@2.15.0:
resolution:
{
@@ -7674,14 +6914,6 @@ packages:
}
hasBin: true
- semver@7.3.5:
- resolution:
- {
- integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==,
- }
- engines: { node: ">=10" }
- hasBin: true
-
semver@7.7.2:
resolution:
{
@@ -7690,10 +6922,10 @@ packages:
engines: { node: ">=10" }
hasBin: true
- set-blocking@2.0.0:
+ serialize-javascript@6.0.2:
resolution:
{
- integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==,
+ integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==,
}
set-function-length@1.2.2:
@@ -7717,12 +6949,6 @@ packages:
}
engines: { node: ">= 0.4" }
- setprototypeof@1.1.1:
- resolution:
- {
- integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==,
- }
-
shebang-command@2.0.0:
resolution:
{
@@ -7771,13 +6997,6 @@ packages:
integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==,
}
- signal-exit@4.0.2:
- resolution:
- {
- integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==,
- }
- engines: { node: ">=14" }
-
signal-exit@4.1.0:
resolution:
{
@@ -7825,6 +7044,12 @@ packages:
integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==,
}
+ source-map-support@0.5.21:
+ resolution:
+ {
+ integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==,
+ }
+
source-map@0.5.7:
resolution:
{
@@ -7883,19 +7108,6 @@ packages:
integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==,
}
- stat-mode@0.3.0:
- resolution:
- {
- integrity: sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==,
- }
-
- statuses@1.5.0:
- resolution:
- {
- integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==,
- }
- engines: { node: ">= 0.6" }
-
stop-iteration-iterator@1.1.0:
resolution:
{
@@ -7903,18 +7115,6 @@ packages:
}
engines: { node: ">= 0.4" }
- stream-to-array@2.3.0:
- resolution:
- {
- integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==,
- }
-
- stream-to-promise@2.2.0:
- resolution:
- {
- integrity: sha512-HAGUASw8NT0k8JvIVutB2Y/9iBk7gpgEyAudXwNJmZERdMITGdajOa4VJfD/kNiA3TppQpTP4J+CtcHwdzKBAw==,
- }
-
streamsearch@1.1.0:
resolution:
{
@@ -8130,19 +7330,39 @@ packages:
engines: { node: ">=14.0.0" }
hasBin: true
- tar@4.4.18:
+ tapable@2.2.3:
resolution:
{
- integrity: sha512-ZuOtqqmkV9RE1+4odd+MhBpibmCxNP6PJhH/h2OqNuotTX7/XHPZQJv2pKvWMplFH9SIZZhitehh6vBH6LO8Pg==,
+ integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==,
}
- engines: { node: ">=4.5" }
+ engines: { node: ">=6" }
- tar@6.2.1:
+ terser-webpack-plugin@5.3.14:
resolution:
{
- integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==,
+ integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==,
+ }
+ engines: { node: ">= 10.13.0" }
+ peerDependencies:
+ "@swc/core": "*"
+ esbuild: "*"
+ uglify-js: "*"
+ webpack: ^5.1.0
+ peerDependenciesMeta:
+ "@swc/core":
+ optional: true
+ esbuild:
+ optional: true
+ uglify-js:
+ optional: true
+
+ terser@5.44.0:
+ resolution:
+ {
+ integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==,
}
engines: { node: ">=10" }
+ hasBin: true
test-exclude@6.0.0:
resolution:
@@ -8164,13 +7384,6 @@ packages:
integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==,
}
- time-span@4.0.0:
- resolution:
- {
- integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==,
- }
- engines: { node: ">=10" }
-
tinyglobby@0.2.14:
resolution:
{
@@ -8191,26 +7404,12 @@ packages:
}
engines: { node: ">=8.0" }
- toidentifier@1.0.0:
- resolution:
- {
- integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==,
- }
- engines: { node: ">=0.6" }
-
tr46@0.0.3:
resolution:
{
integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==,
}
- tree-kill@1.2.2:
- resolution:
- {
- integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==,
- }
- hasBin: true
-
trim-lines@3.0.1:
resolution:
{
@@ -8268,12 +7467,6 @@ packages:
jest-util:
optional: true
- ts-morph@12.0.0:
- resolution:
- {
- integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==,
- }
-
ts-node@10.9.1:
resolution:
{
@@ -8291,12 +7484,6 @@ packages:
"@swc/wasm":
optional: true
- ts-toolbelt@6.15.5:
- resolution:
- {
- integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==,
- }
-
tsconfig-paths@3.15.0:
resolution:
{
@@ -8372,14 +7559,6 @@ packages:
}
engines: { node: ">= 0.4" }
- typescript@4.9.5:
- resolution:
- {
- integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==,
- }
- engines: { node: ">=4.2.0" }
- hasBin: true
-
typescript@5.9.2:
resolution:
{
@@ -8440,12 +7619,6 @@ packages:
integrity: sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==,
}
- uid-promise@1.0.0:
- resolution:
- {
- integrity: sha512-R8375j0qwXyIu/7R0tjdF06/sElHqbmdmWC9M2qQHpEVbvE4I5+38KJI7LUUmQMp7NVq4tKHiBMkT0NFM453Ig==,
- }
-
umap@1.0.2:
resolution:
{
@@ -8465,13 +7638,6 @@ packages:
integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==,
}
- undici@5.28.4:
- resolution:
- {
- integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==,
- }
- engines: { node: ">=14.0" }
-
unified@11.0.5:
resolution:
{
@@ -8508,27 +7674,6 @@ packages:
integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==,
}
- universalify@0.1.2:
- resolution:
- {
- integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==,
- }
- engines: { node: ">= 4.0.0" }
-
- universalify@2.0.1:
- resolution:
- {
- integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==,
- }
- engines: { node: ">= 10.0.0" }
-
- unpipe@1.0.0:
- resolution:
- {
- integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==,
- }
- engines: { node: ">= 0.8" }
-
unrs-resolver@1.11.1:
resolution:
{
@@ -8600,14 +7745,6 @@ packages:
integrity: sha512-Fykw5U4eZESbq739BeLvEBFRuJODfrlmjx5eJux7W817LjRaq4b7/i4t2zxQmhcX+fAj4nMfRdTzO4tmwLKn0w==,
}
- uuid@3.3.2:
- resolution:
- {
- integrity: sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==,
- }
- deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
- hasBin: true
-
uuid@8.3.2:
resolution:
{
@@ -8641,14 +7778,6 @@ packages:
}
engines: { node: ">=10.12.0" }
- vercel@37.14.0:
- resolution:
- {
- integrity: sha512-ZSEvhARyJBn4YnEVZULsvti8/OHd5txRCgJqEhNIyo/XXSvBJSvlCjA+SE1zraqn0rqyEOG3+56N3kh1Enk8Tg==,
- }
- engines: { node: ">= 16" }
- hasBin: true
-
vfile-message@4.0.3:
resolution:
{
@@ -8667,18 +7796,19 @@ packages:
integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==,
}
+ watchpack@2.4.4:
+ resolution:
+ {
+ integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==,
+ }
+ engines: { node: ">=10.13.0" }
+
wavesurfer.js@7.10.1:
resolution:
{
integrity: sha512-tF1ptFCAi8SAqKbM1e7705zouLC3z4ulXCg15kSP5dQ7VDV30Q3x/xFRcuVIYTT5+jB/PdkhiBRCfsMshZG1Ug==,
}
- web-vitals@0.2.4:
- resolution:
- {
- integrity: sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==,
- }
-
webidl-conversions@3.0.1:
resolution:
{
@@ -8692,6 +7822,19 @@ packages:
}
engines: { node: ">=10.13.0" }
+ webpack@5.101.3:
+ resolution:
+ {
+ integrity: sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==,
+ }
+ engines: { node: ">=10.13.0" }
+ hasBin: true
+ peerDependencies:
+ webpack-cli: "*"
+ peerDependenciesMeta:
+ webpack-cli:
+ optional: true
+
webrtc-adapter@9.0.3:
resolution:
{
@@ -8741,12 +7884,6 @@ packages:
engines: { node: ">= 8" }
hasBin: true
- wide-align@1.1.5:
- resolution:
- {
- integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==,
- }
-
word-wrap@1.2.5:
resolution:
{
@@ -8802,20 +7939,6 @@ packages:
utf-8-validate:
optional: true
- xdg-app-paths@5.1.0:
- resolution:
- {
- integrity: sha512-RAQ3WkPf4KTU1A8RtFx3gWywzVKe00tfOPFfl2NDGqbIFENQO4kqAJp7mhQjNj/33W5x5hiWWUdyfPq/5SU3QA==,
- }
- engines: { node: ">=6" }
-
- xdg-portable@7.3.0:
- resolution:
- {
- integrity: sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw==,
- }
- engines: { node: ">= 6.0" }
-
xmlhttprequest-ssl@2.0.0:
resolution:
{
@@ -8877,26 +8000,6 @@ packages:
}
engines: { node: ">=12" }
- yauzl-clone@1.0.4:
- resolution:
- {
- integrity: sha512-igM2RRCf3k8TvZoxR2oguuw4z1xasOnA31joCqHIyLkeWrvAc2Jgay5ISQ2ZplinkoGaJ6orCz56Ey456c5ESA==,
- }
- engines: { node: ">=6" }
-
- yauzl-promise@2.1.3:
- resolution:
- {
- integrity: sha512-A1pf6fzh6eYkK0L4Qp7g9jzJSDrM6nN0bOn5T0IbY4Yo3w+YkWlHFkJP7mzknMXjqusHFHlKsK2N+4OLsK2MRA==,
- }
- engines: { node: ">=6" }
-
- yauzl@2.10.0:
- resolution:
- {
- integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==,
- }
-
yn@3.1.1:
resolution:
{
@@ -9227,18 +8330,7 @@ snapshots:
"@cspotcode/source-map-support@0.8.1":
dependencies:
"@jridgewell/trace-mapping": 0.3.9
-
- "@edge-runtime/format@2.2.1": {}
-
- "@edge-runtime/node-utils@2.3.0": {}
-
- "@edge-runtime/ponyfill@2.4.2": {}
-
- "@edge-runtime/primitives@4.1.0": {}
-
- "@edge-runtime/vm@3.2.0":
- dependencies:
- "@edge-runtime/primitives": 4.1.0
+ optional: true
"@emnapi/core@1.4.5":
dependencies:
@@ -9368,8 +8460,6 @@ snapshots:
"@eslint/core": 0.15.2
levn: 0.4.1
- "@fastify/busboy@2.1.1": {}
-
"@floating-ui/core@1.7.3":
dependencies:
"@floating-ui/utils": 0.2.10
@@ -9459,7 +8549,7 @@ snapshots:
jest-util: 30.0.5
slash: 3.0.0
- "@jest/core@30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))":
+ "@jest/core@30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))":
dependencies:
"@jest/console": 30.1.2
"@jest/pattern": 30.0.1
@@ -9474,7 +8564,7 @@ snapshots:
exit-x: 0.2.2
graceful-fs: 4.2.11
jest-changed-files: 30.0.5
- jest-config: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest-config: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
jest-haste-map: 30.1.0
jest-message-util: 30.1.0
jest-regex-util: 30.0.1
@@ -9649,6 +8739,12 @@ snapshots:
"@jridgewell/resolve-uri@3.1.2": {}
+ "@jridgewell/source-map@0.3.11":
+ dependencies:
+ "@jridgewell/gen-mapping": 0.3.13
+ "@jridgewell/trace-mapping": 0.3.30
+ optional: true
+
"@jridgewell/sourcemap-codec@1.5.5": {}
"@jridgewell/trace-mapping@0.3.30":
@@ -9660,21 +8756,7 @@ snapshots:
dependencies:
"@jridgewell/resolve-uri": 3.1.2
"@jridgewell/sourcemap-codec": 1.5.5
-
- "@mapbox/node-pre-gyp@1.0.11":
- dependencies:
- detect-libc: 2.0.4
- https-proxy-agent: 5.0.1
- make-dir: 3.1.0
- node-fetch: 2.7.0
- nopt: 5.0.0
- npmlog: 5.0.1
- rimraf: 3.0.2
- semver: 7.7.2
- tar: 6.2.1
- transitivePeerDependencies:
- - encoding
- - supports-color
+ optional: true
"@napi-rs/wasm-runtime@0.2.12":
dependencies:
@@ -9730,6 +8812,9 @@ snapshots:
"@nolyfill/is-core-module@1.0.39": {}
+ "@opentelemetry/api@1.9.0":
+ optional: true
+
"@pandacss/is-valid-prop@0.54.0": {}
"@panva/hkdf@1.2.1": {}
@@ -10013,63 +9098,42 @@ snapshots:
optionalDependencies:
react: 18.3.1
- "@rollup/plugin-commonjs@24.0.0(rollup@2.79.2)":
+ "@rollup/plugin-commonjs@24.0.0(rollup@2.78.0)":
dependencies:
- "@rollup/pluginutils": 5.2.0(rollup@2.79.2)
+ "@rollup/pluginutils": 5.2.0(rollup@2.78.0)
commondir: 1.0.1
estree-walker: 2.0.2
glob: 8.1.0
is-reference: 1.2.1
magic-string: 0.27.0
optionalDependencies:
- rollup: 2.79.2
+ rollup: 2.78.0
- "@rollup/pluginutils@4.2.1":
- dependencies:
- estree-walker: 2.0.2
- picomatch: 2.3.1
-
- "@rollup/pluginutils@5.2.0(rollup@2.79.2)":
+ "@rollup/pluginutils@5.2.0(rollup@2.78.0)":
dependencies:
"@types/estree": 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.3
optionalDependencies:
- rollup: 2.79.2
+ rollup: 2.78.0
"@rtsao/scc@1.1.0": {}
"@rushstack/eslint-patch@1.12.0": {}
- "@sentry-internal/feedback@7.120.4":
+ "@sentry-internal/tracing@7.77.0":
dependencies:
- "@sentry/core": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry/core": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
- "@sentry-internal/replay-canvas@7.120.4":
+ "@sentry/browser@7.77.0":
dependencies:
- "@sentry/core": 7.120.4
- "@sentry/replay": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
-
- "@sentry-internal/tracing@7.120.4":
- dependencies:
- "@sentry/core": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
-
- "@sentry/browser@7.120.4":
- dependencies:
- "@sentry-internal/feedback": 7.120.4
- "@sentry-internal/replay-canvas": 7.120.4
- "@sentry-internal/tracing": 7.120.4
- "@sentry/core": 7.120.4
- "@sentry/integrations": 7.120.4
- "@sentry/replay": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry-internal/tracing": 7.77.0
+ "@sentry/core": 7.77.0
+ "@sentry/replay": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
"@sentry/cli@1.77.3":
dependencies:
@@ -10083,78 +9147,79 @@ snapshots:
- encoding
- supports-color
- "@sentry/core@7.120.4":
+ "@sentry/core@7.77.0":
dependencies:
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
- "@sentry/integrations@7.120.4":
+ "@sentry/integrations@7.77.0":
dependencies:
- "@sentry/core": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry/core": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
localforage: 1.10.0
- "@sentry/nextjs@7.120.4(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)":
+ "@sentry/nextjs@7.77.0(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)":
dependencies:
- "@rollup/plugin-commonjs": 24.0.0(rollup@2.79.2)
- "@sentry/core": 7.120.4
- "@sentry/integrations": 7.120.4
- "@sentry/node": 7.120.4
- "@sentry/react": 7.120.4(react@18.3.1)
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
- "@sentry/vercel-edge": 7.120.4
- "@sentry/webpack-plugin": 1.21.0
+ "@rollup/plugin-commonjs": 24.0.0(rollup@2.78.0)
+ "@sentry/core": 7.77.0
+ "@sentry/integrations": 7.77.0
+ "@sentry/node": 7.77.0
+ "@sentry/react": 7.77.0(react@18.3.1)
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
+ "@sentry/vercel-edge": 7.77.0
+ "@sentry/webpack-plugin": 1.20.0
chalk: 3.0.0
- next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
react: 18.3.1
resolve: 1.22.8
- rollup: 2.79.2
+ rollup: 2.78.0
stacktrace-parser: 0.1.11
+ optionalDependencies:
+ webpack: 5.101.3
transitivePeerDependencies:
- encoding
- supports-color
- "@sentry/node@7.120.4":
+ "@sentry/node@7.77.0":
dependencies:
- "@sentry-internal/tracing": 7.120.4
- "@sentry/core": 7.120.4
- "@sentry/integrations": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry-internal/tracing": 7.77.0
+ "@sentry/core": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
+ https-proxy-agent: 5.0.1
+ transitivePeerDependencies:
+ - supports-color
- "@sentry/react@7.120.4(react@18.3.1)":
+ "@sentry/react@7.77.0(react@18.3.1)":
dependencies:
- "@sentry/browser": 7.120.4
- "@sentry/core": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry/browser": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
hoist-non-react-statics: 3.3.2
react: 18.3.1
- "@sentry/replay@7.120.4":
+ "@sentry/replay@7.77.0":
dependencies:
- "@sentry-internal/tracing": 7.120.4
- "@sentry/core": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry-internal/tracing": 7.77.0
+ "@sentry/core": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
- "@sentry/types@7.120.4": {}
+ "@sentry/types@7.77.0": {}
- "@sentry/utils@7.120.4":
+ "@sentry/utils@7.77.0":
dependencies:
- "@sentry/types": 7.120.4
+ "@sentry/types": 7.77.0
- "@sentry/vercel-edge@7.120.4":
+ "@sentry/vercel-edge@7.77.0":
dependencies:
- "@sentry-internal/tracing": 7.120.4
- "@sentry/core": 7.120.4
- "@sentry/integrations": 7.120.4
- "@sentry/types": 7.120.4
- "@sentry/utils": 7.120.4
+ "@sentry/core": 7.77.0
+ "@sentry/types": 7.77.0
+ "@sentry/utils": 7.77.0
- "@sentry/webpack-plugin@1.21.0":
+ "@sentry/webpack-plugin@1.20.0":
dependencies:
"@sentry/cli": 1.77.3
webpack-sources: 3.3.3
@@ -10162,8 +9227,6 @@ snapshots:
- encoding
- supports-color
- "@sinclair/typebox@0.25.24": {}
-
"@sinclair/typebox@0.27.8": {}
"@sinclair/typebox@0.34.41": {}
@@ -10200,22 +9263,17 @@ snapshots:
"@tanstack/query-core": 5.85.9
react: 18.3.1
- "@tootallnate/once@2.0.0": {}
+ "@tsconfig/node10@1.0.11":
+ optional: true
- "@ts-morph/common@0.11.1":
- dependencies:
- fast-glob: 3.3.3
- minimatch: 3.1.2
- mkdirp: 1.0.4
- path-browserify: 1.0.1
+ "@tsconfig/node12@1.0.11":
+ optional: true
- "@tsconfig/node10@1.0.11": {}
+ "@tsconfig/node14@1.0.3":
+ optional: true
- "@tsconfig/node12@1.0.11": {}
-
- "@tsconfig/node14@1.0.3": {}
-
- "@tsconfig/node16@1.0.4": {}
+ "@tsconfig/node16@1.0.4":
+ optional: true
"@tybys/wasm-util@0.10.0":
dependencies:
@@ -10247,6 +9305,18 @@ snapshots:
dependencies:
"@types/ms": 2.1.0
+ "@types/eslint-scope@3.7.7":
+ dependencies:
+ "@types/eslint": 9.6.1
+ "@types/estree": 1.0.8
+ optional: true
+
+ "@types/eslint@9.6.1":
+ dependencies:
+ "@types/estree": 1.0.8
+ "@types/json-schema": 7.0.15
+ optional: true
+
"@types/estree-jsx@1.0.5":
dependencies:
"@types/estree": 1.0.8
@@ -10295,8 +9365,6 @@ snapshots:
"@types/node": 24.2.1
form-data: 4.0.4
- "@types/node@16.18.11": {}
-
"@types/node@24.2.1":
dependencies:
undici-types: 7.10.0
@@ -10493,158 +9561,96 @@ snapshots:
"@unrs/resolver-binding-win32-x64-msvc@1.11.1":
optional: true
- "@vercel/build-utils@8.4.12": {}
-
- "@vercel/edge-config-fs@0.1.0": {}
-
- "@vercel/edge-config@0.4.1":
+ "@webassemblyjs/ast@1.14.1":
dependencies:
- "@vercel/edge-config-fs": 0.1.0
+ "@webassemblyjs/helper-numbers": 1.13.2
+ "@webassemblyjs/helper-wasm-bytecode": 1.13.2
+ optional: true
- "@vercel/error-utils@2.0.2": {}
+ "@webassemblyjs/floating-point-hex-parser@1.13.2":
+ optional: true
- "@vercel/fun@1.1.0":
+ "@webassemblyjs/helper-api-error@1.13.2":
+ optional: true
+
+ "@webassemblyjs/helper-buffer@1.14.1":
+ optional: true
+
+ "@webassemblyjs/helper-numbers@1.13.2":
dependencies:
- "@tootallnate/once": 2.0.0
- async-listen: 1.2.0
- debug: 4.1.1
- execa: 3.2.0
- fs-extra: 8.1.0
- generic-pool: 3.4.2
- micro: 9.3.5-canary.3
- ms: 2.1.1
- node-fetch: 2.6.7
- path-match: 1.2.4
- promisepipe: 3.0.0
- semver: 7.3.5
- stat-mode: 0.3.0
- stream-to-promise: 2.2.0
- tar: 4.4.18
- tree-kill: 1.2.2
- uid-promise: 1.0.0
- uuid: 3.3.2
- xdg-app-paths: 5.1.0
- yauzl-promise: 2.1.3
- transitivePeerDependencies:
- - encoding
- - supports-color
+ "@webassemblyjs/floating-point-hex-parser": 1.13.2
+ "@webassemblyjs/helper-api-error": 1.13.2
+ "@xtuc/long": 4.2.2
+ optional: true
- "@vercel/gatsby-plugin-vercel-analytics@1.0.11":
+ "@webassemblyjs/helper-wasm-bytecode@1.13.2":
+ optional: true
+
+ "@webassemblyjs/helper-wasm-section@1.14.1":
dependencies:
- web-vitals: 0.2.4
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/helper-buffer": 1.14.1
+ "@webassemblyjs/helper-wasm-bytecode": 1.13.2
+ "@webassemblyjs/wasm-gen": 1.14.1
+ optional: true
- "@vercel/gatsby-plugin-vercel-builder@2.0.56":
+ "@webassemblyjs/ieee754@1.13.2":
dependencies:
- "@sinclair/typebox": 0.25.24
- "@vercel/build-utils": 8.4.12
- "@vercel/routing-utils": 3.1.0
- esbuild: 0.14.47
- etag: 1.8.1
- fs-extra: 11.1.0
+ "@xtuc/ieee754": 1.2.0
+ optional: true
- "@vercel/go@3.2.0": {}
-
- "@vercel/hydrogen@1.0.9":
+ "@webassemblyjs/leb128@1.13.2":
dependencies:
- "@vercel/static-config": 3.0.0
- ts-morph: 12.0.0
+ "@xtuc/long": 4.2.2
+ optional: true
- "@vercel/next@4.3.18":
+ "@webassemblyjs/utf8@1.13.2":
+ optional: true
+
+ "@webassemblyjs/wasm-edit@1.14.1":
dependencies:
- "@vercel/nft": 0.27.3
- transitivePeerDependencies:
- - encoding
- - supports-color
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/helper-buffer": 1.14.1
+ "@webassemblyjs/helper-wasm-bytecode": 1.13.2
+ "@webassemblyjs/helper-wasm-section": 1.14.1
+ "@webassemblyjs/wasm-gen": 1.14.1
+ "@webassemblyjs/wasm-opt": 1.14.1
+ "@webassemblyjs/wasm-parser": 1.14.1
+ "@webassemblyjs/wast-printer": 1.14.1
+ optional: true
- "@vercel/nft@0.27.3":
+ "@webassemblyjs/wasm-gen@1.14.1":
dependencies:
- "@mapbox/node-pre-gyp": 1.0.11
- "@rollup/pluginutils": 4.2.1
- acorn: 8.15.0
- acorn-import-attributes: 1.9.5(acorn@8.15.0)
- async-sema: 3.1.1
- bindings: 1.5.0
- estree-walker: 2.0.2
- glob: 7.2.3
- graceful-fs: 4.2.11
- micromatch: 4.0.8
- node-gyp-build: 4.8.4
- resolve-from: 5.0.0
- transitivePeerDependencies:
- - encoding
- - supports-color
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/helper-wasm-bytecode": 1.13.2
+ "@webassemblyjs/ieee754": 1.13.2
+ "@webassemblyjs/leb128": 1.13.2
+ "@webassemblyjs/utf8": 1.13.2
+ optional: true
- "@vercel/node@3.2.24":
+ "@webassemblyjs/wasm-opt@1.14.1":
dependencies:
- "@edge-runtime/node-utils": 2.3.0
- "@edge-runtime/primitives": 4.1.0
- "@edge-runtime/vm": 3.2.0
- "@types/node": 16.18.11
- "@vercel/build-utils": 8.4.12
- "@vercel/error-utils": 2.0.2
- "@vercel/nft": 0.27.3
- "@vercel/static-config": 3.0.0
- async-listen: 3.0.0
- cjs-module-lexer: 1.2.3
- edge-runtime: 2.5.9
- es-module-lexer: 1.4.1
- esbuild: 0.14.47
- etag: 1.8.1
- node-fetch: 2.6.9
- path-to-regexp: 6.2.1
- ts-morph: 12.0.0
- ts-node: 10.9.1(@types/node@16.18.11)(typescript@4.9.5)
- typescript: 4.9.5
- undici: 5.28.4
- transitivePeerDependencies:
- - "@swc/core"
- - "@swc/wasm"
- - encoding
- - supports-color
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/helper-buffer": 1.14.1
+ "@webassemblyjs/wasm-gen": 1.14.1
+ "@webassemblyjs/wasm-parser": 1.14.1
+ optional: true
- "@vercel/python@4.3.1": {}
-
- "@vercel/redwood@2.1.8":
+ "@webassemblyjs/wasm-parser@1.14.1":
dependencies:
- "@vercel/nft": 0.27.3
- "@vercel/routing-utils": 3.1.0
- "@vercel/static-config": 3.0.0
- semver: 6.3.1
- ts-morph: 12.0.0
- transitivePeerDependencies:
- - encoding
- - supports-color
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/helper-api-error": 1.13.2
+ "@webassemblyjs/helper-wasm-bytecode": 1.13.2
+ "@webassemblyjs/ieee754": 1.13.2
+ "@webassemblyjs/leb128": 1.13.2
+ "@webassemblyjs/utf8": 1.13.2
+ optional: true
- "@vercel/remix-builder@2.2.13":
+ "@webassemblyjs/wast-printer@1.14.1":
dependencies:
- "@vercel/error-utils": 2.0.2
- "@vercel/nft": 0.27.3
- "@vercel/static-config": 3.0.0
- ts-morph: 12.0.0
- transitivePeerDependencies:
- - encoding
- - supports-color
-
- "@vercel/routing-utils@3.1.0":
- dependencies:
- path-to-regexp: 6.1.0
- optionalDependencies:
- ajv: 6.12.6
-
- "@vercel/ruby@2.1.0": {}
-
- "@vercel/static-build@2.5.34":
- dependencies:
- "@vercel/gatsby-plugin-vercel-analytics": 1.0.11
- "@vercel/gatsby-plugin-vercel-builder": 2.0.56
- "@vercel/static-config": 3.0.0
- ts-morph: 12.0.0
-
- "@vercel/static-config@3.0.0":
- dependencies:
- ajv: 8.6.3
- json-schema-to-ts: 1.6.4
- ts-morph: 12.0.0
+ "@webassemblyjs/ast": 1.14.1
+ "@xtuc/long": 4.2.2
+ optional: true
"@whereby.com/browser-sdk@3.13.1(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
@@ -10703,6 +9709,12 @@ snapshots:
- supports-color
- utf-8-validate
+ "@xtuc/ieee754@1.2.0":
+ optional: true
+
+ "@xtuc/long@4.2.2":
+ optional: true
+
"@zag-js/accordion@1.21.0":
dependencies:
"@zag-js/anatomy": 1.21.0
@@ -11204,11 +10216,10 @@ snapshots:
"@zag-js/utils@1.21.0": {}
- abbrev@1.1.1: {}
-
- acorn-import-attributes@1.9.5(acorn@8.15.0):
+ acorn-import-phases@1.0.4(acorn@8.15.0):
dependencies:
acorn: 8.15.0
+ optional: true
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
@@ -11217,6 +10228,7 @@ snapshots:
acorn-walk@8.3.4:
dependencies:
acorn: 8.15.0
+ optional: true
acorn@8.15.0: {}
@@ -11228,6 +10240,17 @@ snapshots:
agent-base@7.1.4: {}
+ ajv-formats@2.1.1(ajv@8.17.1):
+ optionalDependencies:
+ ajv: 8.17.1
+ optional: true
+
+ ajv-keywords@5.1.0(ajv@8.17.1):
+ dependencies:
+ ajv: 8.17.1
+ fast-deep-equal: 3.1.3
+ optional: true
+
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -11235,12 +10258,13 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
- ajv@8.6.3:
+ ajv@8.17.1:
dependencies:
fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
- uri-js: 4.4.1
+ optional: true
ansi-colors@4.1.3: {}
@@ -11267,16 +10291,8 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
- aproba@2.1.0: {}
-
- are-we-there-yet@2.0.0:
- dependencies:
- delegates: 1.0.0
- readable-stream: 3.6.2
-
- arg@4.1.0: {}
-
- arg@4.1.3: {}
+ arg@4.1.3:
+ optional: true
arg@5.0.2: {}
@@ -11363,14 +10379,6 @@ snapshots:
async-function@1.0.0: {}
- async-listen@1.2.0: {}
-
- async-listen@3.0.0: {}
-
- async-listen@3.0.1: {}
-
- async-sema@3.1.1: {}
-
asynckit@0.4.0: {}
augmentor@2.2.0:
@@ -11478,10 +10486,6 @@ snapshots:
binary-extensions@2.3.0: {}
- bindings@1.5.0:
- dependencies:
- file-uri-to-path: 1.0.0
-
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
@@ -11512,8 +10516,6 @@ snapshots:
btoa@1.2.1: {}
- buffer-crc32@0.2.13: {}
-
buffer-from@1.1.2: {}
buffer@6.0.3:
@@ -11525,8 +10527,6 @@ snapshots:
dependencies:
streamsearch: 1.1.0
- bytes@3.1.0: {}
-
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -11582,18 +10582,6 @@ snapshots:
dependencies:
ip-range-check: 0.0.2
- chokidar@3.3.1:
- dependencies:
- anymatch: 3.1.3
- braces: 3.0.3
- glob-parent: 5.1.2
- is-binary-path: 2.1.0
- is-glob: 4.0.3
- normalize-path: 3.0.0
- readdirp: 3.3.0
- optionalDependencies:
- fsevents: 2.1.3
-
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -11610,16 +10598,13 @@ snapshots:
dependencies:
readdirp: 4.1.2
- chownr@1.1.4: {}
-
- chownr@2.0.0: {}
+ chrome-trace-event@1.0.4:
+ optional: true
ci-info@3.9.0: {}
ci-info@4.3.0: {}
- cjs-module-lexer@1.2.3: {}
-
cjs-module-lexer@2.1.0: {}
classnames@2.5.1: {}
@@ -11638,8 +10623,6 @@ snapshots:
co@4.6.0: {}
- code-block-writer@10.1.1: {}
-
collect-v8-coverage@1.0.2: {}
color-convert@2.0.1:
@@ -11648,8 +10631,6 @@ snapshots:
color-name@1.1.4: {}
- color-support@1.1.3: {}
-
colorette@1.4.0: {}
combined-stream@1.0.8:
@@ -11658,18 +10639,15 @@ snapshots:
comma-separated-tokens@2.0.3: {}
+ commander@2.20.3:
+ optional: true
+
commander@4.1.1: {}
commondir@1.0.1: {}
concat-map@0.0.1: {}
- console-control-strings@1.1.0: {}
-
- content-type@1.0.4: {}
-
- convert-hrtime@3.0.0: {}
-
convert-source-map@1.9.0: {}
convert-source-map@2.0.0: {}
@@ -11684,7 +10662,8 @@ snapshots:
path-type: 4.0.0
yaml: 1.10.2
- create-require@1.1.1: {}
+ create-require@1.1.1:
+ optional: true
cross-spawn@7.0.6:
dependencies:
@@ -11720,10 +10699,6 @@ snapshots:
dependencies:
ms: 2.1.3
- debug@4.1.1:
- dependencies:
- ms: 2.1.1
-
debug@4.3.7:
dependencies:
ms: 2.1.3
@@ -11766,12 +10741,8 @@ snapshots:
delayed-stream@1.0.0: {}
- delegates@1.0.0: {}
-
denque@2.1.0: {}
- depd@1.1.2: {}
-
dequal@2.0.3: {}
detect-europe-js@0.1.2: {}
@@ -11779,8 +10750,6 @@ snapshots:
detect-libc@1.0.3:
optional: true
- detect-libc@2.0.4: {}
-
detect-newline@3.1.0: {}
detect-node-es@1.1.0: {}
@@ -11791,7 +10760,8 @@ snapshots:
didyoumean@1.2.2: {}
- diff@4.0.2: {}
+ diff@4.0.2:
+ optional: true
dlv@1.1.3: {}
@@ -11823,18 +10793,6 @@ snapshots:
eastasianwidth@0.2.0: {}
- edge-runtime@2.5.9:
- dependencies:
- "@edge-runtime/format": 2.2.1
- "@edge-runtime/ponyfill": 2.4.2
- "@edge-runtime/vm": 3.2.0
- async-listen: 3.0.1
- mri: 1.2.0
- picocolors: 1.0.0
- pretty-ms: 7.0.1
- signal-exit: 4.0.2
- time-span: 4.0.0
-
electron-to-chromium@1.5.200: {}
emittery@0.13.1: {}
@@ -11843,14 +10801,6 @@ snapshots:
emoji-regex@9.2.2: {}
- end-of-stream@1.1.0:
- dependencies:
- once: 1.3.3
-
- end-of-stream@1.4.5:
- dependencies:
- once: 1.4.0
-
engine.io-client@6.5.4:
dependencies:
"@socket.io/component-emitter": 3.1.2
@@ -11865,6 +10815,12 @@ snapshots:
engine.io-parser@5.2.3: {}
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.3
+ optional: true
+
err-code@3.0.1: {}
error-ex@1.3.2:
@@ -11951,7 +10907,8 @@ snapshots:
iterator.prototype: 1.1.5
safe-array-concat: 1.1.3
- es-module-lexer@1.4.1: {}
+ es-module-lexer@1.7.0:
+ optional: true
es-object-atoms@1.1.1:
dependencies:
@@ -11974,89 +10931,6 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
- esbuild-android-64@0.14.47:
- optional: true
-
- esbuild-android-arm64@0.14.47:
- optional: true
-
- esbuild-darwin-64@0.14.47:
- optional: true
-
- esbuild-darwin-arm64@0.14.47:
- optional: true
-
- esbuild-freebsd-64@0.14.47:
- optional: true
-
- esbuild-freebsd-arm64@0.14.47:
- optional: true
-
- esbuild-linux-32@0.14.47:
- optional: true
-
- esbuild-linux-64@0.14.47:
- optional: true
-
- esbuild-linux-arm64@0.14.47:
- optional: true
-
- esbuild-linux-arm@0.14.47:
- optional: true
-
- esbuild-linux-mips64le@0.14.47:
- optional: true
-
- esbuild-linux-ppc64le@0.14.47:
- optional: true
-
- esbuild-linux-riscv64@0.14.47:
- optional: true
-
- esbuild-linux-s390x@0.14.47:
- optional: true
-
- esbuild-netbsd-64@0.14.47:
- optional: true
-
- esbuild-openbsd-64@0.14.47:
- optional: true
-
- esbuild-sunos-64@0.14.47:
- optional: true
-
- esbuild-windows-32@0.14.47:
- optional: true
-
- esbuild-windows-64@0.14.47:
- optional: true
-
- esbuild-windows-arm64@0.14.47:
- optional: true
-
- esbuild@0.14.47:
- optionalDependencies:
- esbuild-android-64: 0.14.47
- esbuild-android-arm64: 0.14.47
- esbuild-darwin-64: 0.14.47
- esbuild-darwin-arm64: 0.14.47
- esbuild-freebsd-64: 0.14.47
- esbuild-freebsd-arm64: 0.14.47
- esbuild-linux-32: 0.14.47
- esbuild-linux-64: 0.14.47
- esbuild-linux-arm: 0.14.47
- esbuild-linux-arm64: 0.14.47
- esbuild-linux-mips64le: 0.14.47
- esbuild-linux-ppc64le: 0.14.47
- esbuild-linux-riscv64: 0.14.47
- esbuild-linux-s390x: 0.14.47
- esbuild-netbsd-64: 0.14.47
- esbuild-openbsd-64: 0.14.47
- esbuild-sunos-64: 0.14.47
- esbuild-windows-32: 0.14.47
- esbuild-windows-64: 0.14.47
- esbuild-windows-arm64: 0.14.47
-
escalade@3.2.0: {}
escape-string-regexp@2.0.0: {}
@@ -12191,6 +11065,12 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
+ eslint-scope@5.1.1:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 4.3.0
+ optional: true
+
eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
@@ -12258,6 +11138,9 @@ snapshots:
dependencies:
estraverse: 5.3.0
+ estraverse@4.3.0:
+ optional: true
+
estraverse@5.3.0: {}
estree-util-is-identifier-name@3.0.0: {}
@@ -12266,27 +11149,10 @@ snapshots:
esutils@2.0.3: {}
- etag@1.8.1: {}
-
event-target-shim@6.0.2: {}
- events-intercept@2.0.0: {}
-
events@3.3.0: {}
- execa@3.2.0:
- dependencies:
- cross-spawn: 7.0.6
- get-stream: 5.2.0
- human-signals: 1.1.1
- is-stream: 2.0.1
- merge-stream: 2.0.0
- npm-run-path: 4.0.1
- onetime: 5.1.2
- p-finally: 2.0.1
- signal-exit: 3.0.7
- strip-final-newline: 2.0.0
-
execa@5.1.1:
dependencies:
cross-spawn: 7.0.6
@@ -12333,6 +11199,9 @@ snapshots:
fast-safe-stringify@2.1.1: {}
+ fast-uri@3.1.0:
+ optional: true
+
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -12341,10 +11210,6 @@ snapshots:
dependencies:
bser: 2.1.1
- fd-slicer@1.1.0:
- dependencies:
- pend: 1.2.0
-
fdir@6.4.6(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
@@ -12353,8 +11218,6 @@ snapshots:
dependencies:
flat-cache: 4.0.1
- file-uri-to-path@1.0.0: {}
-
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -12401,31 +11264,8 @@ snapshots:
fraction.js@4.3.7: {}
- fs-extra@11.1.0:
- dependencies:
- graceful-fs: 4.2.11
- jsonfile: 6.2.0
- universalify: 2.0.1
-
- fs-extra@8.1.0:
- dependencies:
- graceful-fs: 4.2.11
- jsonfile: 4.0.0
- universalify: 0.1.2
-
- fs-minipass@1.2.7:
- dependencies:
- minipass: 2.9.0
-
- fs-minipass@2.1.0:
- dependencies:
- minipass: 3.3.6
-
fs.realpath@1.0.0: {}
- fsevents@2.1.3:
- optional: true
-
fsevents@2.3.3:
optional: true
@@ -12442,20 +11282,6 @@ snapshots:
functions-have-names@1.2.3: {}
- gauge@3.0.2:
- dependencies:
- aproba: 2.1.0
- color-support: 1.1.3
- console-control-strings: 1.1.0
- has-unicode: 2.0.1
- object-assign: 4.1.1
- signal-exit: 3.0.7
- string-width: 4.2.3
- strip-ansi: 6.0.1
- wide-align: 1.1.5
-
- generic-pool@3.4.2: {}
-
gensync@1.0.0-beta.2: {}
get-browser-rtc@1.1.0: {}
@@ -12484,10 +11310,6 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
- get-stream@5.2.0:
- dependencies:
- pump: 3.0.3
-
get-stream@6.0.1: {}
get-symbol-description@1.1.0:
@@ -12508,6 +11330,9 @@ snapshots:
dependencies:
is-glob: 4.0.3
+ glob-to-regexp@0.4.1:
+ optional: true
+
glob@10.3.10:
dependencies:
foreground-child: 3.3.1
@@ -12588,8 +11413,6 @@ snapshots:
dependencies:
has-symbols: 1.1.0
- has-unicode@2.0.1: {}
-
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
@@ -12635,19 +11458,6 @@ snapshots:
html-url-attributes@3.0.1: {}
- http-errors@1.4.0:
- dependencies:
- inherits: 2.0.1
- statuses: 1.5.0
-
- http-errors@1.7.3:
- dependencies:
- depd: 1.1.2
- inherits: 2.0.4
- setprototypeof: 1.1.1
- statuses: 1.5.0
- toidentifier: 1.0.0
-
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
@@ -12662,16 +11472,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- human-signals@1.1.1: {}
-
human-signals@2.1.0: {}
hyperhtml-style@0.1.3: {}
- iconv-lite@0.4.24:
- dependencies:
- safer-buffer: 2.1.2
-
ieee754@1.2.1: {}
ignore@5.3.2: {}
@@ -12703,8 +11507,6 @@ snapshots:
once: 1.4.0
wrappy: 1.0.2
- inherits@2.0.1: {}
-
inherits@2.0.4: {}
inline-style-parser@0.2.4: {}
@@ -12882,8 +11684,6 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
- isarray@0.0.1: {}
-
isarray@2.0.5: {}
isexe@2.0.0: {}
@@ -12972,15 +11772,15 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ jest-cli@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)):
dependencies:
- "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
"@jest/test-result": 30.1.3
"@jest/types": 30.0.5
chalk: 4.1.2
exit-x: 0.2.2
import-local: 3.2.0
- jest-config: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest-config: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
jest-util: 30.0.5
jest-validate: 30.1.0
yargs: 17.7.2
@@ -12991,40 +11791,7 @@ snapshots:
- supports-color
- ts-node
- jest-config@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
- dependencies:
- "@babel/core": 7.28.3
- "@jest/get-type": 30.1.0
- "@jest/pattern": 30.0.1
- "@jest/test-sequencer": 30.1.3
- "@jest/types": 30.0.5
- babel-jest: 30.1.2(@babel/core@7.28.3)
- chalk: 4.1.2
- ci-info: 4.3.0
- deepmerge: 4.3.1
- glob: 10.4.5
- graceful-fs: 4.2.11
- jest-circus: 30.1.3(babel-plugin-macros@3.1.0)
- jest-docblock: 30.0.1
- jest-environment-node: 30.1.2
- jest-regex-util: 30.0.1
- jest-resolve: 30.1.3
- jest-runner: 30.1.3
- jest-util: 30.0.5
- jest-validate: 30.1.0
- micromatch: 4.0.8
- parse-json: 5.2.0
- pretty-format: 30.0.5
- slash: 3.0.0
- strip-json-comments: 3.1.1
- optionalDependencies:
- "@types/node": 16.18.11
- ts-node: 10.9.1(@types/node@16.18.11)(typescript@5.9.2)
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
-
- jest-config@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ jest-config@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)):
dependencies:
"@babel/core": 7.28.3
"@jest/get-type": 30.1.0
@@ -13052,7 +11819,7 @@ snapshots:
strip-json-comments: 3.1.1
optionalDependencies:
"@types/node": 24.2.1
- ts-node: 10.9.1(@types/node@16.18.11)(typescript@5.9.2)
+ ts-node: 10.9.1(@types/node@24.2.1)(typescript@5.9.2)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -13273,6 +12040,13 @@ snapshots:
jest-util: 30.0.5
string-length: 4.0.2
+ jest-worker@27.5.1:
+ dependencies:
+ "@types/node": 24.2.1
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+ optional: true
+
jest-worker@29.7.0:
dependencies:
"@types/node": 24.2.1
@@ -13288,12 +12062,12 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ jest@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)):
dependencies:
- "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ "@jest/core": 30.1.3(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
"@jest/types": 30.0.5
import-local: 3.2.0
- jest-cli: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest-cli: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
transitivePeerDependencies:
- "@types/node"
- babel-plugin-macros
@@ -13326,11 +12100,6 @@ snapshots:
json-parse-even-better-errors@2.3.1: {}
- json-schema-to-ts@1.6.4:
- dependencies:
- "@types/json-schema": 7.0.15
- ts-toolbelt: 6.15.5
-
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -13343,16 +12112,6 @@ snapshots:
json5@2.2.3: {}
- jsonfile@4.0.0:
- optionalDependencies:
- graceful-fs: 4.2.11
-
- jsonfile@6.2.0:
- dependencies:
- universalify: 2.0.1
- optionalDependencies:
- graceful-fs: 4.2.11
-
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.9
@@ -13397,6 +12156,9 @@ snapshots:
lines-and-columns@1.2.4: {}
+ loader-runner@4.3.0:
+ optional: true
+
localforage@1.10.0:
dependencies:
lie: 3.1.1
@@ -13441,10 +12203,6 @@ snapshots:
dependencies:
"@jridgewell/sourcemap-codec": 1.5.5
- make-dir@3.1.0:
- dependencies:
- semver: 6.3.1
-
make-dir@4.0.0:
dependencies:
semver: 7.7.2
@@ -13566,12 +12324,6 @@ snapshots:
merge2@1.4.1: {}
- micro@9.3.5-canary.3:
- dependencies:
- arg: 4.1.0
- content-type: 1.0.4
- raw-body: 2.4.1
-
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.2.0
@@ -13732,40 +12484,14 @@ snapshots:
minimist@1.2.8: {}
- minipass@2.9.0:
- dependencies:
- safe-buffer: 5.2.1
- yallist: 3.1.1
-
- minipass@3.3.6:
- dependencies:
- yallist: 4.0.0
-
- minipass@5.0.0: {}
-
minipass@7.1.2: {}
- minizlib@1.3.3:
- dependencies:
- minipass: 2.9.0
-
- minizlib@2.1.2:
- dependencies:
- minipass: 3.3.6
- yallist: 4.0.0
-
mitt@3.0.1: {}
mkdirp@0.5.6:
dependencies:
minimist: 1.2.8
- mkdirp@1.0.4: {}
-
- mri@1.2.0: {}
-
- ms@2.1.1: {}
-
ms@2.1.3: {}
mz@2.7.0:
@@ -13782,13 +12508,13 @@ snapshots:
neo-async@2.6.2: {}
- next-auth@4.24.11(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next-auth@4.24.11(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
"@babel/runtime": 7.28.2
"@panva/hkdf": 1.2.1
cookie: 0.7.2
jose: 4.15.9
- next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth: 0.9.15
openid-client: 5.7.1
preact: 10.27.0
@@ -13802,7 +12528,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
+ next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
dependencies:
"@next/env": 14.2.31
"@swc/helpers": 0.5.5
@@ -13823,6 +12549,7 @@ snapshots:
"@next/swc-win32-arm64-msvc": 14.2.31
"@next/swc-win32-ia32-msvc": 14.2.31
"@next/swc-win32-x64-msvc": 14.2.31
+ "@opentelemetry/api": 1.9.0
sass: 1.90.0
transitivePeerDependencies:
- "@babel/core"
@@ -13833,28 +12560,14 @@ snapshots:
node-addon-api@7.1.1:
optional: true
- node-fetch@2.6.7:
- dependencies:
- whatwg-url: 5.0.0
-
- node-fetch@2.6.9:
- dependencies:
- whatwg-url: 5.0.0
-
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
- node-gyp-build@4.8.4: {}
-
node-int64@0.4.0: {}
node-releases@2.0.19: {}
- nopt@5.0.0:
- dependencies:
- abbrev: 1.1.1
-
normalize-path@3.0.0: {}
normalize-range@0.1.2: {}
@@ -13863,19 +12576,12 @@ snapshots:
dependencies:
path-key: 3.1.1
- npmlog@5.0.1:
- dependencies:
- are-we-there-yet: 2.0.0
- console-control-strings: 1.1.0
- gauge: 3.0.2
- set-blocking: 2.0.0
-
- nuqs@2.4.3(next@14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
+ nuqs@2.4.3(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
dependencies:
mitt: 3.0.1
react: 18.3.1
optionalDependencies:
- next: 14.2.31(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth@0.9.15: {}
@@ -13927,10 +12633,6 @@ snapshots:
oidc-token-hash@5.1.1: {}
- once@1.3.3:
- dependencies:
- wrappy: 1.0.2
-
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -13977,16 +12679,12 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
- os-paths@4.4.0: {}
-
own-keys@1.0.1:
dependencies:
get-intrinsic: 1.3.0
object-keys: 1.1.1
safe-push-apply: 1.0.0
- p-finally@2.0.1: {}
-
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
@@ -14034,21 +12732,12 @@ snapshots:
index-to-position: 1.1.0
type-fest: 4.41.0
- parse-ms@2.1.0: {}
-
- path-browserify@1.0.1: {}
-
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
path-key@3.1.1: {}
- path-match@1.2.4:
- dependencies:
- http-errors: 1.4.0
- path-to-regexp: 1.9.0
-
path-parse@1.0.7: {}
path-scurry@1.11.1:
@@ -14056,22 +12745,10 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
- path-to-regexp@1.9.0:
- dependencies:
- isarray: 0.0.1
-
- path-to-regexp@6.1.0: {}
-
- path-to-regexp@6.2.1: {}
-
path-type@4.0.0: {}
- pend@1.2.0: {}
-
perfect-freehand@1.2.2: {}
- picocolors@1.0.0: {}
-
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -14102,13 +12779,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.5.6
- postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)):
dependencies:
lilconfig: 3.1.3
yaml: 2.8.1
optionalDependencies:
postcss: 8.5.6
- ts-node: 10.9.1(@types/node@16.18.11)(typescript@5.9.2)
+ ts-node: 10.9.1(@types/node@24.2.1)(typescript@5.9.2)
postcss-nested@6.2.0(postcss@8.5.6):
dependencies:
@@ -14153,14 +12830,8 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
- pretty-ms@7.0.1:
- dependencies:
- parse-ms: 2.1.0
-
progress@2.0.3: {}
- promisepipe@3.0.0: {}
-
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@@ -14177,11 +12848,6 @@ snapshots:
dependencies:
proxy-compare: 3.0.1
- pump@3.0.3:
- dependencies:
- end-of-stream: 1.4.5
- once: 1.4.0
-
punycode@2.3.1: {}
pure-rand@7.0.1: {}
@@ -14194,13 +12860,6 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
- raw-body@2.4.1:
- dependencies:
- bytes: 3.1.0
- http-errors: 1.7.3
- iconv-lite: 0.4.24
- unpipe: 1.0.0
-
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -14292,10 +12951,6 @@ snapshots:
string_decoder: 1.3.0
util-deprecate: 1.0.2
- readdirp@3.3.0:
- dependencies:
- picomatch: 2.3.1
-
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
@@ -14393,11 +13048,7 @@ snapshots:
reusify@1.1.0: {}
- rimraf@3.0.2:
- dependencies:
- glob: 7.2.3
-
- rollup@2.79.2:
+ rollup@2.78.0:
optionalDependencies:
fsevents: 2.3.3
@@ -14431,8 +13082,6 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
- safer-buffer@2.1.2: {}
-
sass@1.90.0:
dependencies:
chokidar: 4.0.3
@@ -14445,19 +13094,26 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ schema-utils@4.3.2:
+ dependencies:
+ "@types/json-schema": 7.0.15
+ ajv: 8.17.1
+ ajv-formats: 2.1.1(ajv@8.17.1)
+ ajv-keywords: 5.1.0(ajv@8.17.1)
+ optional: true
+
sdp-transform@2.15.0: {}
sdp@3.2.1: {}
semver@6.3.1: {}
- semver@7.3.5:
- dependencies:
- lru-cache: 6.0.0
-
semver@7.7.2: {}
- set-blocking@2.0.0: {}
+ serialize-javascript@6.0.2:
+ dependencies:
+ randombytes: 2.1.0
+ optional: true
set-function-length@1.2.2:
dependencies:
@@ -14481,8 +13137,6 @@ snapshots:
es-errors: 1.3.0
es-object-atoms: 1.1.1
- setprototypeof@1.1.1: {}
-
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -14519,8 +13173,6 @@ snapshots:
signal-exit@3.0.7: {}
- signal-exit@4.0.2: {}
-
signal-exit@4.1.0: {}
simple-peer@9.11.1:
@@ -14562,6 +13214,12 @@ snapshots:
buffer-from: 1.1.2
source-map: 0.6.1
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+ optional: true
+
source-map@0.5.7: {}
source-map@0.6.1: {}
@@ -14584,25 +13242,11 @@ snapshots:
standard-as-callback@2.1.0: {}
- stat-mode@0.3.0: {}
-
- statuses@1.5.0: {}
-
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
internal-slot: 1.1.0
- stream-to-array@2.3.0:
- dependencies:
- any-promise: 1.3.0
-
- stream-to-promise@2.2.0:
- dependencies:
- any-promise: 1.3.0
- end-of-stream: 1.1.0
- stream-to-array: 2.3.0
-
streamsearch@1.1.0: {}
string-length@4.0.2:
@@ -14743,7 +13387,7 @@ snapshots:
dependencies:
"@pkgr/core": 0.2.9
- tailwindcss@3.4.17(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)):
+ tailwindcss@3.4.17(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)):
dependencies:
"@alloc/quick-lru": 5.2.0
arg: 5.0.2
@@ -14762,7 +13406,7 @@ snapshots:
postcss: 8.5.6
postcss-import: 15.1.0(postcss@8.5.6)
postcss-js: 4.0.1(postcss@8.5.6)
- postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
postcss-nested: 6.2.0(postcss@8.5.6)
postcss-selector-parser: 6.1.2
resolve: 1.22.10
@@ -14770,24 +13414,26 @@ snapshots:
transitivePeerDependencies:
- ts-node
- tar@4.4.18:
- dependencies:
- chownr: 1.1.4
- fs-minipass: 1.2.7
- minipass: 2.9.0
- minizlib: 1.3.3
- mkdirp: 0.5.6
- safe-buffer: 5.2.1
- yallist: 3.1.1
+ tapable@2.2.3:
+ optional: true
- tar@6.2.1:
+ terser-webpack-plugin@5.3.14(webpack@5.101.3):
dependencies:
- chownr: 2.0.0
- fs-minipass: 2.1.0
- minipass: 5.0.0
- minizlib: 2.1.2
- mkdirp: 1.0.4
- yallist: 4.0.0
+ "@jridgewell/trace-mapping": 0.3.30
+ jest-worker: 27.5.1
+ schema-utils: 4.3.2
+ serialize-javascript: 6.0.2
+ terser: 5.44.0
+ webpack: 5.101.3
+ optional: true
+
+ terser@5.44.0:
+ dependencies:
+ "@jridgewell/source-map": 0.3.11
+ acorn: 8.15.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+ optional: true
test-exclude@6.0.0:
dependencies:
@@ -14803,10 +13449,6 @@ snapshots:
dependencies:
any-promise: 1.3.0
- time-span@4.0.0:
- dependencies:
- convert-hrtime: 3.0.0
-
tinyglobby@0.2.14:
dependencies:
fdir: 6.4.6(picomatch@4.0.3)
@@ -14818,12 +13460,8 @@ snapshots:
dependencies:
is-number: 7.0.0
- toidentifier@1.0.0: {}
-
tr46@0.0.3: {}
- tree-kill@1.2.2: {}
-
trim-lines@3.0.1: {}
trough@2.2.0: {}
@@ -14834,12 +13472,12 @@ snapshots:
ts-interface-checker@0.1.13: {}
- ts-jest@29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2)))(typescript@5.9.2):
+ ts-jest@29.4.1(@babel/core@7.28.3)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.3))(jest-util@30.0.5)(jest@30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2)))(typescript@5.9.2):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
handlebars: 4.7.8
- jest: 30.1.3(@types/node@16.18.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2))
+ jest: 30.1.3(@types/node@24.2.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2))
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
@@ -14854,37 +13492,14 @@ snapshots:
babel-jest: 30.1.2(@babel/core@7.28.3)
jest-util: 30.0.5
- ts-morph@12.0.0:
- dependencies:
- "@ts-morph/common": 0.11.1
- code-block-writer: 10.1.1
-
- ts-node@10.9.1(@types/node@16.18.11)(typescript@4.9.5):
+ ts-node@10.9.1(@types/node@24.2.1)(typescript@5.9.2):
dependencies:
"@cspotcode/source-map-support": 0.8.1
"@tsconfig/node10": 1.0.11
"@tsconfig/node12": 1.0.11
"@tsconfig/node14": 1.0.3
"@tsconfig/node16": 1.0.4
- "@types/node": 16.18.11
- acorn: 8.15.0
- acorn-walk: 8.3.4
- arg: 4.1.3
- create-require: 1.1.1
- diff: 4.0.2
- make-error: 1.3.6
- typescript: 4.9.5
- v8-compile-cache-lib: 3.0.1
- yn: 3.1.1
-
- ts-node@10.9.1(@types/node@16.18.11)(typescript@5.9.2):
- dependencies:
- "@cspotcode/source-map-support": 0.8.1
- "@tsconfig/node10": 1.0.11
- "@tsconfig/node12": 1.0.11
- "@tsconfig/node14": 1.0.3
- "@tsconfig/node16": 1.0.4
- "@types/node": 16.18.11
+ "@types/node": 24.2.1
acorn: 8.15.0
acorn-walk: 8.3.4
arg: 4.1.3
@@ -14896,8 +13511,6 @@ snapshots:
yn: 3.1.1
optional: true
- ts-toolbelt@6.15.5: {}
-
tsconfig-paths@3.15.0:
dependencies:
"@types/json5": 0.0.29
@@ -14952,8 +13565,6 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
- typescript@4.9.5: {}
-
typescript@5.9.2: {}
ua-is-frozen@0.1.2: {}
@@ -14983,8 +13594,6 @@ snapshots:
uhyphen@0.1.0: {}
- uid-promise@1.0.0: {}
-
umap@1.0.2: {}
unbox-primitive@1.1.0:
@@ -14996,10 +13605,6 @@ snapshots:
undici-types@7.10.0: {}
- undici@5.28.4:
- dependencies:
- "@fastify/busboy": 2.1.1
-
unified@11.0.5:
dependencies:
"@types/unist": 3.0.3
@@ -15033,12 +13638,6 @@ snapshots:
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
- universalify@0.1.2: {}
-
- universalify@2.0.1: {}
-
- unpipe@1.0.0: {}
-
unrs-resolver@1.11.1:
dependencies:
napi-postinstall: 0.3.3
@@ -15096,8 +13695,6 @@ snapshots:
uuid-validate@0.0.3: {}
- uuid@3.3.2: {}
-
uuid@8.3.2: {}
uuid@9.0.1: {}
@@ -15106,7 +13703,8 @@ snapshots:
dependencies:
uarray: 1.0.0
- v8-compile-cache-lib@3.0.1: {}
+ v8-compile-cache-lib@3.0.1:
+ optional: true
v8-to-istanbul@9.3.0:
dependencies:
@@ -15114,26 +13712,6 @@ snapshots:
"@types/istanbul-lib-coverage": 2.0.6
convert-source-map: 2.0.0
- vercel@37.14.0:
- dependencies:
- "@vercel/build-utils": 8.4.12
- "@vercel/fun": 1.1.0
- "@vercel/go": 3.2.0
- "@vercel/hydrogen": 1.0.9
- "@vercel/next": 4.3.18
- "@vercel/node": 3.2.24
- "@vercel/python": 4.3.1
- "@vercel/redwood": 2.1.8
- "@vercel/remix-builder": 2.2.13
- "@vercel/ruby": 2.1.0
- "@vercel/static-build": 2.5.34
- chokidar: 3.3.1
- transitivePeerDependencies:
- - "@swc/core"
- - "@swc/wasm"
- - encoding
- - supports-color
-
vfile-message@4.0.3:
dependencies:
"@types/unist": 3.0.3
@@ -15148,14 +13726,51 @@ snapshots:
dependencies:
makeerror: 1.0.12
- wavesurfer.js@7.10.1: {}
+ watchpack@2.4.4:
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ optional: true
- web-vitals@0.2.4: {}
+ wavesurfer.js@7.10.1: {}
webidl-conversions@3.0.1: {}
webpack-sources@3.3.3: {}
+ webpack@5.101.3:
+ dependencies:
+ "@types/eslint-scope": 3.7.7
+ "@types/estree": 1.0.8
+ "@types/json-schema": 7.0.15
+ "@webassemblyjs/ast": 1.14.1
+ "@webassemblyjs/wasm-edit": 1.14.1
+ "@webassemblyjs/wasm-parser": 1.14.1
+ acorn: 8.15.0
+ acorn-import-phases: 1.0.4(acorn@8.15.0)
+ browserslist: 4.25.2
+ chrome-trace-event: 1.0.4
+ enhanced-resolve: 5.18.3
+ es-module-lexer: 1.7.0
+ eslint-scope: 5.1.1
+ events: 3.3.0
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ json-parse-even-better-errors: 2.3.1
+ loader-runner: 4.3.0
+ mime-types: 2.1.35
+ neo-async: 2.6.2
+ schema-utils: 4.3.2
+ tapable: 2.2.3
+ terser-webpack-plugin: 5.3.14(webpack@5.101.3)
+ watchpack: 2.4.4
+ webpack-sources: 3.3.3
+ transitivePeerDependencies:
+ - "@swc/core"
+ - esbuild
+ - uglify-js
+ optional: true
+
webrtc-adapter@9.0.3:
dependencies:
sdp: 3.2.1
@@ -15210,10 +13825,6 @@ snapshots:
dependencies:
isexe: 2.0.0
- wide-align@1.1.5:
- dependencies:
- string-width: 4.2.3
-
word-wrap@1.2.5: {}
wordwrap@1.0.0: {}
@@ -15239,14 +13850,6 @@ snapshots:
ws@8.17.1: {}
- xdg-app-paths@5.1.0:
- dependencies:
- xdg-portable: 7.3.0
-
- xdg-portable@7.3.0:
- dependencies:
- os-paths: 4.4.0
-
xmlhttprequest-ssl@2.0.0: {}
y18n@5.0.8: {}
@@ -15273,21 +13876,8 @@ snapshots:
y18n: 5.0.8
yargs-parser: 21.1.1
- yauzl-clone@1.0.4:
- dependencies:
- events-intercept: 2.0.0
-
- yauzl-promise@2.1.3:
- dependencies:
- yauzl: 2.10.0
- yauzl-clone: 1.0.4
-
- yauzl@2.10.0:
- dependencies:
- buffer-crc32: 0.2.13
- fd-slicer: 1.1.0
-
- yn@3.1.1: {}
+ yn@3.1.1:
+ optional: true
yocto-queue@0.1.0: {}
From b3a8e9739da1aa423646b682d80265ffffd6dd2a Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Thu, 11 Sep 2025 17:52:34 -0400
Subject: [PATCH 14/20] chore: whereby & s3 settings env error reporting (#637)
Co-authored-by: Igor Loskutov
---
server/reflector/settings.py | 4 +-
server/reflector/utils/string.py | 7 +++-
server/reflector/whereby.py | 65 +++++++++++++++++++++++++++-----
3 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/server/reflector/settings.py b/server/reflector/settings.py
index 686f67c1..9659f648 100644
--- a/server/reflector/settings.py
+++ b/server/reflector/settings.py
@@ -1,6 +1,8 @@
from pydantic.types import PositiveInt
from pydantic_settings import BaseSettings, SettingsConfigDict
+from reflector.utils.string import NonEmptyString
+
class Settings(BaseSettings):
model_config = SettingsConfigDict(
@@ -120,7 +122,7 @@ class Settings(BaseSettings):
# Whereby integration
WHEREBY_API_URL: str = "https://api.whereby.dev/v1"
- WHEREBY_API_KEY: str | None = None
+ WHEREBY_API_KEY: NonEmptyString | None = None
WHEREBY_WEBHOOK_SECRET: str | None = None
AWS_WHEREBY_ACCESS_KEY_ID: str | None = None
AWS_WHEREBY_ACCESS_KEY_SECRET: str | None = None
diff --git a/server/reflector/utils/string.py b/server/reflector/utils/string.py
index 08a9de78..05f40e30 100644
--- a/server/reflector/utils/string.py
+++ b/server/reflector/utils/string.py
@@ -10,8 +10,11 @@ NonEmptyString = Annotated[
non_empty_string_adapter = TypeAdapter(NonEmptyString)
-def parse_non_empty_string(s: str) -> NonEmptyString:
- return non_empty_string_adapter.validate_python(s)
+def parse_non_empty_string(s: str, error: str | None = None) -> NonEmptyString:
+ try:
+ return non_empty_string_adapter.validate_python(s)
+ except Exception as e:
+ raise ValueError(f"{e}: {error}" if error else e) from e
def try_parse_non_empty_string(s: str) -> NonEmptyString | None:
diff --git a/server/reflector/whereby.py b/server/reflector/whereby.py
index deaa5274..8b5c18fd 100644
--- a/server/reflector/whereby.py
+++ b/server/reflector/whereby.py
@@ -1,18 +1,60 @@
+import logging
from datetime import datetime
import httpx
from reflector.db.rooms import Room
from reflector.settings import settings
+from reflector.utils.string import parse_non_empty_string
+
+logger = logging.getLogger(__name__)
+
+
+def _get_headers():
+ api_key = parse_non_empty_string(
+ settings.WHEREBY_API_KEY, "WHEREBY_API_KEY value is required."
+ )
+ return {
+ "Content-Type": "application/json; charset=utf-8",
+ "Authorization": f"Bearer {api_key}",
+ }
+
-HEADERS = {
- "Content-Type": "application/json; charset=utf-8",
- "Authorization": f"Bearer {settings.WHEREBY_API_KEY}",
-}
TIMEOUT = 10 # seconds
+def _get_whereby_s3_auth():
+ errors = []
+ try:
+ bucket_name = parse_non_empty_string(
+ settings.RECORDING_STORAGE_AWS_BUCKET_NAME,
+ "RECORDING_STORAGE_AWS_BUCKET_NAME value is required.",
+ )
+ except Exception as e:
+ errors.append(e)
+ try:
+ key_id = parse_non_empty_string(
+ settings.AWS_WHEREBY_ACCESS_KEY_ID,
+ "AWS_WHEREBY_ACCESS_KEY_ID value is required.",
+ )
+ except Exception as e:
+ errors.append(e)
+ try:
+ key_secret = parse_non_empty_string(
+ settings.AWS_WHEREBY_ACCESS_KEY_SECRET,
+ "AWS_WHEREBY_ACCESS_KEY_SECRET value is required.",
+ )
+ except Exception as e:
+ errors.append(e)
+ if len(errors) > 0:
+ raise Exception(
+ f"Failed to get Whereby auth settings: {', '.join(str(e) for e in errors)}"
+ )
+ return bucket_name, key_id, key_secret
+
+
async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room):
+ s3_bucket_name, s3_key_id, s3_key_secret = _get_whereby_s3_auth()
data = {
"isLocked": room.is_locked,
"roomNamePrefix": room_name_prefix,
@@ -23,23 +65,26 @@ async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room):
"type": room.recording_type,
"destination": {
"provider": "s3",
- "bucket": settings.RECORDING_STORAGE_AWS_BUCKET_NAME,
- "accessKeyId": settings.AWS_WHEREBY_ACCESS_KEY_ID,
- "accessKeySecret": settings.AWS_WHEREBY_ACCESS_KEY_SECRET,
+ "bucket": s3_bucket_name,
+ "accessKeyId": s3_key_id,
+ "accessKeySecret": s3_key_secret,
"fileFormat": "mp4",
},
"startTrigger": room.recording_trigger,
},
"fields": ["hostRoomUrl"],
}
-
async with httpx.AsyncClient() as client:
response = await client.post(
f"{settings.WHEREBY_API_URL}/meetings",
- headers=HEADERS,
+ headers=_get_headers(),
json=data,
timeout=TIMEOUT,
)
+ if response.status_code == 403:
+ logger.warning(
+ f"Failed to create meeting: access denied on Whereby: {response.text}"
+ )
response.raise_for_status()
return response.json()
@@ -48,7 +93,7 @@ async def get_room_sessions(room_name: str):
async with httpx.AsyncClient() as client:
response = await client.get(
f"{settings.WHEREBY_API_URL}/insights/room-sessions?roomName={room_name}",
- headers=HEADERS,
+ headers=_get_headers(),
timeout=TIMEOUT,
)
response.raise_for_status()
From 43ea9349f51cfb83edd9e5c70990d7938350375f Mon Sep 17 00:00:00 2001
From: Mathieu Virbel
Date: Thu, 11 Sep 2025 20:57:19 -0600
Subject: [PATCH 15/20] chore(main): release 0.10.0 (#616)
---
CHANGELOG.md | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 987a6579..40174fc4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## [0.10.0](https://github.com/Monadical-SAS/reflector/compare/v0.9.0...v0.10.0) (2025-09-11)
+
+
+### Features
+
+* replace nextjs-config with environment variables ([#632](https://github.com/Monadical-SAS/reflector/issues/632)) ([369ecdf](https://github.com/Monadical-SAS/reflector/commit/369ecdff13f3862d926a9c0b87df52c9d94c4dde))
+
+
+### Bug Fixes
+
+* anonymous users transcript permissions ([#621](https://github.com/Monadical-SAS/reflector/issues/621)) ([f81fe99](https://github.com/Monadical-SAS/reflector/commit/f81fe9948a9237b3e0001b2d8ca84f54d76878f9))
+* auth post ([#624](https://github.com/Monadical-SAS/reflector/issues/624)) ([cde99ca](https://github.com/Monadical-SAS/reflector/commit/cde99ca2716f84ba26798f289047732f0448742e))
+* auth post ([#626](https://github.com/Monadical-SAS/reflector/issues/626)) ([3b85ff3](https://github.com/Monadical-SAS/reflector/commit/3b85ff3bdf4fb053b103070646811bc990c0e70a))
+* auth post ([#627](https://github.com/Monadical-SAS/reflector/issues/627)) ([962038e](https://github.com/Monadical-SAS/reflector/commit/962038ee3f2a555dc3c03856be0e4409456e0996))
+* missing follow_redirects=True on modal endpoint ([#630](https://github.com/Monadical-SAS/reflector/issues/630)) ([fc363bd](https://github.com/Monadical-SAS/reflector/commit/fc363bd49b17b075e64f9186e5e0185abc325ea7))
+* sync backend and frontend token refresh logic ([#614](https://github.com/Monadical-SAS/reflector/issues/614)) ([5a5b323](https://github.com/Monadical-SAS/reflector/commit/5a5b3233820df9536da75e87ce6184a983d4713a))
+
## [0.9.0](https://github.com/Monadical-SAS/reflector/compare/v0.8.2...v0.9.0) (2025-09-06)
From 5cba5d310d14168f99312acb149603e01c50e953 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Fri, 12 Sep 2025 12:41:44 -0400
Subject: [PATCH 16/20] chore: sentry and nextjs major bumps (#633)
* chore: remove nextjs-config
* build fix
* sentry update
* nextjs update
* feature flags doc
* update readme
* explicit nextjs env vars + remove feature-unrelated things and obsolete vars from config
* full config removal
* remove force-dynamic from pages
* compile fix
* restore claude-deleted tests
* no sentry backward compat
* better .env.example
* AUTHENTIK_REFRESH_TOKEN_URL not so required
* accommodate auth system to requiredLogin feature
---------
Co-authored-by: Igor Loskutov
---
.../[transcriptId]/correct/page.tsx | 14 +-
.../(app)/transcripts/[transcriptId]/page.tsx | 11 +-
.../[transcriptId]/record/page.tsx | 19 +-
.../[transcriptId]/upload/page.tsx | 17 +-
www/app/[roomName]/page.tsx | 8 +-
www/app/api/auth/[...nextauth]/route.ts | 2 +-
www/app/layout.tsx | 8 +-
www/app/lib/AuthProvider.tsx | 115 +-
www/app/lib/apiClient.tsx | 5 +-
www/app/lib/array.ts | 12 +
www/app/lib/authBackend.ts | 211 +-
www/app/lib/errorUtils.ts | 42 +-
www/app/lib/features.ts | 8 +-
www/app/webinars/[title]/page.tsx | 9 +-
...nt.config.ts => instrumentation-client.ts} | 2 +
www/instrumentation.ts | 9 +
www/next.config.js | 1 -
www/package.json | 6 +-
www/pnpm-lock.yaml | 2379 ++++++++++++++---
19 files changed, 2285 insertions(+), 593 deletions(-)
create mode 100644 www/app/lib/array.ts
rename www/{sentry.client.config.ts => instrumentation-client.ts} (91%)
create mode 100644 www/instrumentation.ts
diff --git a/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
index 1c7705f4..c4d5a9fc 100644
--- a/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/correct/page.tsx
@@ -1,5 +1,5 @@
"use client";
-import { useState } from "react";
+import { useState, use } from "react";
import TopicHeader from "./topicHeader";
import TopicWords from "./topicWords";
import TopicPlayer from "./topicPlayer";
@@ -18,14 +18,16 @@ import { useRouter } from "next/navigation";
import { Box, Grid } from "@chakra-ui/react";
export type TranscriptCorrect = {
- params: {
+ params: Promise<{
transcriptId: string;
- };
+ }>;
};
-export default function TranscriptCorrect({
- params: { transcriptId },
-}: TranscriptCorrect) {
+export default function TranscriptCorrect(props: TranscriptCorrect) {
+ const params = use(props.params);
+
+ const { transcriptId } = params;
+
const updateTranscriptMutation = useTranscriptUpdate();
const transcript = useTranscriptGet(transcriptId);
const stateCurrentTopic = useState();
diff --git a/www/app/(app)/transcripts/[transcriptId]/page.tsx b/www/app/(app)/transcripts/[transcriptId]/page.tsx
index 73bf2ae7..f06e8935 100644
--- a/www/app/(app)/transcripts/[transcriptId]/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/page.tsx
@@ -5,7 +5,7 @@ import useWaveform from "../useWaveform";
import useMp3 from "../useMp3";
import { TopicList } from "./_components/TopicList";
import { Topic } from "../webSocketTypes";
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, use } from "react";
import FinalSummary from "./finalSummary";
import TranscriptTitle from "../transcriptTitle";
import Player from "../player";
@@ -15,13 +15,14 @@ import { useTranscriptGet } from "../../../lib/apiHooks";
import { TranscriptStatus } from "../../../lib/transcript";
type TranscriptDetails = {
- params: {
+ params: Promise<{
transcriptId: string;
- };
+ }>;
};
export default function TranscriptDetails(details: TranscriptDetails) {
- const transcriptId = details.params.transcriptId;
+ const params = use(details.params);
+ const transcriptId = params.transcriptId;
const router = useRouter();
const statusToRedirect = [
"idle",
@@ -43,7 +44,7 @@ export default function TranscriptDetails(details: TranscriptDetails) {
useEffect(() => {
if (waiting) {
- const newUrl = "/transcripts/" + details.params.transcriptId + "/record";
+ const newUrl = "/transcripts/" + params.transcriptId + "/record";
// Shallow redirection does not work on NextJS 13
// https://github.com/vercel/next.js/discussions/48110
// https://github.com/vercel/next.js/discussions/49540
diff --git a/www/app/(app)/transcripts/[transcriptId]/record/page.tsx b/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
index 0dc26c6d..d93b34b6 100644
--- a/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/record/page.tsx
@@ -1,5 +1,5 @@
"use client";
-import { useEffect, useState } from "react";
+import { useEffect, useState, use } from "react";
import Recorder from "../../recorder";
import { TopicList } from "../_components/TopicList";
import { useWebSockets } from "../../useWebSockets";
@@ -14,19 +14,20 @@ import { useTranscriptGet } from "../../../../lib/apiHooks";
import { TranscriptStatus } from "../../../../lib/transcript";
type TranscriptDetails = {
- params: {
+ params: Promise<{
transcriptId: string;
- };
+ }>;
};
const TranscriptRecord = (details: TranscriptDetails) => {
- const transcript = useTranscriptGet(details.params.transcriptId);
+ const params = use(details.params);
+ const transcript = useTranscriptGet(params.transcriptId);
const [transcriptStarted, setTranscriptStarted] = useState(false);
const useActiveTopic = useState(null);
- const webSockets = useWebSockets(details.params.transcriptId);
+ const webSockets = useWebSockets(params.transcriptId);
- const mp3 = useMp3(details.params.transcriptId, true);
+ const mp3 = useMp3(params.transcriptId, true);
const router = useRouter();
@@ -47,7 +48,7 @@ const TranscriptRecord = (details: TranscriptDetails) => {
if (newStatus && (newStatus == "ended" || newStatus == "error")) {
console.log(newStatus, "redirecting");
- const newUrl = "/transcripts/" + details.params.transcriptId;
+ const newUrl = "/transcripts/" + params.transcriptId;
router.replace(newUrl);
}
}, [webSockets.status?.value, transcript.data?.status]);
@@ -75,7 +76,7 @@ const TranscriptRecord = (details: TranscriptDetails) => {
) : (
// todo: only start recording animation when you get "recorded" status
-
+
)}
{
topics={webSockets.topics}
useActiveTopic={useActiveTopic}
autoscroll={true}
- transcriptId={details.params.transcriptId}
+ transcriptId={params.transcriptId}
status={status}
currentTranscriptText={webSockets.accumulatedText}
/>
diff --git a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
index 844d05e9..b4bc25cc 100644
--- a/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
+++ b/www/app/(app)/transcripts/[transcriptId]/upload/page.tsx
@@ -1,5 +1,5 @@
"use client";
-import { useEffect, useState } from "react";
+import { useEffect, useState, use } from "react";
import { useWebSockets } from "../../useWebSockets";
import { lockWakeState, releaseWakeState } from "../../../../lib/wakeLock";
import { useRouter } from "next/navigation";
@@ -9,18 +9,19 @@ import FileUploadButton from "../../fileUploadButton";
import { useTranscriptGet } from "../../../../lib/apiHooks";
type TranscriptUpload = {
- params: {
+ params: Promise<{
transcriptId: string;
- };
+ }>;
};
const TranscriptUpload = (details: TranscriptUpload) => {
- const transcript = useTranscriptGet(details.params.transcriptId);
+ const params = use(details.params);
+ const transcript = useTranscriptGet(params.transcriptId);
const [transcriptStarted, setTranscriptStarted] = useState(false);
- const webSockets = useWebSockets(details.params.transcriptId);
+ const webSockets = useWebSockets(params.transcriptId);
- const mp3 = useMp3(details.params.transcriptId, true);
+ const mp3 = useMp3(params.transcriptId, true);
const router = useRouter();
@@ -50,7 +51,7 @@ const TranscriptUpload = (details: TranscriptUpload) => {
if (newStatus && (newStatus == "ended" || newStatus == "error")) {
console.log(newStatus, "redirecting");
- const newUrl = "/transcripts/" + details.params.transcriptId;
+ const newUrl = "/transcripts/" + params.transcriptId;
router.replace(newUrl);
}
}, [webSockets.status?.value, transcript.data?.status]);
@@ -84,7 +85,7 @@ const TranscriptUpload = (details: TranscriptUpload) => {
Please select the file, supported formats: .mp3, m4a, .wav,
.mp4, .mov or .webm
-
+
>
)}
{status && status == "uploaded" && (
diff --git a/www/app/[roomName]/page.tsx b/www/app/[roomName]/page.tsx
index 0130588b..867aeb3e 100644
--- a/www/app/[roomName]/page.tsx
+++ b/www/app/[roomName]/page.tsx
@@ -7,6 +7,7 @@ import {
useState,
useContext,
RefObject,
+ use,
} from "react";
import {
Box,
@@ -30,9 +31,9 @@ import { FaBars } from "react-icons/fa6";
import { useAuth } from "../lib/AuthProvider";
export type RoomDetails = {
- params: {
+ params: Promise<{
roomName: string;
- };
+ }>;
};
// stages: we focus on the consent, then whereby steals focus, then we focus on the consent again, then return focus to whoever stole it initially
@@ -255,9 +256,10 @@ const useWhereby = () => {
};
export default function Room(details: RoomDetails) {
+ const params = use(details.params);
const wherebyLoaded = useWhereby();
const wherebyRef = useRef
(null);
- const roomName = details.params.roomName;
+ const roomName = params.roomName;
const meeting = useRoomMeeting(roomName);
const router = useRouter();
const status = useAuth().status;
diff --git a/www/app/api/auth/[...nextauth]/route.ts b/www/app/api/auth/[...nextauth]/route.ts
index 7b73c22a..250e9e34 100644
--- a/www/app/api/auth/[...nextauth]/route.ts
+++ b/www/app/api/auth/[...nextauth]/route.ts
@@ -1,6 +1,6 @@
import NextAuth from "next-auth";
import { authOptions } from "../../../lib/authBackend";
-const handler = NextAuth(authOptions);
+const handler = NextAuth(authOptions());
export { handler as GET, handler as POST };
diff --git a/www/app/layout.tsx b/www/app/layout.tsx
index 93fb15e9..175b7cbc 100644
--- a/www/app/layout.tsx
+++ b/www/app/layout.tsx
@@ -6,6 +6,7 @@ import ErrorMessage from "./(errors)/errorMessage";
import { RecordingConsentProvider } from "./recordingConsentContext";
import { ErrorBoundary } from "@sentry/nextjs";
import { Providers } from "./providers";
+import { assertExistsAndNonEmptyString } from "./lib/utils";
const poppins = Poppins({
subsets: ["latin"],
@@ -20,8 +21,13 @@ export const viewport: Viewport = {
maximumScale: 1,
};
+const NEXT_PUBLIC_SITE_URL = assertExistsAndNonEmptyString(
+ process.env.NEXT_PUBLIC_SITE_URL,
+ "NEXT_PUBLIC_SITE_URL required",
+);
+
export const metadata: Metadata = {
- metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL!),
+ metadataBase: new URL(NEXT_PUBLIC_SITE_URL),
title: {
template: "%s – Reflector",
default: "Reflector - AI-Powered Meeting Transcriptions by Monadical",
diff --git a/www/app/lib/AuthProvider.tsx b/www/app/lib/AuthProvider.tsx
index 1a8ebea6..e1eabf99 100644
--- a/www/app/lib/AuthProvider.tsx
+++ b/www/app/lib/AuthProvider.tsx
@@ -9,6 +9,7 @@ import { Session } from "next-auth";
import { SessionAutoRefresh } from "./SessionAutoRefresh";
import { REFRESH_ACCESS_TOKEN_ERROR } from "./auth";
import { assertExists } from "./utils";
+import { featureEnabled } from "./features";
type AuthContextType = (
| { status: "loading" }
@@ -27,65 +28,83 @@ type AuthContextType = (
};
const AuthContext = createContext(undefined);
+const isAuthEnabled = featureEnabled("requireLogin");
+
+const noopAuthContext: AuthContextType = {
+ status: "unauthenticated",
+ update: async () => {
+ return null;
+ },
+ signIn: async () => {
+ throw new Error("signIn not supposed to be called");
+ },
+ signOut: async () => {
+ throw new Error("signOut not supposed to be called");
+ },
+};
export function AuthProvider({ children }: { children: React.ReactNode }) {
const { data: session, status, update } = useNextAuthSession();
- const customSession = session ? assertCustomSession(session) : null;
- const contextValue: AuthContextType = {
- ...(() => {
- switch (status) {
- case "loading": {
- const sessionIsHere = !!customSession;
- switch (sessionIsHere) {
- case false: {
- return { status };
+ const contextValue: AuthContextType = isAuthEnabled
+ ? {
+ ...(() => {
+ switch (status) {
+ case "loading": {
+ const sessionIsHere = !!session;
+ // actually exists sometimes; nextAuth types are something else
+ switch (sessionIsHere as boolean) {
+ case false: {
+ return { status };
+ }
+ case true: {
+ return {
+ status: "refreshing" as const,
+ user: assertCustomSession(
+ assertExists(session as unknown as Session),
+ ).user,
+ };
+ }
+ default: {
+ throw new Error("unreachable");
+ }
+ }
}
- case true: {
- return {
- status: "refreshing" as const,
- user: assertExists(customSession).user,
- };
+ case "authenticated": {
+ const customSession = assertCustomSession(session);
+ if (customSession?.error === REFRESH_ACCESS_TOKEN_ERROR) {
+ // token had expired but next auth still returns "authenticated" so show user unauthenticated state
+ return {
+ status: "unauthenticated" as const,
+ };
+ } else if (customSession?.accessToken) {
+ return {
+ status,
+ accessToken: customSession.accessToken,
+ accessTokenExpires: customSession.accessTokenExpires,
+ user: customSession.user,
+ };
+ } else {
+ console.warn(
+ "illegal state: authenticated but have no session/or access token. ignoring",
+ );
+ return { status: "unauthenticated" as const };
+ }
+ }
+ case "unauthenticated": {
+ return { status: "unauthenticated" as const };
}
default: {
- const _: never = sessionIsHere;
+ const _: never = status;
throw new Error("unreachable");
}
}
- }
- case "authenticated": {
- if (customSession?.error === REFRESH_ACCESS_TOKEN_ERROR) {
- // token had expired but next auth still returns "authenticated" so show user unauthenticated state
- return {
- status: "unauthenticated" as const,
- };
- } else if (customSession?.accessToken) {
- return {
- status,
- accessToken: customSession.accessToken,
- accessTokenExpires: customSession.accessTokenExpires,
- user: customSession.user,
- };
- } else {
- console.warn(
- "illegal state: authenticated but have no session/or access token. ignoring",
- );
- return { status: "unauthenticated" as const };
- }
- }
- case "unauthenticated": {
- return { status: "unauthenticated" as const };
- }
- default: {
- const _: never = status;
- throw new Error("unreachable");
- }
+ })(),
+ update,
+ signIn,
+ signOut,
}
- })(),
- update,
- signIn,
- signOut,
- };
+ : noopAuthContext;
// not useEffect, we need it ASAP
// apparently, still no guarantee this code runs before mutations are fired
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index 95051913..86f8f161 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -7,7 +7,10 @@ import { assertExistsAndNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";
export const API_URL = !isBuildPhase
- ? assertExistsAndNonEmptyString(process.env.NEXT_PUBLIC_API_URL)
+ ? assertExistsAndNonEmptyString(
+ process.env.NEXT_PUBLIC_API_URL,
+ "NEXT_PUBLIC_API_URL required",
+ )
: "http://localhost";
// TODO decide strict validation or not
diff --git a/www/app/lib/array.ts b/www/app/lib/array.ts
new file mode 100644
index 00000000..f47aaa42
--- /dev/null
+++ b/www/app/lib/array.ts
@@ -0,0 +1,12 @@
+export type NonEmptyArray = [T, ...T[]];
+export const isNonEmptyArray = (arr: T[]): arr is NonEmptyArray =>
+ arr.length > 0;
+export const assertNonEmptyArray = (
+ arr: T[],
+ err?: string,
+): NonEmptyArray => {
+ if (isNonEmptyArray(arr)) {
+ return arr;
+ }
+ throw new Error(err ?? "Expected non-empty array");
+};
diff --git a/www/app/lib/authBackend.ts b/www/app/lib/authBackend.ts
index 06bddff2..5e9767c9 100644
--- a/www/app/lib/authBackend.ts
+++ b/www/app/lib/authBackend.ts
@@ -19,102 +19,126 @@ import {
} from "./redisTokenCache";
import { tokenCacheRedis, redlock } from "./redisClient";
import { isBuildPhase } from "./next";
+import { sequenceThrows } from "./errorUtils";
+import { featureEnabled } from "./features";
const TOKEN_CACHE_TTL = REFRESH_ACCESS_TOKEN_BEFORE;
-const CLIENT_ID = !isBuildPhase
- ? assertExistsAndNonEmptyString(process.env.AUTHENTIK_CLIENT_ID)
- : "noop";
-const CLIENT_SECRET = !isBuildPhase
- ? assertExistsAndNonEmptyString(process.env.AUTHENTIK_CLIENT_SECRET)
- : "noop";
+const getAuthentikClientId = () =>
+ assertExistsAndNonEmptyString(
+ process.env.AUTHENTIK_CLIENT_ID,
+ "AUTHENTIK_CLIENT_ID required",
+ );
+const getAuthentikClientSecret = () =>
+ assertExistsAndNonEmptyString(
+ process.env.AUTHENTIK_CLIENT_SECRET,
+ "AUTHENTIK_CLIENT_SECRET required",
+ );
+const getAuthentikRefreshTokenUrl = () =>
+ assertExistsAndNonEmptyString(
+ process.env.AUTHENTIK_REFRESH_TOKEN_URL,
+ "AUTHENTIK_REFRESH_TOKEN_URL required",
+ );
-export const authOptions: AuthOptions = {
- providers: [
- AuthentikProvider({
- clientId: CLIENT_ID,
- clientSecret: CLIENT_SECRET,
- issuer: process.env.AUTHENTIK_ISSUER,
- authorization: {
- params: {
- scope: "openid email profile offline_access",
+export const authOptions = (): AuthOptions =>
+ featureEnabled("requireLogin")
+ ? {
+ providers: [
+ AuthentikProvider({
+ ...(() => {
+ const [clientId, clientSecret] = sequenceThrows(
+ getAuthentikClientId,
+ getAuthentikClientSecret,
+ );
+ return {
+ clientId,
+ clientSecret,
+ };
+ })(),
+ issuer: process.env.AUTHENTIK_ISSUER,
+ authorization: {
+ params: {
+ scope: "openid email profile offline_access",
+ },
+ },
+ }),
+ ],
+ session: {
+ strategy: "jwt",
},
- },
- }),
- ],
- session: {
- strategy: "jwt",
- },
- callbacks: {
- async jwt({ token, account, user }) {
- if (account && !account.access_token) {
- await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
- }
+ callbacks: {
+ async jwt({ token, account, user }) {
+ if (account && !account.access_token) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ }
- if (account && user) {
- // called only on first login
- // XXX account.expires_in used in example is not defined for authentik backend, but expires_at is
- if (account.access_token) {
- const expiresAtS = assertExists(account.expires_at);
- const expiresAtMs = expiresAtS * 1000;
- const jwtToken: JWTWithAccessToken = {
- ...token,
- accessToken: account.access_token,
- accessTokenExpires: expiresAtMs,
- refreshToken: account.refresh_token,
- };
- if (jwtToken.error) {
- await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
- } else {
- assertNotExists(
- jwtToken.error,
- `panic! trying to cache token with error in jwt: ${jwtToken.error}`,
+ if (account && user) {
+ // called only on first login
+ // XXX account.expires_in used in example is not defined for authentik backend, but expires_at is
+ if (account.access_token) {
+ const expiresAtS = assertExists(account.expires_at);
+ const expiresAtMs = expiresAtS * 1000;
+ const jwtToken: JWTWithAccessToken = {
+ ...token,
+ accessToken: account.access_token,
+ accessTokenExpires: expiresAtMs,
+ refreshToken: account.refresh_token,
+ };
+ if (jwtToken.error) {
+ await deleteTokenCache(tokenCacheRedis, `token:${token.sub}`);
+ } else {
+ assertNotExists(
+ jwtToken.error,
+ `panic! trying to cache token with error in jwt: ${jwtToken.error}`,
+ );
+ await setTokenCache(tokenCacheRedis, `token:${token.sub}`, {
+ token: jwtToken,
+ timestamp: Date.now(),
+ });
+ return jwtToken;
+ }
+ }
+ }
+
+ const currentToken = await getTokenCache(
+ tokenCacheRedis,
+ `token:${token.sub}`,
);
- await setTokenCache(tokenCacheRedis, `token:${token.sub}`, {
- token: jwtToken,
- timestamp: Date.now(),
- });
- return jwtToken;
- }
- }
- }
+ console.debug(
+ "currentToken from cache",
+ JSON.stringify(currentToken, null, 2),
+ "will be returned?",
+ currentToken &&
+ !shouldRefreshToken(currentToken.token.accessTokenExpires),
+ );
+ if (
+ currentToken &&
+ !shouldRefreshToken(currentToken.token.accessTokenExpires)
+ ) {
+ return currentToken.token;
+ }
- const currentToken = await getTokenCache(
- tokenCacheRedis,
- `token:${token.sub}`,
- );
- console.debug(
- "currentToken from cache",
- JSON.stringify(currentToken, null, 2),
- "will be returned?",
- currentToken &&
- !shouldRefreshToken(currentToken.token.accessTokenExpires),
- );
- if (
- currentToken &&
- !shouldRefreshToken(currentToken.token.accessTokenExpires)
- ) {
- return currentToken.token;
- }
-
- // access token has expired, try to update it
- return await lockedRefreshAccessToken(token);
- },
- async session({ session, token }) {
- const extendedToken = token as JWTWithAccessToken;
- return {
- ...session,
- accessToken: extendedToken.accessToken,
- accessTokenExpires: extendedToken.accessTokenExpires,
- error: extendedToken.error,
- user: {
- id: assertExists(extendedToken.sub),
- name: extendedToken.name,
- email: extendedToken.email,
+ // access token has expired, try to update it
+ return await lockedRefreshAccessToken(token);
+ },
+ async session({ session, token }) {
+ const extendedToken = token as JWTWithAccessToken;
+ return {
+ ...session,
+ accessToken: extendedToken.accessToken,
+ accessTokenExpires: extendedToken.accessTokenExpires,
+ error: extendedToken.error,
+ user: {
+ id: assertExists(extendedToken.sub),
+ name: extendedToken.name,
+ email: extendedToken.email,
+ },
+ } satisfies CustomSession;
+ },
},
- } satisfies CustomSession;
- },
- },
-};
+ }
+ : {
+ providers: [],
+ };
async function lockedRefreshAccessToken(
token: JWT,
@@ -174,16 +198,19 @@ async function lockedRefreshAccessToken(
}
async function refreshAccessToken(token: JWT): Promise {
+ const [url, clientId, clientSecret] = sequenceThrows(
+ getAuthentikRefreshTokenUrl,
+ getAuthentikClientId,
+ getAuthentikClientSecret,
+ );
try {
- const url = `${process.env.AUTHENTIK_REFRESH_TOKEN_URL}`;
-
const options = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
- client_id: process.env.AUTHENTIK_CLIENT_ID as string,
- client_secret: process.env.AUTHENTIK_CLIENT_SECRET as string,
+ client_id: clientId,
+ client_secret: clientSecret,
grant_type: "refresh_token",
refresh_token: token.refreshToken as string,
}).toString(),
diff --git a/www/app/lib/errorUtils.ts b/www/app/lib/errorUtils.ts
index e9e5300d..1512230c 100644
--- a/www/app/lib/errorUtils.ts
+++ b/www/app/lib/errorUtils.ts
@@ -1,4 +1,6 @@
-function shouldShowError(error: Error | null | undefined) {
+import { isNonEmptyArray, NonEmptyArray } from "./array";
+
+export function shouldShowError(error: Error | null | undefined) {
if (
error?.name == "ResponseError" &&
(error["response"].status == 404 || error["response"].status == 403)
@@ -8,4 +10,40 @@ function shouldShowError(error: Error | null | undefined) {
return true;
}
-export { shouldShowError };
+const defaultMergeErrors = (ex: NonEmptyArray): unknown => {
+ try {
+ return new Error(
+ ex
+ .map((e) =>
+ e ? (e.toString ? e.toString() : JSON.stringify(e)) : `${e}`,
+ )
+ .join("\n"),
+ );
+ } catch (e) {
+ console.error("Error merging errors:", e);
+ return ex[0];
+ }
+};
+
+type ReturnTypes any)[]> = {
+ [K in keyof T]: T[K] extends () => infer R ? R : never;
+};
+
+// sequence semantic for "throws"
+// calls functions passed and collects its thrown values
+export function sequenceThrows any)[]>(
+ ...fs: Fns
+): ReturnTypes {
+ const results: unknown[] = [];
+ const errors: unknown[] = [];
+
+ for (const f of fs) {
+ try {
+ results.push(f());
+ } catch (e) {
+ errors.push(e);
+ }
+ }
+ if (errors.length) throw defaultMergeErrors(errors as NonEmptyArray);
+ return results as ReturnTypes;
+}
diff --git a/www/app/lib/features.ts b/www/app/lib/features.ts
index 86452ae7..7684c8e0 100644
--- a/www/app/lib/features.ts
+++ b/www/app/lib/features.ts
@@ -11,11 +11,11 @@ export type FeatureName = (typeof FEATURES)[number];
export type Features = Readonly>;
export const DEFAULT_FEATURES: Features = {
- requireLogin: false,
+ requireLogin: true,
privacy: true,
- browse: false,
- sendToZulip: false,
- rooms: false,
+ browse: true,
+ sendToZulip: true,
+ rooms: true,
} as const;
function parseBooleanEnv(
diff --git a/www/app/webinars/[title]/page.tsx b/www/app/webinars/[title]/page.tsx
index ab873a6b..51583a2a 100644
--- a/www/app/webinars/[title]/page.tsx
+++ b/www/app/webinars/[title]/page.tsx
@@ -1,5 +1,5 @@
"use client";
-import { useEffect, useState } from "react";
+import { useEffect, useState, use } from "react";
import Link from "next/link";
import Image from "next/image";
import { notFound } from "next/navigation";
@@ -30,9 +30,9 @@ const FORM_FIELDS = {
};
export type WebinarDetails = {
- params: {
+ params: Promise<{
title: string;
- };
+ }>;
};
export type Webinar = {
@@ -63,7 +63,8 @@ const WEBINARS: Webinar[] = [
];
export default function WebinarPage(details: WebinarDetails) {
- const title = details.params.title;
+ const params = use(details.params);
+ const title = params.title;
const webinar = WEBINARS.find((webinar) => webinar.title === title);
if (!webinar) {
return notFound();
diff --git a/www/sentry.client.config.ts b/www/instrumentation-client.ts
similarity index 91%
rename from www/sentry.client.config.ts
rename to www/instrumentation-client.ts
index aff65bbd..5ea5e2e9 100644
--- a/www/sentry.client.config.ts
+++ b/www/instrumentation-client.ts
@@ -23,3 +23,5 @@ if (SENTRY_DSN) {
replaysSessionSampleRate: 0.0,
});
}
+
+export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
diff --git a/www/instrumentation.ts b/www/instrumentation.ts
new file mode 100644
index 00000000..f8a929ba
--- /dev/null
+++ b/www/instrumentation.ts
@@ -0,0 +1,9 @@
+export async function register() {
+ if (process.env.NEXT_RUNTIME === "nodejs") {
+ await import("./sentry.server.config");
+ }
+
+ if (process.env.NEXT_RUNTIME === "edge") {
+ await import("./sentry.edge.config");
+ }
+}
diff --git a/www/next.config.js b/www/next.config.js
index bbc3f710..eedbac7f 100644
--- a/www/next.config.js
+++ b/www/next.config.js
@@ -1,7 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
- experimental: { esmExternals: "loose" },
env: {
IS_CI: process.env.IS_CI,
},
diff --git a/www/package.json b/www/package.json
index e55be4f0..d53c1536 100644
--- a/www/package.json
+++ b/www/package.json
@@ -17,19 +17,19 @@
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
- "@sentry/nextjs": "^7.77.0",
+ "@sentry/nextjs": "^10.11.0",
"@tanstack/react-query": "^5.85.9",
"@types/ioredis": "^5.0.0",
"@whereby.com/browser-sdk": "^3.3.4",
"autoprefixer": "10.4.20",
"axios": "^1.8.2",
"eslint": "^9.33.0",
- "eslint-config-next": "^14.2.31",
+ "eslint-config-next": "^15.5.3",
"fontawesome": "^5.6.3",
"ioredis": "^5.7.0",
"jest-worker": "^29.6.2",
"lucide-react": "^0.525.0",
- "next": "^14.2.30",
+ "next": "^15.5.3",
"next-auth": "^4.24.7",
"next-themes": "^0.4.6",
"nuqs": "^2.4.3",
diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml
index cf9351d4..a4e78972 100644
--- a/www/pnpm-lock.yaml
+++ b/www/pnpm-lock.yaml
@@ -23,8 +23,8 @@ importers:
specifier: ^0.2.0
version: 0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@18.3.1)
"@sentry/nextjs":
- specifier: ^7.77.0
- version: 7.77.0(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)
+ specifier: ^10.11.0
+ version: 10.11.0(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)
"@tanstack/react-query":
specifier: ^5.85.9
version: 5.85.9(react@18.3.1)
@@ -44,8 +44,8 @@ importers:
specifier: ^9.33.0
version: 9.33.0(jiti@1.21.7)
eslint-config-next:
- specifier: ^14.2.31
- version: 14.2.31(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)
+ specifier: ^15.5.3
+ version: 15.5.3(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)
fontawesome:
specifier: ^5.6.3
version: 5.6.3
@@ -59,17 +59,17 @@ importers:
specifier: ^0.525.0
version: 0.525.0(react@18.3.1)
next:
- specifier: ^14.2.30
- version: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ specifier: ^15.5.3
+ version: 15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
next-auth:
specifier: ^4.24.7
- version: 4.24.11(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 4.24.11(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nuqs:
specifier: ^2.4.3
- version: 2.4.3(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
+ version: 2.4.3(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)
openapi-fetch:
specifier: ^0.14.0
version: 0.14.0
@@ -745,6 +745,194 @@ packages:
}
engines: { node: ">=18.18" }
+ "@img/sharp-darwin-arm64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [arm64]
+ os: [darwin]
+
+ "@img/sharp-darwin-x64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [x64]
+ os: [darwin]
+
+ "@img/sharp-libvips-darwin-arm64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==,
+ }
+ cpu: [arm64]
+ os: [darwin]
+
+ "@img/sharp-libvips-darwin-x64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==,
+ }
+ cpu: [x64]
+ os: [darwin]
+
+ "@img/sharp-libvips-linux-arm64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==,
+ }
+ cpu: [arm64]
+ os: [linux]
+
+ "@img/sharp-libvips-linux-arm@1.2.0":
+ resolution:
+ {
+ integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==,
+ }
+ cpu: [arm]
+ os: [linux]
+
+ "@img/sharp-libvips-linux-ppc64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==,
+ }
+ cpu: [ppc64]
+ os: [linux]
+
+ "@img/sharp-libvips-linux-s390x@1.2.0":
+ resolution:
+ {
+ integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==,
+ }
+ cpu: [s390x]
+ os: [linux]
+
+ "@img/sharp-libvips-linux-x64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==,
+ }
+ cpu: [x64]
+ os: [linux]
+
+ "@img/sharp-libvips-linuxmusl-arm64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==,
+ }
+ cpu: [arm64]
+ os: [linux]
+
+ "@img/sharp-libvips-linuxmusl-x64@1.2.0":
+ resolution:
+ {
+ integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==,
+ }
+ cpu: [x64]
+ os: [linux]
+
+ "@img/sharp-linux-arm64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [arm64]
+ os: [linux]
+
+ "@img/sharp-linux-arm@0.34.3":
+ resolution:
+ {
+ integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [arm]
+ os: [linux]
+
+ "@img/sharp-linux-ppc64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [ppc64]
+ os: [linux]
+
+ "@img/sharp-linux-s390x@0.34.3":
+ resolution:
+ {
+ integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [s390x]
+ os: [linux]
+
+ "@img/sharp-linux-x64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [x64]
+ os: [linux]
+
+ "@img/sharp-linuxmusl-arm64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [arm64]
+ os: [linux]
+
+ "@img/sharp-linuxmusl-x64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [x64]
+ os: [linux]
+
+ "@img/sharp-wasm32@0.34.3":
+ resolution:
+ {
+ integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [wasm32]
+
+ "@img/sharp-win32-arm64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [arm64]
+ os: [win32]
+
+ "@img/sharp-win32-ia32@0.34.3":
+ resolution:
+ {
+ integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [ia32]
+ os: [win32]
+
+ "@img/sharp-win32-x64@0.34.3":
+ resolution:
+ {
+ integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+ cpu: [x64]
+ os: [win32]
+
"@internationalized/date@3.8.2":
resolution:
{
@@ -965,6 +1153,12 @@ packages:
integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==,
}
+ "@jridgewell/trace-mapping@0.3.31":
+ resolution:
+ {
+ integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==,
+ }
+
"@jridgewell/trace-mapping@0.3.9":
resolution:
{
@@ -977,94 +1171,85 @@ packages:
integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==,
}
- "@next/env@14.2.31":
+ "@next/env@15.5.3":
resolution:
{
- integrity: sha512-X8VxxYL6VuezrG82h0pUA1V+DuTSJp7Nv15bxq3ivrFqZLjx81rfeHMWOE9T0jm1n3DtHGv8gdn6B0T0kr0D3Q==,
+ integrity: sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==,
}
- "@next/eslint-plugin-next@14.2.31":
+ "@next/eslint-plugin-next@15.5.3":
resolution:
{
- integrity: sha512-ouaB+l8Cr/uzGxoGHUvd01OnfFTM8qM81Crw1AG0xoWDRN0DKLXyTWVe0FdAOHVBpGuXB87aufdRmrwzZDArIw==,
+ integrity: sha512-SdhaKdko6dpsSr0DldkESItVrnPYB1NS2NpShCSX5lc7SSQmLZt5Mug6t2xbiuVWEVDLZSuIAoQyYVBYp0dR5g==,
}
- "@next/swc-darwin-arm64@14.2.31":
+ "@next/swc-darwin-arm64@15.5.3":
resolution:
{
- integrity: sha512-dTHKfaFO/xMJ3kzhXYgf64VtV6MMwDs2viedDOdP+ezd0zWMOQZkxcwOfdcQeQCpouTr9b+xOqMCUXxgLizl8Q==,
+ integrity: sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [darwin]
- "@next/swc-darwin-x64@14.2.31":
+ "@next/swc-darwin-x64@15.5.3":
resolution:
{
- integrity: sha512-iSavebQgeMukUAfjfW8Fi2Iz01t95yxRl2w2wCzjD91h5In9la99QIDKcKSYPfqLjCgwz3JpIWxLG6LM/sxL4g==,
+ integrity: sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [darwin]
- "@next/swc-linux-arm64-gnu@14.2.31":
+ "@next/swc-linux-arm64-gnu@15.5.3":
resolution:
{
- integrity: sha512-XJb3/LURg1u1SdQoopG6jDL2otxGKChH2UYnUTcby4izjM0il7ylBY5TIA7myhvHj9lG5pn9F2nR2s3i8X9awQ==,
+ integrity: sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [linux]
- "@next/swc-linux-arm64-musl@14.2.31":
+ "@next/swc-linux-arm64-musl@15.5.3":
resolution:
{
- integrity: sha512-IInDAcchNCu3BzocdqdCv1bKCmUVO/bKJHnBFTeq3svfaWpOPewaLJ2Lu3GL4yV76c/86ZvpBbG/JJ1lVIs5MA==,
+ integrity: sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [linux]
- "@next/swc-linux-x64-gnu@14.2.31":
+ "@next/swc-linux-x64-gnu@15.5.3":
resolution:
{
- integrity: sha512-YTChJL5/9e4NXPKW+OJzsQa42RiWUNbE+k+ReHvA+lwXk+bvzTsVQboNcezWOuCD+p/J+ntxKOB/81o0MenBhw==,
+ integrity: sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [linux]
- "@next/swc-linux-x64-musl@14.2.31":
+ "@next/swc-linux-x64-musl@15.5.3":
resolution:
{
- integrity: sha512-A0JmD1y4q/9ufOGEAhoa60Sof++X10PEoiWOH0gZ2isufWZeV03NnyRlRmJpRQWGIbRkJUmBo9I3Qz5C10vx4w==,
+ integrity: sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [linux]
- "@next/swc-win32-arm64-msvc@14.2.31":
+ "@next/swc-win32-arm64-msvc@15.5.3":
resolution:
{
- integrity: sha512-nowJ5GbMeDOMzbTm29YqrdrD6lTM8qn2wnZfGpYMY7SZODYYpaJHH1FJXE1l1zWICHR+WfIMytlTDBHu10jb8A==,
+ integrity: sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [win32]
- "@next/swc-win32-ia32-msvc@14.2.31":
+ "@next/swc-win32-x64-msvc@15.5.3":
resolution:
{
- integrity: sha512-pk9Bu4K0015anTS1OS9d/SpS0UtRObC+xe93fwnm7Gvqbv/W1ZbzhK4nvc96RURIQOux3P/bBH316xz8wjGSsA==,
- }
- engines: { node: ">= 10" }
- cpu: [ia32]
- os: [win32]
-
- "@next/swc-win32-x64-msvc@14.2.31":
- resolution:
- {
- integrity: sha512-LwFZd4JFnMHGceItR9+jtlMm8lGLU/IPkgjBBgYmdYSfalbHCiDpjMYtgDQ2wtwiAOSJOCyFI4m8PikrsDyA6Q==,
+ integrity: sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==,
}
engines: { node: ">= 10" }
cpu: [x64]
@@ -1098,6 +1283,27 @@ packages:
}
engines: { node: ">=12.4.0" }
+ "@opentelemetry/api-logs@0.203.0":
+ resolution:
+ {
+ integrity: sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==,
+ }
+ engines: { node: ">=8.0.0" }
+
+ "@opentelemetry/api-logs@0.204.0":
+ resolution:
+ {
+ integrity: sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw==,
+ }
+ engines: { node: ">=8.0.0" }
+
+ "@opentelemetry/api-logs@0.57.2":
+ resolution:
+ {
+ integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==,
+ }
+ engines: { node: ">=14" }
+
"@opentelemetry/api@1.9.0":
resolution:
{
@@ -1105,6 +1311,299 @@ packages:
}
engines: { node: ">=8.0.0" }
+ "@opentelemetry/context-async-hooks@2.1.0":
+ resolution:
+ {
+ integrity: sha512-zOyetmZppnwTyPrt4S7jMfXiSX9yyfF0hxlA8B5oo2TtKl+/RGCy7fi4DrBfIf3lCPrkKsRBWZZD7RFojK7FDg==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+
+ "@opentelemetry/core@2.0.1":
+ resolution:
+ {
+ integrity: sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+
+ "@opentelemetry/core@2.1.0":
+ resolution:
+ {
+ integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+
+ "@opentelemetry/instrumentation-amqplib@0.50.0":
+ resolution:
+ {
+ integrity: sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-connect@0.47.0":
+ resolution:
+ {
+ integrity: sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-dataloader@0.21.1":
+ resolution:
+ {
+ integrity: sha512-hNAm/bwGawLM8VDjKR0ZUDJ/D/qKR3s6lA5NV+btNaPVm2acqhPcT47l2uCVi+70lng2mywfQncor9v8/ykuyw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-express@0.52.0":
+ resolution:
+ {
+ integrity: sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-fs@0.23.0":
+ resolution:
+ {
+ integrity: sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-generic-pool@0.47.0":
+ resolution:
+ {
+ integrity: sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-graphql@0.51.0":
+ resolution:
+ {
+ integrity: sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-hapi@0.50.0":
+ resolution:
+ {
+ integrity: sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-http@0.203.0":
+ resolution:
+ {
+ integrity: sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-ioredis@0.52.0":
+ resolution:
+ {
+ integrity: sha512-rUvlyZwI90HRQPYicxpDGhT8setMrlHKokCtBtZgYxQWRF5RBbG4q0pGtbZvd7kyseuHbFpA3I/5z7M8b/5ywg==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-kafkajs@0.13.0":
+ resolution:
+ {
+ integrity: sha512-FPQyJsREOaGH64hcxlzTsIEQC4DYANgTwHjiB7z9lldmvua1LRMVn3/FfBlzXoqF179B0VGYviz6rn75E9wsDw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-knex@0.48.0":
+ resolution:
+ {
+ integrity: sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-koa@0.51.0":
+ resolution:
+ {
+ integrity: sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-lru-memoizer@0.48.0":
+ resolution:
+ {
+ integrity: sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-mongodb@0.56.0":
+ resolution:
+ {
+ integrity: sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-mongoose@0.50.0":
+ resolution:
+ {
+ integrity: sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-mysql2@0.50.0":
+ resolution:
+ {
+ integrity: sha512-PoOMpmq73rOIE3nlTNLf3B1SyNYGsp7QXHYKmeTZZnJ2Ou7/fdURuOhWOI0e6QZ5gSem18IR1sJi6GOULBQJ9g==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-mysql@0.49.0":
+ resolution:
+ {
+ integrity: sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-pg@0.55.0":
+ resolution:
+ {
+ integrity: sha512-yfJ5bYE7CnkW/uNsnrwouG/FR7nmg09zdk2MSs7k0ZOMkDDAE3WBGpVFFApGgNu2U+gtzLgEzOQG4I/X+60hXw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-redis@0.51.0":
+ resolution:
+ {
+ integrity: sha512-uL/GtBA0u72YPPehwOvthAe+Wf8k3T+XQPBssJmTYl6fzuZjNq8zTfxVFhl9nRFjFVEe+CtiYNT0Q3AyqW1Z0A==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-tedious@0.22.0":
+ resolution:
+ {
+ integrity: sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation-undici@0.14.0":
+ resolution:
+ {
+ integrity: sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.7.0
+
+ "@opentelemetry/instrumentation@0.203.0":
+ resolution:
+ {
+ integrity: sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation@0.204.0":
+ resolution:
+ {
+ integrity: sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/instrumentation@0.57.2":
+ resolution:
+ {
+ integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==,
+ }
+ engines: { node: ">=14" }
+ peerDependencies:
+ "@opentelemetry/api": ^1.3.0
+
+ "@opentelemetry/redis-common@0.38.0":
+ resolution:
+ {
+ integrity: sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+
+ "@opentelemetry/resources@2.1.0":
+ resolution:
+ {
+ integrity: sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+
+ "@opentelemetry/sdk-trace-base@2.1.0":
+ resolution:
+ {
+ integrity: sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+
+ "@opentelemetry/semantic-conventions@1.37.0":
+ resolution:
+ {
+ integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==,
+ }
+ engines: { node: ">=14" }
+
+ "@opentelemetry/sql-common@0.41.0":
+ resolution:
+ {
+ integrity: sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==,
+ }
+ engines: { node: ^18.19.0 || >=20.6.0 }
+ peerDependencies:
+ "@opentelemetry/api": ^1.1.0
+
"@pandacss/is-valid-prop@0.54.0":
resolution:
{
@@ -1255,6 +1754,14 @@ packages:
}
engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 }
+ "@prisma/instrumentation@6.14.0":
+ resolution:
+ {
+ integrity: sha512-Po/Hry5bAeunRDq0yAQueKookW3glpP+qjjvvyOfm6dI2KG5/Y6Bgg3ahyWd7B0u2E+Wf9xRk2rtdda7ySgK1A==,
+ }
+ peerDependencies:
+ "@opentelemetry/api": ^1.8
+
"@radix-ui/primitive@1.1.3":
resolution:
{
@@ -1572,14 +2079,14 @@ packages:
react-redux:
optional: true
- "@rollup/plugin-commonjs@24.0.0":
+ "@rollup/plugin-commonjs@28.0.1":
resolution:
{
- integrity: sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g==,
+ integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==,
}
- engines: { node: ">=14.0.0" }
+ engines: { node: ">=16.0.0 || 14 >= 14.17" }
peerDependencies:
- rollup: ^2.68.0||^3.0.0
+ rollup: ^2.68.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
@@ -1596,6 +2103,174 @@ packages:
rollup:
optional: true
+ "@rollup/rollup-android-arm-eabi@4.50.1":
+ resolution:
+ {
+ integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==,
+ }
+ cpu: [arm]
+ os: [android]
+
+ "@rollup/rollup-android-arm64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==,
+ }
+ cpu: [arm64]
+ os: [android]
+
+ "@rollup/rollup-darwin-arm64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==,
+ }
+ cpu: [arm64]
+ os: [darwin]
+
+ "@rollup/rollup-darwin-x64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==,
+ }
+ cpu: [x64]
+ os: [darwin]
+
+ "@rollup/rollup-freebsd-arm64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==,
+ }
+ cpu: [arm64]
+ os: [freebsd]
+
+ "@rollup/rollup-freebsd-x64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==,
+ }
+ cpu: [x64]
+ os: [freebsd]
+
+ "@rollup/rollup-linux-arm-gnueabihf@4.50.1":
+ resolution:
+ {
+ integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==,
+ }
+ cpu: [arm]
+ os: [linux]
+
+ "@rollup/rollup-linux-arm-musleabihf@4.50.1":
+ resolution:
+ {
+ integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==,
+ }
+ cpu: [arm]
+ os: [linux]
+
+ "@rollup/rollup-linux-arm64-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==,
+ }
+ cpu: [arm64]
+ os: [linux]
+
+ "@rollup/rollup-linux-arm64-musl@4.50.1":
+ resolution:
+ {
+ integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==,
+ }
+ cpu: [arm64]
+ os: [linux]
+
+ "@rollup/rollup-linux-loongarch64-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==,
+ }
+ cpu: [loong64]
+ os: [linux]
+
+ "@rollup/rollup-linux-ppc64-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==,
+ }
+ cpu: [ppc64]
+ os: [linux]
+
+ "@rollup/rollup-linux-riscv64-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==,
+ }
+ cpu: [riscv64]
+ os: [linux]
+
+ "@rollup/rollup-linux-riscv64-musl@4.50.1":
+ resolution:
+ {
+ integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==,
+ }
+ cpu: [riscv64]
+ os: [linux]
+
+ "@rollup/rollup-linux-s390x-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==,
+ }
+ cpu: [s390x]
+ os: [linux]
+
+ "@rollup/rollup-linux-x64-gnu@4.50.1":
+ resolution:
+ {
+ integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==,
+ }
+ cpu: [x64]
+ os: [linux]
+
+ "@rollup/rollup-linux-x64-musl@4.50.1":
+ resolution:
+ {
+ integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==,
+ }
+ cpu: [x64]
+ os: [linux]
+
+ "@rollup/rollup-openharmony-arm64@4.50.1":
+ resolution:
+ {
+ integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==,
+ }
+ cpu: [arm64]
+ os: [openharmony]
+
+ "@rollup/rollup-win32-arm64-msvc@4.50.1":
+ resolution:
+ {
+ integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==,
+ }
+ cpu: [arm64]
+ os: [win32]
+
+ "@rollup/rollup-win32-ia32-msvc@4.50.1":
+ resolution:
+ {
+ integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==,
+ }
+ cpu: [ia32]
+ os: [win32]
+
+ "@rollup/rollup-win32-x64-msvc@4.50.1":
+ resolution:
+ {
+ integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==,
+ }
+ cpu: [x64]
+ os: [win32]
+
"@rtsao/scc@1.1.0":
resolution:
{
@@ -1608,106 +2283,209 @@ packages:
integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==,
}
- "@sentry-internal/tracing@7.77.0":
+ "@sentry-internal/browser-utils@10.11.0":
resolution:
{
- integrity: sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==,
+ integrity: sha512-fnMlz5ntap6x4vRsLOHwPqXh7t82StgAiRt+EaqcMX0t9l8C0w0df8qwrONKXvE5GdHWTNFJj5qR15FERSkg3Q==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
- "@sentry/browser@7.77.0":
+ "@sentry-internal/feedback@10.11.0":
resolution:
{
- integrity: sha512-nJ2KDZD90H8jcPx9BysQLiQW+w7k7kISCWeRjrEMJzjtge32dmHA8G4stlUTRIQugy5F+73cOayWShceFP7QJQ==,
+ integrity: sha512-ADey51IIaa29kepb8B7aSgSGSrcyT7QZdRsN1rhitefzrruHzpSUci5c2EPIvmWfKJq8Wnvukm9BHXZXAAIOzA==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
- "@sentry/cli@1.77.3":
+ "@sentry-internal/replay-canvas@10.11.0":
resolution:
{
- integrity: sha512-c3eDqcDRmy4TFz2bFU5Y6QatlpoBPPa8cxBooaS4aMQpnIdLYPF1xhyyiW0LQlDUNc3rRjNF7oN5qKoaRoMTQQ==,
+ integrity: sha512-brWQ90IYQyZr44IpTprlmvbtz4l2ABzLdpP94Egh12Onf/q6n4CjLKaA25N5kX0uggHqX1Rs7dNaG0mP3ETHhA==,
}
- engines: { node: ">= 8" }
+ engines: { node: ">=18" }
+
+ "@sentry-internal/replay@10.11.0":
+ resolution:
+ {
+ integrity: sha512-t4M2bxMp2rKGK/l7bkVWjN+xVw9H9V12jAeXmO/Fskz2RcG1ZNLQnKSx/W/zCRMk8k7xOQFsfiApq+zDN+ziKA==,
+ }
+ engines: { node: ">=18" }
+
+ "@sentry/babel-plugin-component-annotate@4.3.0":
+ resolution:
+ {
+ integrity: sha512-OuxqBprXRyhe8Pkfyz/4yHQJc5c3lm+TmYWSSx8u48g5yKewSQDOxkiLU5pAk3WnbLPy8XwU/PN+2BG0YFU9Nw==,
+ }
+ engines: { node: ">= 14" }
+
+ "@sentry/browser@10.11.0":
+ resolution:
+ {
+ integrity: sha512-qemaKCJKJHHCyGBpdLq23xL5u9Xvir20XN7YFTnHcEq4Jvj0GoWsslxKi5cQB2JvpYn62WxTiDgVLeQlleZhSg==,
+ }
+ engines: { node: ">=18" }
+
+ "@sentry/bundler-plugin-core@4.3.0":
+ resolution:
+ {
+ integrity: sha512-dmR4DJhJ4jqVWGWppuTL2blNFqOZZnt4aLkewbD1myFG3KVfUx8CrMQWEmGjkgPOtj5TO6xH9PyTJjXC6o5tnA==,
+ }
+ engines: { node: ">= 14" }
+
+ "@sentry/cli-darwin@2.53.0":
+ resolution:
+ {
+ integrity: sha512-NNPfpILMwKgpHiyJubHHuauMKltkrgLQ5tvMdxNpxY60jBNdo5VJtpESp4XmXlnidzV4j1z61V4ozU6ttDgt5Q==,
+ }
+ engines: { node: ">=10" }
+ os: [darwin]
+
+ "@sentry/cli-linux-arm64@2.53.0":
+ resolution:
+ {
+ integrity: sha512-xY/CZ1dVazsSCvTXzKpAgXaRqfljVfdrFaYZRUaRPf1ZJRGa3dcrivoOhSIeG/p5NdYtMvslMPY9Gm2MT0M83A==,
+ }
+ engines: { node: ">=10" }
+ cpu: [arm64]
+ os: [linux, freebsd, android]
+
+ "@sentry/cli-linux-arm@2.53.0":
+ resolution:
+ {
+ integrity: sha512-NdRzQ15Ht83qG0/Lyu11ciy/Hu/oXbbtJUgwzACc7bWvHQA8xEwTsehWexqn1529Kfc5EjuZ0Wmj3MHmp+jOWw==,
+ }
+ engines: { node: ">=10" }
+ cpu: [arm]
+ os: [linux, freebsd, android]
+
+ "@sentry/cli-linux-i686@2.53.0":
+ resolution:
+ {
+ integrity: sha512-0REmBibGAB4jtqt9S6JEsFF4QybzcXHPcHtJjgMi5T0ueh952uG9wLzjSxQErCsxTKF+fL8oG0Oz5yKBuCwCCQ==,
+ }
+ engines: { node: ">=10" }
+ cpu: [x86, ia32]
+ os: [linux, freebsd, android]
+
+ "@sentry/cli-linux-x64@2.53.0":
+ resolution:
+ {
+ integrity: sha512-9UGJL+Vy5N/YL1EWPZ/dyXLkShlNaDNrzxx4G7mTS9ywjg+BIuemo6rnN7w43K1NOjObTVO6zY0FwumJ1pCyLg==,
+ }
+ engines: { node: ">=10" }
+ cpu: [x64]
+ os: [linux, freebsd, android]
+
+ "@sentry/cli-win32-arm64@2.53.0":
+ resolution:
+ {
+ integrity: sha512-G1kjOjrjMBY20rQcJV2GA8KQE74ufmROCDb2GXYRfjvb1fKAsm4Oh8N5+Tqi7xEHdjQoLPkE4CNW0aH68JSUDQ==,
+ }
+ engines: { node: ">=10" }
+ cpu: [arm64]
+ os: [win32]
+
+ "@sentry/cli-win32-i686@2.53.0":
+ resolution:
+ {
+ integrity: sha512-qbGTZUzesuUaPtY9rPXdNfwLqOZKXrJRC1zUFn52hdo6B+Dmv0m/AHwRVFHZP53Tg1NCa8bDei2K/uzRN0dUZw==,
+ }
+ engines: { node: ">=10" }
+ cpu: [x86, ia32]
+ os: [win32]
+
+ "@sentry/cli-win32-x64@2.53.0":
+ resolution:
+ {
+ integrity: sha512-1TXYxYHtwgUq5KAJt3erRzzUtPqg7BlH9T7MdSPHjJatkrr/kwZqnVe2H6Arr/5NH891vOlIeSPHBdgJUAD69g==,
+ }
+ engines: { node: ">=10" }
+ cpu: [x64]
+ os: [win32]
+
+ "@sentry/cli@2.53.0":
+ resolution:
+ {
+ integrity: sha512-n2ZNb+5Z6AZKQSI0SusQ7ZzFL637mfw3Xh4C3PEyVSn9LiF683fX0TTq8OeGmNZQS4maYfS95IFD+XpydU0dEA==,
+ }
+ engines: { node: ">= 10" }
hasBin: true
- "@sentry/core@7.77.0":
+ "@sentry/core@10.11.0":
resolution:
{
- integrity: sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==,
+ integrity: sha512-39Rxn8cDXConx3+SKOCAhW+/hklM7UDaz+U1OFzFMDlT59vXSpfI6bcXtNiFDrbOxlQ2hX8yAqx8YRltgSftoA==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
- "@sentry/integrations@7.77.0":
+ "@sentry/nextjs@10.11.0":
resolution:
{
- integrity: sha512-P055qXgBHeZNKnnVEs5eZYLdy6P49Zr77A1aWJuNih/EenzMy922GOeGy2mF6XYrn1YJSjEwsNMNsQkcvMTK8Q==,
+ integrity: sha512-oMRmRW982H6kNlUHNij5QAro8Kbi43r3VrcrKtrx7LgjHOUTFUvZmJeynC+T+PcMgLhQNvCC3JgzOhfSqxOChg==,
}
- engines: { node: ">=8" }
-
- "@sentry/nextjs@7.77.0":
- resolution:
- {
- integrity: sha512-8tYPBt5luFjrng1sAMJqNjM9sq80q0jbt6yariADU9hEr7Zk8YqFaOI2/Q6yn9dZ6XyytIRtLEo54kk2AO94xw==,
- }
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
peerDependencies:
- next: ^10.0.8 || ^11.0 || ^12.0 || ^13.0 || ^14.0
- react: 16.x || 17.x || 18.x
- webpack: ">= 4.0.0"
- peerDependenciesMeta:
- webpack:
- optional: true
+ next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0
- "@sentry/node@7.77.0":
+ "@sentry/node-core@10.11.0":
resolution:
{
- integrity: sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g==,
+ integrity: sha512-dkVZ06F+W5W0CsD47ATTTOTTocmccT/ezrF9idspQq+HVOcjoKSU60WpWo22NjtVNdSYKLnom0q1LKRoaRA/Ww==,
}
- engines: { node: ">=8" }
-
- "@sentry/react@7.77.0":
- resolution:
- {
- integrity: sha512-Q+htKzib5em0MdaQZMmPomaswaU3xhcVqmLi2CxqQypSjbYgBPPd+DuhrXKoWYLDDkkbY2uyfe4Lp3yLRWeXYw==,
- }
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
peerDependencies:
- react: 15.x || 16.x || 17.x || 18.x
+ "@opentelemetry/api": ^1.9.0
+ "@opentelemetry/context-async-hooks": ^1.30.1 || ^2.0.0
+ "@opentelemetry/core": ^1.30.1 || ^2.0.0
+ "@opentelemetry/instrumentation": ">=0.57.1 <1"
+ "@opentelemetry/resources": ^1.30.1 || ^2.0.0
+ "@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.0.0
+ "@opentelemetry/semantic-conventions": ^1.34.0
- "@sentry/replay@7.77.0":
+ "@sentry/node@10.11.0":
resolution:
{
- integrity: sha512-M9Ik2J5ekl+C1Och3wzLRZVaRGK33BlnBwfwf3qKjgLDwfKW+1YkwDfTHbc2b74RowkJbOVNcp4m8ptlehlSaQ==,
+ integrity: sha512-Tbcjr3iQAEjYi7/QIpdS8afv/LU1TwDTiy5x87MSpVEoeFcZ7f2iFC4GV0fhB3p4qDuFdL2JGVsIIrzapp8Y4A==,
}
- engines: { node: ">=12" }
+ engines: { node: ">=18" }
- "@sentry/types@7.77.0":
+ "@sentry/opentelemetry@10.11.0":
resolution:
{
- integrity: sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==,
+ integrity: sha512-BY2SsVlRKICzNUO9atUy064BZqYnhV5A/O+JjEx0kj7ylq+oZd++zmGkks00rSwaJE220cVcVhpwqxcFUpc2hw==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
+ peerDependencies:
+ "@opentelemetry/api": ^1.9.0
+ "@opentelemetry/context-async-hooks": ^1.30.1 || ^2.0.0
+ "@opentelemetry/core": ^1.30.1 || ^2.0.0
+ "@opentelemetry/sdk-trace-base": ^1.30.1 || ^2.0.0
+ "@opentelemetry/semantic-conventions": ^1.34.0
- "@sentry/utils@7.77.0":
+ "@sentry/react@10.11.0":
resolution:
{
- integrity: sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==,
+ integrity: sha512-bE4lJ5Ni/n9JUdLWGG99yucY0/zOUXjKl9gfSTkvUvOiAIX/bY0Y4WgOqeWySvbMz679ZdOwF34k8RA/gI7a8g==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
+ peerDependencies:
+ react: ^16.14.0 || 17.x || 18.x || 19.x
- "@sentry/vercel-edge@7.77.0":
+ "@sentry/vercel-edge@10.11.0":
resolution:
{
- integrity: sha512-ffddPCgxVeAccPYuH5sooZeHBqDuJ9OIhIRYKoDi4TvmwAzWo58zzZWhRpkHqHgIQdQvhLVZ5F+FSQVWnYSOkw==,
+ integrity: sha512-jAsJ8RbbF2JWj2wnXfd6BwWxCR6GBITMtlaoWc7pG22HknEtoH15dKsQC3Ew5r/KRcofr2e+ywdnBn5CPr1Pbg==,
}
- engines: { node: ">=8" }
+ engines: { node: ">=18" }
- "@sentry/webpack-plugin@1.20.0":
+ "@sentry/webpack-plugin@4.3.0":
resolution:
{
- integrity: sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==,
+ integrity: sha512-K4nU1SheK/tvyakBws2zfd+MN6hzmpW+wPTbSbDWn1+WL9+g9hsPh8hjFFiVe47AhhUoUZ3YgiH2HyeHXjHflA==,
}
- engines: { node: ">= 8" }
+ engines: { node: ">= 14" }
+ peerDependencies:
+ webpack: ">=4.40.0"
"@sinclair/typebox@0.27.8":
resolution:
@@ -1751,10 +2529,10 @@ packages:
integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==,
}
- "@swc/counter@0.1.3":
+ "@swc/helpers@0.5.15":
resolution:
{
- integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==,
+ integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==,
}
"@swc/helpers@0.5.17":
@@ -1763,12 +2541,6 @@ packages:
integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==,
}
- "@swc/helpers@0.5.5":
- resolution:
- {
- integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==,
- }
-
"@tanstack/query-core@5.85.9":
resolution:
{
@@ -1837,6 +2609,12 @@ packages:
integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==,
}
+ "@types/connect@3.4.38":
+ resolution:
+ {
+ integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==,
+ }
+
"@types/debug@4.1.12":
resolution:
{
@@ -1934,6 +2712,12 @@ packages:
integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==,
}
+ "@types/mysql@2.15.27":
+ resolution:
+ {
+ integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==,
+ }
+
"@types/node-fetch@2.6.13":
resolution:
{
@@ -1946,12 +2730,30 @@ packages:
integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==,
}
+ "@types/node@24.3.1":
+ resolution:
+ {
+ integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==,
+ }
+
"@types/parse-json@4.0.2":
resolution:
{
integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==,
}
+ "@types/pg-pool@2.0.6":
+ resolution:
+ {
+ integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==,
+ }
+
+ "@types/pg@8.15.4":
+ resolution:
+ {
+ integrity: sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==,
+ }
+
"@types/prop-types@15.7.15":
resolution:
{
@@ -1970,12 +2772,24 @@ packages:
integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==,
}
+ "@types/shimmer@1.2.0":
+ resolution:
+ {
+ integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==,
+ }
+
"@types/stack-utils@2.0.3":
resolution:
{
integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==,
}
+ "@types/tedious@4.0.14":
+ resolution:
+ {
+ integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==,
+ }
+
"@types/ua-parser-js@0.7.39":
resolution:
{
@@ -2830,6 +3644,14 @@ packages:
integrity: sha512-yI/CZizbk387TdkDCy9Uc4l53uaeQuWAIJESrmAwwq6yMNbHZ2dm5+1NHdZr/guES5TgyJa/BYJsNJeCsCfesg==,
}
+ acorn-import-attributes@1.9.5:
+ resolution:
+ {
+ integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==,
+ }
+ peerDependencies:
+ acorn: ^8
+
acorn-import-phases@1.0.4:
resolution:
{
@@ -3264,13 +4086,6 @@ packages:
integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==,
}
- busboy@1.6.0:
- resolution:
- {
- integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==,
- }
- engines: { node: ">=10.16.0" }
-
call-bind-apply-helpers@1.0.2:
resolution:
{
@@ -3424,6 +4239,12 @@ packages:
}
engines: { node: ">=8" }
+ cjs-module-lexer@1.4.3:
+ resolution:
+ {
+ integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==,
+ }
+
cjs-module-lexer@2.1.0:
resolution:
{
@@ -3489,6 +4310,19 @@ packages:
integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==,
}
+ color-string@1.9.1:
+ resolution:
+ {
+ integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==,
+ }
+
+ color@4.2.3:
+ resolution:
+ {
+ integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==,
+ }
+ engines: { node: ">=12.5.0" }
+
colorette@1.4.0:
resolution:
{
@@ -3727,6 +4561,13 @@ packages:
engines: { node: ">=0.10" }
hasBin: true
+ detect-libc@2.0.4:
+ resolution:
+ {
+ integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==,
+ }
+ engines: { node: ">=8" }
+
detect-newline@3.1.0:
resolution:
{
@@ -3790,6 +4631,13 @@ packages:
integrity: sha512-h7g5eduvnLwowJJPkcB5lNzo8vd/Hx4e3I4IOtLpX0qB2wBiuryGLNa61MeFre4b6gMaQIhegMIZ2I8rQCAJwQ==,
}
+ dotenv@16.6.1:
+ resolution:
+ {
+ integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==,
+ }
+ engines: { node: ">=12" }
+
dunder-proto@1.0.1:
resolution:
{
@@ -3943,13 +4791,13 @@ packages:
}
engines: { node: ">=10" }
- eslint-config-next@14.2.31:
+ eslint-config-next@15.5.3:
resolution:
{
- integrity: sha512-sT32j4678je7SWstBM6l0kE2L+LSgAARDAxw8iloNhI4/8xwkdDesbrGCPaGWzQv+dD6f6adhB+eRSThpGkBdg==,
+ integrity: sha512-e6j+QhQFOr5pfsc8VJbuTD9xTXJaRvMHYjEeLPA2pFkheNlgPLCkxdvhxhfuM4KGcqSZj2qEnpHisdTVs3BxuQ==,
}
peerDependencies:
- eslint: ^7.23.0 || ^8.0.0
+ eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
typescript: ">=3.3.1"
peerDependenciesMeta:
typescript:
@@ -4023,14 +4871,14 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
- eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705:
+ eslint-plugin-react-hooks@5.2.0:
resolution:
{
- integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==,
+ integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==,
}
engines: { node: ">=10" }
peerDependencies:
- eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
eslint-plugin-react@7.37.5:
resolution:
@@ -4197,6 +5045,13 @@ packages:
integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==,
}
+ fast-glob@3.3.1:
+ resolution:
+ {
+ integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==,
+ }
+ engines: { node: ">=8.6.0" }
+
fast-glob@3.3.3:
resolution:
{
@@ -4337,6 +5192,12 @@ packages:
}
engines: { node: ">= 6" }
+ forwarded-parse@2.1.2:
+ resolution:
+ {
+ integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==,
+ }
+
fraction.js@4.3.7:
resolution:
{
@@ -4464,14 +5325,6 @@ packages:
integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==,
}
- glob@10.3.10:
- resolution:
- {
- integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==,
- }
- engines: { node: ">=16 || 14 >=14.17" }
- hasBin: true
-
glob@10.4.5:
resolution:
{
@@ -4486,13 +5339,12 @@ packages:
}
deprecated: Glob versions prior to v9 are no longer supported
- glob@8.1.0:
+ glob@9.3.5:
resolution:
{
- integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==,
+ integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==,
}
- engines: { node: ">=12" }
- deprecated: Glob versions prior to v9 are no longer supported
+ engines: { node: ">=16 || 14 >=14.17" }
globals@14.0.0:
resolution:
@@ -4673,12 +5525,6 @@ packages:
}
engines: { node: ">= 4" }
- immediate@3.0.6:
- resolution:
- {
- integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==,
- }
-
immer@10.1.1:
resolution:
{
@@ -4698,6 +5544,12 @@ packages:
}
engines: { node: ">=6" }
+ import-in-the-middle@1.14.2:
+ resolution:
+ {
+ integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==,
+ }
+
import-local@3.2.0:
resolution:
{
@@ -4798,6 +5650,12 @@ packages:
integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==,
}
+ is-arrayish@0.3.2:
+ resolution:
+ {
+ integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==,
+ }
+
is-async-function@2.1.1:
resolution:
{
@@ -5085,13 +5943,6 @@ packages:
}
engines: { node: ">= 0.4" }
- jackspeak@2.3.6:
- resolution:
- {
- integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==,
- }
- engines: { node: ">=14" }
-
jackspeak@3.4.3:
resolution:
{
@@ -5461,12 +6312,6 @@ packages:
}
engines: { node: ">= 0.8.0" }
- lie@3.1.1:
- resolution:
- {
- integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==,
- }
-
lighterhtml@4.2.0:
resolution:
{
@@ -5493,12 +6338,6 @@ packages:
}
engines: { node: ">=6.11.5" }
- localforage@1.10.0:
- resolution:
- {
- integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==,
- }
-
locate-path@5.0.0:
resolution:
{
@@ -5577,10 +6416,16 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
- magic-string@0.27.0:
+ magic-string@0.30.19:
resolution:
{
- integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==,
+ integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==,
+ }
+
+ magic-string@0.30.8:
+ resolution:
+ {
+ integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==,
}
engines: { node: ">=12" }
@@ -5845,6 +6690,13 @@ packages:
}
engines: { node: ">=10" }
+ minimatch@8.0.4:
+ resolution:
+ {
+ integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==,
+ }
+ engines: { node: ">=16 || 14 >=14.17" }
+
minimatch@9.0.5:
resolution:
{
@@ -5858,6 +6710,13 @@ packages:
integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==,
}
+ minipass@4.2.8:
+ resolution:
+ {
+ integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==,
+ }
+ engines: { node: ">=8" }
+
minipass@7.1.2:
resolution:
{
@@ -5871,12 +6730,11 @@ packages:
integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==,
}
- mkdirp@0.5.6:
+ module-details-from-path@1.0.4:
resolution:
{
- integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==,
+ integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==,
}
- hasBin: true
ms@2.1.3:
resolution:
@@ -5944,24 +6802,27 @@ packages:
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
- next@14.2.31:
+ next@15.5.3:
resolution:
{
- integrity: sha512-Wyw1m4t8PhqG+or5a1U/Deb888YApC4rAez9bGhHkTsfwAy4SWKVro0GhEx4sox1856IbLhvhce2hAA6o8vkog==,
+ integrity: sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==,
}
- engines: { node: ">=18.17.0" }
+ engines: { node: ^18.18.0 || ^19.8.0 || >= 20.0.0 }
hasBin: true
peerDependencies:
"@opentelemetry/api": ^1.1.0
- "@playwright/test": ^1.41.2
- react: ^18.2.0
- react-dom: ^18.2.0
+ "@playwright/test": ^1.51.1
+ babel-plugin-react-compiler: "*"
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
"@opentelemetry/api":
optional: true
"@playwright/test":
optional: true
+ babel-plugin-react-compiler:
+ optional: true
sass:
optional: true
@@ -6304,6 +7165,26 @@ packages:
integrity: sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==,
}
+ pg-int8@1.0.1:
+ resolution:
+ {
+ integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==,
+ }
+ engines: { node: ">=4.0.0" }
+
+ pg-protocol@1.10.3:
+ resolution:
+ {
+ integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==,
+ }
+
+ pg-types@2.2.0:
+ resolution:
+ {
+ integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==,
+ }
+ engines: { node: ">=4" }
+
picocolors@1.1.1:
resolution:
{
@@ -6428,6 +7309,34 @@ packages:
}
engines: { node: ^10 || ^12 || >=14 }
+ postgres-array@2.0.0:
+ resolution:
+ {
+ integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==,
+ }
+ engines: { node: ">=4" }
+
+ postgres-bytea@1.0.0:
+ resolution:
+ {
+ integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==,
+ }
+ engines: { node: ">=0.10.0" }
+
+ postgres-date@1.0.7:
+ resolution:
+ {
+ integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==,
+ }
+ engines: { node: ">=0.10.0" }
+
+ postgres-interval@1.2.0:
+ resolution:
+ {
+ integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==,
+ }
+ engines: { node: ">=0.10.0" }
+
preact-render-to-string@5.2.6:
resolution:
{
@@ -6750,6 +7659,13 @@ packages:
}
engines: { node: ">=0.10.0" }
+ require-in-the-middle@7.5.2:
+ resolution:
+ {
+ integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==,
+ }
+ engines: { node: ">=8.6.0" }
+
reraf@1.1.1:
resolution:
{
@@ -6818,12 +7734,12 @@ packages:
}
engines: { iojs: ">=1.0.0", node: ">=0.10.0" }
- rollup@2.78.0:
+ rollup@4.50.1:
resolution:
{
- integrity: sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==,
+ integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==,
}
- engines: { node: ">=10.0.0" }
+ engines: { node: ">=18.0.0", npm: ">=8.0.0" }
hasBin: true
rtcstats@https://codeload.github.com/whereby/rtcstats/tar.gz/63bcb6420d76d34161b39e494524ae73aa6dd70d:
@@ -6949,6 +7865,13 @@ packages:
}
engines: { node: ">= 0.4" }
+ sharp@0.34.3:
+ resolution:
+ {
+ integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==,
+ }
+ engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 }
+
shebang-command@2.0.0:
resolution:
{
@@ -6963,6 +7886,12 @@ packages:
}
engines: { node: ">=8" }
+ shimmer@1.2.1:
+ resolution:
+ {
+ integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==,
+ }
+
side-channel-list@1.0.0:
resolution:
{
@@ -7010,6 +7939,12 @@ packages:
integrity: sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==,
}
+ simple-swizzle@0.2.2:
+ resolution:
+ {
+ integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==,
+ }
+
slash@3.0.0:
resolution:
{
@@ -7115,13 +8050,6 @@ packages:
}
engines: { node: ">= 0.4" }
- streamsearch@1.1.0:
- resolution:
- {
- integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==,
- }
- engines: { node: ">=10.0.0" }
-
string-length@4.0.2:
resolution:
{
@@ -7250,16 +8178,16 @@ packages:
integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==,
}
- styled-jsx@5.1.1:
+ styled-jsx@5.1.6:
resolution:
{
- integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==,
+ integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==,
}
engines: { node: ">= 12.0.0" }
peerDependencies:
"@babel/core": "*"
babel-plugin-macros: "*"
- react: ">= 16.8.0 || 17.x.x || ^18.0.0-0"
+ react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
peerDependenciesMeta:
"@babel/core":
optional: true
@@ -7674,6 +8602,12 @@ packages:
integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==,
}
+ unplugin@1.0.1:
+ resolution:
+ {
+ integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==,
+ }
+
unrs-resolver@1.11.1:
resolution:
{
@@ -7822,6 +8756,12 @@ packages:
}
engines: { node: ">=10.13.0" }
+ webpack-virtual-modules@0.5.0:
+ resolution:
+ {
+ integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==,
+ }
+
webpack@5.101.3:
resolution:
{
@@ -7946,6 +8886,13 @@ packages:
}
engines: { node: ">=0.4.0" }
+ xtend@4.0.2:
+ resolution:
+ {
+ integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==,
+ }
+ engines: { node: ">=0.4" }
+
y18n@5.0.8:
resolution:
{
@@ -8511,6 +9458,92 @@ snapshots:
"@humanwhocodes/retry@0.4.3": {}
+ "@img/sharp-darwin-arm64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-arm64": 1.2.0
+ optional: true
+
+ "@img/sharp-darwin-x64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-x64": 1.2.0
+ optional: true
+
+ "@img/sharp-libvips-darwin-arm64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-darwin-x64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linux-arm64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linux-arm@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linux-ppc64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linux-s390x@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linux-x64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linuxmusl-arm64@1.2.0":
+ optional: true
+
+ "@img/sharp-libvips-linuxmusl-x64@1.2.0":
+ optional: true
+
+ "@img/sharp-linux-arm64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm64": 1.2.0
+ optional: true
+
+ "@img/sharp-linux-arm@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm": 1.2.0
+ optional: true
+
+ "@img/sharp-linux-ppc64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linux-ppc64": 1.2.0
+ optional: true
+
+ "@img/sharp-linux-s390x@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linux-s390x": 1.2.0
+ optional: true
+
+ "@img/sharp-linux-x64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linux-x64": 1.2.0
+ optional: true
+
+ "@img/sharp-linuxmusl-arm64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-arm64": 1.2.0
+ optional: true
+
+ "@img/sharp-linuxmusl-x64@0.34.3":
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-x64": 1.2.0
+ optional: true
+
+ "@img/sharp-wasm32@0.34.3":
+ dependencies:
+ "@emnapi/runtime": 1.4.5
+ optional: true
+
+ "@img/sharp-win32-arm64@0.34.3":
+ optional: true
+
+ "@img/sharp-win32-ia32@0.34.3":
+ optional: true
+
+ "@img/sharp-win32-x64@0.34.3":
+ optional: true
+
"@internationalized/date@3.8.2":
dependencies:
"@swc/helpers": 0.5.17
@@ -8742,8 +9775,7 @@ snapshots:
"@jridgewell/source-map@0.3.11":
dependencies:
"@jridgewell/gen-mapping": 0.3.13
- "@jridgewell/trace-mapping": 0.3.30
- optional: true
+ "@jridgewell/trace-mapping": 0.3.31
"@jridgewell/sourcemap-codec@1.5.5": {}
@@ -8752,6 +9784,11 @@ snapshots:
"@jridgewell/resolve-uri": 3.1.2
"@jridgewell/sourcemap-codec": 1.5.5
+ "@jridgewell/trace-mapping@0.3.31":
+ dependencies:
+ "@jridgewell/resolve-uri": 3.1.2
+ "@jridgewell/sourcemap-codec": 1.5.5
+
"@jridgewell/trace-mapping@0.3.9":
dependencies:
"@jridgewell/resolve-uri": 3.1.2
@@ -8765,37 +9802,34 @@ snapshots:
"@tybys/wasm-util": 0.10.0
optional: true
- "@next/env@14.2.31": {}
+ "@next/env@15.5.3": {}
- "@next/eslint-plugin-next@14.2.31":
+ "@next/eslint-plugin-next@15.5.3":
dependencies:
- glob: 10.3.10
+ fast-glob: 3.3.1
- "@next/swc-darwin-arm64@14.2.31":
+ "@next/swc-darwin-arm64@15.5.3":
optional: true
- "@next/swc-darwin-x64@14.2.31":
+ "@next/swc-darwin-x64@15.5.3":
optional: true
- "@next/swc-linux-arm64-gnu@14.2.31":
+ "@next/swc-linux-arm64-gnu@15.5.3":
optional: true
- "@next/swc-linux-arm64-musl@14.2.31":
+ "@next/swc-linux-arm64-musl@15.5.3":
optional: true
- "@next/swc-linux-x64-gnu@14.2.31":
+ "@next/swc-linux-x64-gnu@15.5.3":
optional: true
- "@next/swc-linux-x64-musl@14.2.31":
+ "@next/swc-linux-x64-musl@15.5.3":
optional: true
- "@next/swc-win32-arm64-msvc@14.2.31":
+ "@next/swc-win32-arm64-msvc@15.5.3":
optional: true
- "@next/swc-win32-ia32-msvc@14.2.31":
- optional: true
-
- "@next/swc-win32-x64-msvc@14.2.31":
+ "@next/swc-win32-x64-msvc@15.5.3":
optional: true
"@nodelib/fs.scandir@2.1.5":
@@ -8812,8 +9846,275 @@ snapshots:
"@nolyfill/is-core-module@1.0.39": {}
- "@opentelemetry/api@1.9.0":
- optional: true
+ "@opentelemetry/api-logs@0.203.0":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+
+ "@opentelemetry/api-logs@0.204.0":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+
+ "@opentelemetry/api-logs@0.57.2":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+
+ "@opentelemetry/api@1.9.0": {}
+
+ "@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+
+ "@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/semantic-conventions": 1.37.0
+
+ "@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/semantic-conventions": 1.37.0
+
+ "@opentelemetry/instrumentation-amqplib@0.50.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-connect@0.47.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@types/connect": 3.4.38
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-dataloader@0.21.1(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-express@0.52.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-fs@0.23.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-generic-pool@0.47.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-graphql@0.51.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-hapi@0.50.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-http@0.203.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.0.1(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ forwarded-parse: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-ioredis@0.52.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.204.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/redis-common": 0.38.0
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-kafkajs@0.13.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-knex@0.48.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-koa@0.51.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-lru-memoizer@0.48.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-mongodb@0.56.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-mongoose@0.50.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-mysql2@0.50.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@opentelemetry/sql-common": 0.41.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-mysql@0.49.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@types/mysql": 2.15.27
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-pg@0.55.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@opentelemetry/sql-common": 0.41.0(@opentelemetry/api@1.9.0)
+ "@types/pg": 8.15.4
+ "@types/pg-pool": 2.0.6
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-redis@0.51.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/redis-common": 0.38.0
+ "@opentelemetry/semantic-conventions": 1.37.0
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-tedious@0.22.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@types/tedious": 4.0.14
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation-undici@0.14.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/api-logs": 0.203.0
+ import-in-the-middle: 1.14.2
+ require-in-the-middle: 7.5.2
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation@0.204.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/api-logs": 0.204.0
+ import-in-the-middle: 1.14.2
+ require-in-the-middle: 7.5.2
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/api-logs": 0.57.2
+ "@types/shimmer": 1.2.0
+ import-in-the-middle: 1.14.2
+ require-in-the-middle: 7.5.2
+ semver: 7.7.2
+ shimmer: 1.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ "@opentelemetry/redis-common@0.38.0": {}
+
+ "@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+
+ "@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/resources": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+
+ "@opentelemetry/semantic-conventions@1.37.0": {}
+
+ "@opentelemetry/sql-common@0.41.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
"@pandacss/is-valid-prop@0.54.0": {}
@@ -8885,6 +10186,13 @@ snapshots:
"@pkgr/core@0.2.9": {}
+ "@prisma/instrumentation@6.14.0(@opentelemetry/api@1.9.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/instrumentation": 0.57.2(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
"@radix-ui/primitive@1.1.3": {}
"@radix-ui/react-arrow@1.1.7(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
@@ -9098,131 +10406,289 @@ snapshots:
optionalDependencies:
react: 18.3.1
- "@rollup/plugin-commonjs@24.0.0(rollup@2.78.0)":
+ "@rollup/plugin-commonjs@28.0.1(rollup@4.50.1)":
dependencies:
- "@rollup/pluginutils": 5.2.0(rollup@2.78.0)
+ "@rollup/pluginutils": 5.2.0(rollup@4.50.1)
commondir: 1.0.1
estree-walker: 2.0.2
- glob: 8.1.0
+ fdir: 6.4.6(picomatch@4.0.3)
is-reference: 1.2.1
- magic-string: 0.27.0
+ magic-string: 0.30.19
+ picomatch: 4.0.3
optionalDependencies:
- rollup: 2.78.0
+ rollup: 4.50.1
- "@rollup/pluginutils@5.2.0(rollup@2.78.0)":
+ "@rollup/pluginutils@5.2.0(rollup@4.50.1)":
dependencies:
"@types/estree": 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.3
optionalDependencies:
- rollup: 2.78.0
+ rollup: 4.50.1
+
+ "@rollup/rollup-android-arm-eabi@4.50.1":
+ optional: true
+
+ "@rollup/rollup-android-arm64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-darwin-arm64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-darwin-x64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-freebsd-arm64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-freebsd-x64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-arm-gnueabihf@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-arm-musleabihf@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-arm64-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-arm64-musl@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-loongarch64-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-ppc64-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-riscv64-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-riscv64-musl@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-s390x-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-x64-gnu@4.50.1":
+ optional: true
+
+ "@rollup/rollup-linux-x64-musl@4.50.1":
+ optional: true
+
+ "@rollup/rollup-openharmony-arm64@4.50.1":
+ optional: true
+
+ "@rollup/rollup-win32-arm64-msvc@4.50.1":
+ optional: true
+
+ "@rollup/rollup-win32-ia32-msvc@4.50.1":
+ optional: true
+
+ "@rollup/rollup-win32-x64-msvc@4.50.1":
+ optional: true
"@rtsao/scc@1.1.0": {}
"@rushstack/eslint-patch@1.12.0": {}
- "@sentry-internal/tracing@7.77.0":
+ "@sentry-internal/browser-utils@10.11.0":
dependencies:
- "@sentry/core": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
+ "@sentry/core": 10.11.0
- "@sentry/browser@7.77.0":
+ "@sentry-internal/feedback@10.11.0":
dependencies:
- "@sentry-internal/tracing": 7.77.0
- "@sentry/core": 7.77.0
- "@sentry/replay": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
+ "@sentry/core": 10.11.0
- "@sentry/cli@1.77.3":
+ "@sentry-internal/replay-canvas@10.11.0":
+ dependencies:
+ "@sentry-internal/replay": 10.11.0
+ "@sentry/core": 10.11.0
+
+ "@sentry-internal/replay@10.11.0":
+ dependencies:
+ "@sentry-internal/browser-utils": 10.11.0
+ "@sentry/core": 10.11.0
+
+ "@sentry/babel-plugin-component-annotate@4.3.0": {}
+
+ "@sentry/browser@10.11.0":
+ dependencies:
+ "@sentry-internal/browser-utils": 10.11.0
+ "@sentry-internal/feedback": 10.11.0
+ "@sentry-internal/replay": 10.11.0
+ "@sentry-internal/replay-canvas": 10.11.0
+ "@sentry/core": 10.11.0
+
+ "@sentry/bundler-plugin-core@4.3.0":
+ dependencies:
+ "@babel/core": 7.28.3
+ "@sentry/babel-plugin-component-annotate": 4.3.0
+ "@sentry/cli": 2.53.0
+ dotenv: 16.6.1
+ find-up: 5.0.0
+ glob: 9.3.5
+ magic-string: 0.30.8
+ unplugin: 1.0.1
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ "@sentry/cli-darwin@2.53.0":
+ optional: true
+
+ "@sentry/cli-linux-arm64@2.53.0":
+ optional: true
+
+ "@sentry/cli-linux-arm@2.53.0":
+ optional: true
+
+ "@sentry/cli-linux-i686@2.53.0":
+ optional: true
+
+ "@sentry/cli-linux-x64@2.53.0":
+ optional: true
+
+ "@sentry/cli-win32-arm64@2.53.0":
+ optional: true
+
+ "@sentry/cli-win32-i686@2.53.0":
+ optional: true
+
+ "@sentry/cli-win32-x64@2.53.0":
+ optional: true
+
+ "@sentry/cli@2.53.0":
dependencies:
https-proxy-agent: 5.0.1
- mkdirp: 0.5.6
node-fetch: 2.7.0
progress: 2.0.3
proxy-from-env: 1.1.0
which: 2.0.2
- transitivePeerDependencies:
- - encoding
- - supports-color
-
- "@sentry/core@7.77.0":
- dependencies:
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
-
- "@sentry/integrations@7.77.0":
- dependencies:
- "@sentry/core": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
- localforage: 1.10.0
-
- "@sentry/nextjs@7.77.0(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)":
- dependencies:
- "@rollup/plugin-commonjs": 24.0.0(rollup@2.78.0)
- "@sentry/core": 7.77.0
- "@sentry/integrations": 7.77.0
- "@sentry/node": 7.77.0
- "@sentry/react": 7.77.0(react@18.3.1)
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
- "@sentry/vercel-edge": 7.77.0
- "@sentry/webpack-plugin": 1.20.0
- chalk: 3.0.0
- next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
- react: 18.3.1
- resolve: 1.22.8
- rollup: 2.78.0
- stacktrace-parser: 0.1.11
optionalDependencies:
- webpack: 5.101.3
+ "@sentry/cli-darwin": 2.53.0
+ "@sentry/cli-linux-arm": 2.53.0
+ "@sentry/cli-linux-arm64": 2.53.0
+ "@sentry/cli-linux-i686": 2.53.0
+ "@sentry/cli-linux-x64": 2.53.0
+ "@sentry/cli-win32-arm64": 2.53.0
+ "@sentry/cli-win32-i686": 2.53.0
+ "@sentry/cli-win32-x64": 2.53.0
transitivePeerDependencies:
- encoding
- supports-color
- "@sentry/node@7.77.0":
+ "@sentry/core@10.11.0": {}
+
+ "@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1)(webpack@5.101.3)":
dependencies:
- "@sentry-internal/tracing": 7.77.0
- "@sentry/core": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
- https-proxy-agent: 5.0.1
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@rollup/plugin-commonjs": 28.0.1(rollup@4.50.1)
+ "@sentry-internal/browser-utils": 10.11.0
+ "@sentry/bundler-plugin-core": 4.3.0
+ "@sentry/core": 10.11.0
+ "@sentry/node": 10.11.0
+ "@sentry/opentelemetry": 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
+ "@sentry/react": 10.11.0(react@18.3.1)
+ "@sentry/vercel-edge": 10.11.0
+ "@sentry/webpack-plugin": 4.3.0(webpack@5.101.3)
+ chalk: 3.0.0
+ next: 15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ resolve: 1.22.8
+ rollup: 4.50.1
+ stacktrace-parser: 0.1.11
+ transitivePeerDependencies:
+ - "@opentelemetry/context-async-hooks"
+ - "@opentelemetry/core"
+ - "@opentelemetry/sdk-trace-base"
+ - encoding
+ - react
+ - supports-color
+ - webpack
+
+ "@sentry/node-core@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/context-async-hooks": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/resources": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/sdk-trace-base": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@sentry/core": 10.11.0
+ "@sentry/opentelemetry": 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
+ import-in-the-middle: 1.14.2
+
+ "@sentry/node@10.11.0":
+ dependencies:
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/context-async-hooks": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-amqplib": 0.50.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-connect": 0.47.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-dataloader": 0.21.1(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-express": 0.52.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-fs": 0.23.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-generic-pool": 0.47.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-graphql": 0.51.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-hapi": 0.50.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-http": 0.203.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-ioredis": 0.52.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-kafkajs": 0.13.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-knex": 0.48.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-koa": 0.51.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-lru-memoizer": 0.48.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-mongodb": 0.56.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-mongoose": 0.50.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-mysql": 0.49.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-mysql2": 0.50.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-pg": 0.55.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-redis": 0.51.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-tedious": 0.22.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/instrumentation-undici": 0.14.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/resources": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/sdk-trace-base": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@prisma/instrumentation": 6.14.0(@opentelemetry/api@1.9.0)
+ "@sentry/core": 10.11.0
+ "@sentry/node-core": 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
+ "@sentry/opentelemetry": 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)
+ import-in-the-middle: 1.14.2
+ minimatch: 9.0.5
transitivePeerDependencies:
- supports-color
- "@sentry/react@7.77.0(react@18.3.1)":
+ "@sentry/opentelemetry@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)":
dependencies:
- "@sentry/browser": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/context-async-hooks": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/core": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/sdk-trace-base": 2.1.0(@opentelemetry/api@1.9.0)
+ "@opentelemetry/semantic-conventions": 1.37.0
+ "@sentry/core": 10.11.0
+
+ "@sentry/react@10.11.0(react@18.3.1)":
+ dependencies:
+ "@sentry/browser": 10.11.0
+ "@sentry/core": 10.11.0
hoist-non-react-statics: 3.3.2
react: 18.3.1
- "@sentry/replay@7.77.0":
+ "@sentry/vercel-edge@10.11.0":
dependencies:
- "@sentry-internal/tracing": 7.77.0
- "@sentry/core": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
+ "@opentelemetry/api": 1.9.0
+ "@opentelemetry/resources": 2.1.0(@opentelemetry/api@1.9.0)
+ "@sentry/core": 10.11.0
- "@sentry/types@7.77.0": {}
-
- "@sentry/utils@7.77.0":
+ "@sentry/webpack-plugin@4.3.0(webpack@5.101.3)":
dependencies:
- "@sentry/types": 7.77.0
-
- "@sentry/vercel-edge@7.77.0":
- dependencies:
- "@sentry/core": 7.77.0
- "@sentry/types": 7.77.0
- "@sentry/utils": 7.77.0
-
- "@sentry/webpack-plugin@1.20.0":
- dependencies:
- "@sentry/cli": 1.77.3
- webpack-sources: 3.3.3
+ "@sentry/bundler-plugin-core": 4.3.0
+ unplugin: 1.0.1
+ uuid: 9.0.1
+ webpack: 5.101.3
transitivePeerDependencies:
- encoding
- supports-color
@@ -9245,15 +10711,12 @@ snapshots:
"@standard-schema/utils@0.3.0": {}
- "@swc/counter@0.1.3": {}
-
- "@swc/helpers@0.5.17":
+ "@swc/helpers@0.5.15":
dependencies:
tslib: 2.8.1
- "@swc/helpers@0.5.5":
+ "@swc/helpers@0.5.17":
dependencies:
- "@swc/counter": 0.1.3
tslib: 2.8.1
"@tanstack/query-core@5.85.9": {}
@@ -9301,6 +10764,10 @@ snapshots:
dependencies:
"@babel/types": 7.28.2
+ "@types/connect@3.4.38":
+ dependencies:
+ "@types/node": 24.2.1
+
"@types/debug@4.1.12":
dependencies:
"@types/ms": 2.1.0
@@ -9309,13 +10776,11 @@ snapshots:
dependencies:
"@types/eslint": 9.6.1
"@types/estree": 1.0.8
- optional: true
"@types/eslint@9.6.1":
dependencies:
"@types/estree": 1.0.8
"@types/json-schema": 7.0.15
- optional: true
"@types/estree-jsx@1.0.5":
dependencies:
@@ -9360,6 +10825,10 @@ snapshots:
"@types/ms@2.1.0": {}
+ "@types/mysql@2.15.27":
+ dependencies:
+ "@types/node": 24.2.1
+
"@types/node-fetch@2.6.13":
dependencies:
"@types/node": 24.2.1
@@ -9369,8 +10838,22 @@ snapshots:
dependencies:
undici-types: 7.10.0
+ "@types/node@24.3.1":
+ dependencies:
+ undici-types: 7.10.0
+
"@types/parse-json@4.0.2": {}
+ "@types/pg-pool@2.0.6":
+ dependencies:
+ "@types/pg": 8.15.4
+
+ "@types/pg@8.15.4":
+ dependencies:
+ "@types/node": 24.2.1
+ pg-protocol: 1.10.3
+ pg-types: 2.2.0
+
"@types/prop-types@15.7.15": {}
"@types/react@18.2.20":
@@ -9381,8 +10864,14 @@ snapshots:
"@types/scheduler@0.26.0": {}
+ "@types/shimmer@1.2.0": {}
+
"@types/stack-utils@2.0.3": {}
+ "@types/tedious@4.0.14":
+ dependencies:
+ "@types/node": 24.2.1
+
"@types/ua-parser-js@0.7.39": {}
"@types/unist@2.0.11": {}
@@ -9565,26 +11054,20 @@ snapshots:
dependencies:
"@webassemblyjs/helper-numbers": 1.13.2
"@webassemblyjs/helper-wasm-bytecode": 1.13.2
- optional: true
- "@webassemblyjs/floating-point-hex-parser@1.13.2":
- optional: true
+ "@webassemblyjs/floating-point-hex-parser@1.13.2": {}
- "@webassemblyjs/helper-api-error@1.13.2":
- optional: true
+ "@webassemblyjs/helper-api-error@1.13.2": {}
- "@webassemblyjs/helper-buffer@1.14.1":
- optional: true
+ "@webassemblyjs/helper-buffer@1.14.1": {}
"@webassemblyjs/helper-numbers@1.13.2":
dependencies:
"@webassemblyjs/floating-point-hex-parser": 1.13.2
"@webassemblyjs/helper-api-error": 1.13.2
"@xtuc/long": 4.2.2
- optional: true
- "@webassemblyjs/helper-wasm-bytecode@1.13.2":
- optional: true
+ "@webassemblyjs/helper-wasm-bytecode@1.13.2": {}
"@webassemblyjs/helper-wasm-section@1.14.1":
dependencies:
@@ -9592,20 +11075,16 @@ snapshots:
"@webassemblyjs/helper-buffer": 1.14.1
"@webassemblyjs/helper-wasm-bytecode": 1.13.2
"@webassemblyjs/wasm-gen": 1.14.1
- optional: true
"@webassemblyjs/ieee754@1.13.2":
dependencies:
"@xtuc/ieee754": 1.2.0
- optional: true
"@webassemblyjs/leb128@1.13.2":
dependencies:
"@xtuc/long": 4.2.2
- optional: true
- "@webassemblyjs/utf8@1.13.2":
- optional: true
+ "@webassemblyjs/utf8@1.13.2": {}
"@webassemblyjs/wasm-edit@1.14.1":
dependencies:
@@ -9617,7 +11096,6 @@ snapshots:
"@webassemblyjs/wasm-opt": 1.14.1
"@webassemblyjs/wasm-parser": 1.14.1
"@webassemblyjs/wast-printer": 1.14.1
- optional: true
"@webassemblyjs/wasm-gen@1.14.1":
dependencies:
@@ -9626,7 +11104,6 @@ snapshots:
"@webassemblyjs/ieee754": 1.13.2
"@webassemblyjs/leb128": 1.13.2
"@webassemblyjs/utf8": 1.13.2
- optional: true
"@webassemblyjs/wasm-opt@1.14.1":
dependencies:
@@ -9634,7 +11111,6 @@ snapshots:
"@webassemblyjs/helper-buffer": 1.14.1
"@webassemblyjs/wasm-gen": 1.14.1
"@webassemblyjs/wasm-parser": 1.14.1
- optional: true
"@webassemblyjs/wasm-parser@1.14.1":
dependencies:
@@ -9644,13 +11120,11 @@ snapshots:
"@webassemblyjs/ieee754": 1.13.2
"@webassemblyjs/leb128": 1.13.2
"@webassemblyjs/utf8": 1.13.2
- optional: true
"@webassemblyjs/wast-printer@1.14.1":
dependencies:
"@webassemblyjs/ast": 1.14.1
"@xtuc/long": 4.2.2
- optional: true
"@whereby.com/browser-sdk@3.13.1(@types/react@18.2.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
@@ -9709,11 +11183,9 @@ snapshots:
- supports-color
- utf-8-validate
- "@xtuc/ieee754@1.2.0":
- optional: true
+ "@xtuc/ieee754@1.2.0": {}
- "@xtuc/long@4.2.2":
- optional: true
+ "@xtuc/long@4.2.2": {}
"@zag-js/accordion@1.21.0":
dependencies:
@@ -10216,10 +11688,13 @@ snapshots:
"@zag-js/utils@1.21.0": {}
+ acorn-import-attributes@1.9.5(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
acorn-import-phases@1.0.4(acorn@8.15.0):
dependencies:
acorn: 8.15.0
- optional: true
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
@@ -10243,13 +11718,11 @@ snapshots:
ajv-formats@2.1.1(ajv@8.17.1):
optionalDependencies:
ajv: 8.17.1
- optional: true
ajv-keywords@5.1.0(ajv@8.17.1):
dependencies:
ajv: 8.17.1
fast-deep-equal: 3.1.3
- optional: true
ajv@6.12.6:
dependencies:
@@ -10264,7 +11737,6 @@ snapshots:
fast-uri: 3.1.0
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
- optional: true
ansi-colors@4.1.3: {}
@@ -10523,10 +11995,6 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- busboy@1.6.0:
- dependencies:
- streamsearch: 1.1.0
-
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -10598,13 +12066,14 @@ snapshots:
dependencies:
readdirp: 4.1.2
- chrome-trace-event@1.0.4:
- optional: true
+ chrome-trace-event@1.0.4: {}
ci-info@3.9.0: {}
ci-info@4.3.0: {}
+ cjs-module-lexer@1.4.3: {}
+
cjs-module-lexer@2.1.0: {}
classnames@2.5.1: {}
@@ -10631,6 +12100,18 @@ snapshots:
color-name@1.1.4: {}
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ optional: true
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
colorette@1.4.0: {}
combined-stream@1.0.8:
@@ -10639,8 +12120,7 @@ snapshots:
comma-separated-tokens@2.0.3: {}
- commander@2.20.3:
- optional: true
+ commander@2.20.3: {}
commander@4.1.1: {}
@@ -10750,6 +12230,9 @@ snapshots:
detect-libc@1.0.3:
optional: true
+ detect-libc@2.0.4:
+ optional: true
+
detect-newline@3.1.0: {}
detect-node-es@1.1.0: {}
@@ -10785,6 +12268,8 @@ snapshots:
domsanitizer: 0.2.3
umap: 1.0.2
+ dotenv@16.6.1: {}
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -10819,7 +12304,6 @@ snapshots:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.3
- optional: true
err-code@3.0.1: {}
@@ -10907,8 +12391,7 @@ snapshots:
iterator.prototype: 1.1.5
safe-array-concat: 1.1.3
- es-module-lexer@1.7.0:
- optional: true
+ es-module-lexer@1.7.0: {}
es-object-atoms@1.1.1:
dependencies:
@@ -10937,9 +12420,9 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-next@14.2.31(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2):
+ eslint-config-next@15.5.3(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2):
dependencies:
- "@next/eslint-plugin-next": 14.2.31
+ "@next/eslint-plugin-next": 15.5.3
"@rushstack/eslint-patch": 1.12.0
"@typescript-eslint/eslint-plugin": 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)
"@typescript-eslint/parser": 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)
@@ -10949,7 +12432,7 @@ snapshots:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.33.0(jiti@1.21.7))
eslint-plugin-react: 7.37.5(eslint@9.33.0(jiti@1.21.7))
- eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.33.0(jiti@1.21.7))
+ eslint-plugin-react-hooks: 5.2.0(eslint@9.33.0(jiti@1.21.7))
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -11039,7 +12522,7 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
- eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.33.0(jiti@1.21.7)):
+ eslint-plugin-react-hooks@5.2.0(eslint@9.33.0(jiti@1.21.7)):
dependencies:
eslint: 9.33.0(jiti@1.21.7)
@@ -11069,7 +12552,6 @@ snapshots:
dependencies:
esrecurse: 4.3.0
estraverse: 4.3.0
- optional: true
eslint-scope@8.4.0:
dependencies:
@@ -11138,8 +12620,7 @@ snapshots:
dependencies:
estraverse: 5.3.0
- estraverse@4.3.0:
- optional: true
+ estraverse@4.3.0: {}
estraverse@5.3.0: {}
@@ -11185,6 +12666,14 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fast-glob@3.3.1:
+ dependencies:
+ "@nodelib/fs.stat": 2.0.5
+ "@nodelib/fs.walk": 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
fast-glob@3.3.3:
dependencies:
"@nodelib/fs.stat": 2.0.5
@@ -11199,8 +12688,7 @@ snapshots:
fast-safe-stringify@2.1.1: {}
- fast-uri@3.1.0:
- optional: true
+ fast-uri@3.1.0: {}
fastq@1.19.1:
dependencies:
@@ -11262,6 +12750,8 @@ snapshots:
hasown: 2.0.2
mime-types: 2.1.35
+ forwarded-parse@2.1.2: {}
+
fraction.js@4.3.7: {}
fs.realpath@1.0.0: {}
@@ -11330,16 +12820,7 @@ snapshots:
dependencies:
is-glob: 4.0.3
- glob-to-regexp@0.4.1:
- optional: true
-
- glob@10.3.10:
- dependencies:
- foreground-child: 3.3.1
- jackspeak: 2.3.6
- minimatch: 9.0.5
- minipass: 7.1.2
- path-scurry: 1.11.1
+ glob-to-regexp@0.4.1: {}
glob@10.4.5:
dependencies:
@@ -11359,13 +12840,12 @@ snapshots:
once: 1.4.0
path-is-absolute: 1.0.1
- glob@8.1.0:
+ glob@9.3.5:
dependencies:
fs.realpath: 1.0.0
- inflight: 1.0.6
- inherits: 2.0.4
- minimatch: 5.1.6
- once: 1.4.0
+ minimatch: 8.0.4
+ minipass: 4.2.8
+ path-scurry: 1.11.1
globals@14.0.0: {}
@@ -11482,8 +12962,6 @@ snapshots:
ignore@7.0.5: {}
- immediate@3.0.6: {}
-
immer@10.1.1: {}
immutable@5.1.3: {}
@@ -11493,6 +12971,13 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-in-the-middle@1.14.2:
+ dependencies:
+ acorn: 8.15.0
+ acorn-import-attributes: 1.9.5(acorn@8.15.0)
+ cjs-module-lexer: 1.4.3
+ module-details-from-path: 1.0.4
+
import-local@3.2.0:
dependencies:
pkg-dir: 4.2.0
@@ -11557,6 +13042,9 @@ snapshots:
is-arrayish@0.2.1: {}
+ is-arrayish@0.3.2:
+ optional: true
+
is-async-function@2.1.1:
dependencies:
async-function: 1.0.0
@@ -11728,12 +13216,6 @@ snapshots:
has-symbols: 1.1.0
set-function-name: 2.0.2
- jackspeak@2.3.6:
- dependencies:
- "@isaacs/cliui": 8.0.2
- optionalDependencies:
- "@pkgjs/parseargs": 0.11.0
-
jackspeak@3.4.3:
dependencies:
"@isaacs/cliui": 8.0.2
@@ -12042,10 +13524,9 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- "@types/node": 24.2.1
+ "@types/node": 24.3.1
merge-stream: 2.0.0
supports-color: 8.1.1
- optional: true
jest-worker@29.7.0:
dependencies:
@@ -12136,10 +13617,6 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
- lie@3.1.1:
- dependencies:
- immediate: 3.0.6
-
lighterhtml@4.2.0:
dependencies:
"@ungap/create-content": 0.2.0
@@ -12156,12 +13633,7 @@ snapshots:
lines-and-columns@1.2.4: {}
- loader-runner@4.3.0:
- optional: true
-
- localforage@1.10.0:
- dependencies:
- lie: 3.1.1
+ loader-runner@4.3.0: {}
locate-path@5.0.0:
dependencies:
@@ -12199,7 +13671,11 @@ snapshots:
dependencies:
react: 18.3.1
- magic-string@0.27.0:
+ magic-string@0.30.19:
+ dependencies:
+ "@jridgewell/sourcemap-codec": 1.5.5
+
+ magic-string@0.30.8:
dependencies:
"@jridgewell/sourcemap-codec": 1.5.5
@@ -12478,19 +13954,23 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
+ minimatch@8.0.4:
+ dependencies:
+ brace-expansion: 2.0.2
+
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.2
minimist@1.2.8: {}
+ minipass@4.2.8: {}
+
minipass@7.1.2: {}
mitt@3.0.1: {}
- mkdirp@0.5.6:
- dependencies:
- minimist: 1.2.8
+ module-details-from-path@1.0.4: {}
ms@2.1.3: {}
@@ -12508,13 +13988,13 @@ snapshots:
neo-async@2.6.2: {}
- next-auth@4.24.11(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next-auth@4.24.11(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
"@babel/runtime": 7.28.2
"@panva/hkdf": 1.2.1
cookie: 0.7.2
jose: 4.15.9
- next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth: 0.9.15
openid-client: 5.7.1
preact: 10.27.0
@@ -12528,29 +14008,27 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
+ next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0):
dependencies:
- "@next/env": 14.2.31
- "@swc/helpers": 0.5.5
- busboy: 1.6.0
+ "@next/env": 15.5.3
+ "@swc/helpers": 0.5.15
caniuse-lite: 1.0.30001734
- graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- styled-jsx: 5.1.1(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1)
+ styled-jsx: 5.1.6(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1)
optionalDependencies:
- "@next/swc-darwin-arm64": 14.2.31
- "@next/swc-darwin-x64": 14.2.31
- "@next/swc-linux-arm64-gnu": 14.2.31
- "@next/swc-linux-arm64-musl": 14.2.31
- "@next/swc-linux-x64-gnu": 14.2.31
- "@next/swc-linux-x64-musl": 14.2.31
- "@next/swc-win32-arm64-msvc": 14.2.31
- "@next/swc-win32-ia32-msvc": 14.2.31
- "@next/swc-win32-x64-msvc": 14.2.31
+ "@next/swc-darwin-arm64": 15.5.3
+ "@next/swc-darwin-x64": 15.5.3
+ "@next/swc-linux-arm64-gnu": 15.5.3
+ "@next/swc-linux-arm64-musl": 15.5.3
+ "@next/swc-linux-x64-gnu": 15.5.3
+ "@next/swc-linux-x64-musl": 15.5.3
+ "@next/swc-win32-arm64-msvc": 15.5.3
+ "@next/swc-win32-x64-msvc": 15.5.3
"@opentelemetry/api": 1.9.0
sass: 1.90.0
+ sharp: 0.34.3
transitivePeerDependencies:
- "@babel/core"
- babel-plugin-macros
@@ -12576,12 +14054,12 @@ snapshots:
dependencies:
path-key: 3.1.1
- nuqs@2.4.3(next@14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
+ nuqs@2.4.3(next@15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0))(react@18.3.1):
dependencies:
mitt: 3.0.1
react: 18.3.1
optionalDependencies:
- next: 14.2.31(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
+ next: 15.5.3(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0)
oauth@0.9.15: {}
@@ -12749,6 +14227,18 @@ snapshots:
perfect-freehand@1.2.2: {}
+ pg-int8@1.0.1: {}
+
+ pg-protocol@1.10.3: {}
+
+ pg-types@2.2.0:
+ dependencies:
+ pg-int8: 1.0.1
+ postgres-array: 2.0.0
+ postgres-bytea: 1.0.0
+ postgres-date: 1.0.7
+ postgres-interval: 1.2.0
+
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -12811,6 +14301,16 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ postgres-array@2.0.0: {}
+
+ postgres-bytea@1.0.0: {}
+
+ postgres-date@1.0.7: {}
+
+ postgres-interval@1.2.0:
+ dependencies:
+ xtend: 4.0.2
+
preact-render-to-string@5.2.6(preact@10.27.0):
dependencies:
preact: 10.27.0
@@ -13014,6 +14514,14 @@ snapshots:
require-from-string@2.0.2: {}
+ require-in-the-middle@7.5.2:
+ dependencies:
+ debug: 4.4.1(supports-color@9.4.0)
+ module-details-from-path: 1.0.4
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
reraf@1.1.1: {}
reselect@5.1.1: {}
@@ -13048,8 +14556,31 @@ snapshots:
reusify@1.1.0: {}
- rollup@2.78.0:
+ rollup@4.50.1:
+ dependencies:
+ "@types/estree": 1.0.8
optionalDependencies:
+ "@rollup/rollup-android-arm-eabi": 4.50.1
+ "@rollup/rollup-android-arm64": 4.50.1
+ "@rollup/rollup-darwin-arm64": 4.50.1
+ "@rollup/rollup-darwin-x64": 4.50.1
+ "@rollup/rollup-freebsd-arm64": 4.50.1
+ "@rollup/rollup-freebsd-x64": 4.50.1
+ "@rollup/rollup-linux-arm-gnueabihf": 4.50.1
+ "@rollup/rollup-linux-arm-musleabihf": 4.50.1
+ "@rollup/rollup-linux-arm64-gnu": 4.50.1
+ "@rollup/rollup-linux-arm64-musl": 4.50.1
+ "@rollup/rollup-linux-loongarch64-gnu": 4.50.1
+ "@rollup/rollup-linux-ppc64-gnu": 4.50.1
+ "@rollup/rollup-linux-riscv64-gnu": 4.50.1
+ "@rollup/rollup-linux-riscv64-musl": 4.50.1
+ "@rollup/rollup-linux-s390x-gnu": 4.50.1
+ "@rollup/rollup-linux-x64-gnu": 4.50.1
+ "@rollup/rollup-linux-x64-musl": 4.50.1
+ "@rollup/rollup-openharmony-arm64": 4.50.1
+ "@rollup/rollup-win32-arm64-msvc": 4.50.1
+ "@rollup/rollup-win32-ia32-msvc": 4.50.1
+ "@rollup/rollup-win32-x64-msvc": 4.50.1
fsevents: 2.3.3
rtcstats@https://codeload.github.com/whereby/rtcstats/tar.gz/63bcb6420d76d34161b39e494524ae73aa6dd70d:
@@ -13100,7 +14631,6 @@ snapshots:
ajv: 8.17.1
ajv-formats: 2.1.1(ajv@8.17.1)
ajv-keywords: 5.1.0(ajv@8.17.1)
- optional: true
sdp-transform@2.15.0: {}
@@ -13113,7 +14643,6 @@ snapshots:
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
- optional: true
set-function-length@1.2.2:
dependencies:
@@ -13137,12 +14666,44 @@ snapshots:
es-errors: 1.3.0
es-object-atoms: 1.1.1
+ sharp@0.34.3:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.4
+ semver: 7.7.2
+ optionalDependencies:
+ "@img/sharp-darwin-arm64": 0.34.3
+ "@img/sharp-darwin-x64": 0.34.3
+ "@img/sharp-libvips-darwin-arm64": 1.2.0
+ "@img/sharp-libvips-darwin-x64": 1.2.0
+ "@img/sharp-libvips-linux-arm": 1.2.0
+ "@img/sharp-libvips-linux-arm64": 1.2.0
+ "@img/sharp-libvips-linux-ppc64": 1.2.0
+ "@img/sharp-libvips-linux-s390x": 1.2.0
+ "@img/sharp-libvips-linux-x64": 1.2.0
+ "@img/sharp-libvips-linuxmusl-arm64": 1.2.0
+ "@img/sharp-libvips-linuxmusl-x64": 1.2.0
+ "@img/sharp-linux-arm": 0.34.3
+ "@img/sharp-linux-arm64": 0.34.3
+ "@img/sharp-linux-ppc64": 0.34.3
+ "@img/sharp-linux-s390x": 0.34.3
+ "@img/sharp-linux-x64": 0.34.3
+ "@img/sharp-linuxmusl-arm64": 0.34.3
+ "@img/sharp-linuxmusl-x64": 0.34.3
+ "@img/sharp-wasm32": 0.34.3
+ "@img/sharp-win32-arm64": 0.34.3
+ "@img/sharp-win32-ia32": 0.34.3
+ "@img/sharp-win32-x64": 0.34.3
+ optional: true
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
+ shimmer@1.2.1: {}
+
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -13187,6 +14748,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ simple-swizzle@0.2.2:
+ dependencies:
+ is-arrayish: 0.3.2
+ optional: true
+
slash@3.0.0: {}
socket.io-client@4.7.2:
@@ -13218,7 +14784,6 @@ snapshots:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
- optional: true
source-map@0.5.7: {}
@@ -13247,8 +14812,6 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
- streamsearch@1.1.0: {}
-
string-length@4.0.2:
dependencies:
char-regex: 1.0.2
@@ -13349,7 +14912,7 @@ snapshots:
dependencies:
inline-style-parser: 0.2.4
- styled-jsx@5.1.1(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1):
+ styled-jsx@5.1.6(@babel/core@7.28.3)(babel-plugin-macros@3.1.0)(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
@@ -13414,18 +14977,16 @@ snapshots:
transitivePeerDependencies:
- ts-node
- tapable@2.2.3:
- optional: true
+ tapable@2.2.3: {}
terser-webpack-plugin@5.3.14(webpack@5.101.3):
dependencies:
- "@jridgewell/trace-mapping": 0.3.30
+ "@jridgewell/trace-mapping": 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.2
serialize-javascript: 6.0.2
terser: 5.44.0
webpack: 5.101.3
- optional: true
terser@5.44.0:
dependencies:
@@ -13433,7 +14994,6 @@ snapshots:
acorn: 8.15.0
commander: 2.20.3
source-map-support: 0.5.21
- optional: true
test-exclude@6.0.0:
dependencies:
@@ -13638,6 +15198,13 @@ snapshots:
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
+ unplugin@1.0.1:
+ dependencies:
+ acorn: 8.15.0
+ chokidar: 3.6.0
+ webpack-sources: 3.3.3
+ webpack-virtual-modules: 0.5.0
+
unrs-resolver@1.11.1:
dependencies:
napi-postinstall: 0.3.3
@@ -13730,7 +15297,6 @@ snapshots:
dependencies:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
- optional: true
wavesurfer.js@7.10.1: {}
@@ -13738,6 +15304,8 @@ snapshots:
webpack-sources@3.3.3: {}
+ webpack-virtual-modules@0.5.0: {}
+
webpack@5.101.3:
dependencies:
"@types/eslint-scope": 3.7.7
@@ -13769,7 +15337,6 @@ snapshots:
- "@swc/core"
- esbuild
- uglify-js
- optional: true
webrtc-adapter@9.0.3:
dependencies:
@@ -13852,6 +15419,8 @@ snapshots:
xmlhttprequest-ssl@2.0.0: {}
+ xtend@4.0.2: {}
+
y18n@5.0.8: {}
yallist@3.1.1: {}
From 79f161436e53392389653434cfda98858c8bd552 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Fri, 12 Sep 2025 13:07:58 -0400
Subject: [PATCH 17/20] chore: meeting user id removal and room id requirement
(#635)
* chore: remove meeting user id and make meeting room id required
* meeting room_id optional
* orphaned meeting room ids DATA migration
* ci fix
* fix meeting_room_id_fkey downgrade
* fix migration rollback
* fix: put index back (meeting room id)
* fix: put index back (meeting room id)
* fix: put index back (meeting room id)
* remove noop migrations
---------
Co-authored-by: Igor Loskutov
---
...da2ee_remove_user_id_from_meeting_table.py | 36 ++++++++++++++++++
...lean_up_orphaned_room_id_references_in_.py | 32 ++++++++++++++++
..._make_meeting_room_id_required_and_add_.py | 38 +++++++++++++++++++
...make_meeting_room_id_nullable_but_keep_.py | 34 +++++++++++++++++
server/reflector/db/meetings.py | 34 +++++------------
server/reflector/views/rooms.py | 1 -
server/tests/test_cleanup.py | 2 -
7 files changed, 150 insertions(+), 27 deletions(-)
create mode 100644 server/migrations/versions/0ce521cda2ee_remove_user_id_from_meeting_table.py
create mode 100644 server/migrations/versions/2ae3db106d4e_clean_up_orphaned_room_id_references_in_.py
create mode 100644 server/migrations/versions/6dec9fb5b46c_make_meeting_room_id_required_and_add_.py
create mode 100644 server/migrations/versions/def1b5867d4c_make_meeting_room_id_nullable_but_keep_.py
diff --git a/server/migrations/versions/0ce521cda2ee_remove_user_id_from_meeting_table.py b/server/migrations/versions/0ce521cda2ee_remove_user_id_from_meeting_table.py
new file mode 100644
index 00000000..2e76e8a6
--- /dev/null
+++ b/server/migrations/versions/0ce521cda2ee_remove_user_id_from_meeting_table.py
@@ -0,0 +1,36 @@
+"""remove user_id from meeting table
+
+Revision ID: 0ce521cda2ee
+Revises: 6dec9fb5b46c
+Create Date: 2025-09-10 12:40:55.688899
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "0ce521cda2ee"
+down_revision: Union[str, None] = "6dec9fb5b46c"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.drop_column("user_id")
+
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.add_column(
+ sa.Column("user_id", sa.VARCHAR(), autoincrement=False, nullable=True)
+ )
+
+ # ### end Alembic commands ###
diff --git a/server/migrations/versions/2ae3db106d4e_clean_up_orphaned_room_id_references_in_.py b/server/migrations/versions/2ae3db106d4e_clean_up_orphaned_room_id_references_in_.py
new file mode 100644
index 00000000..c091ab49
--- /dev/null
+++ b/server/migrations/versions/2ae3db106d4e_clean_up_orphaned_room_id_references_in_.py
@@ -0,0 +1,32 @@
+"""clean up orphaned room_id references in meeting table
+
+Revision ID: 2ae3db106d4e
+Revises: def1b5867d4c
+Create Date: 2025-09-11 10:35:15.759967
+
+"""
+
+from typing import Sequence, Union
+
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "2ae3db106d4e"
+down_revision: Union[str, None] = "def1b5867d4c"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # Set room_id to NULL for meetings that reference non-existent rooms
+ op.execute("""
+ UPDATE meeting
+ SET room_id = NULL
+ WHERE room_id IS NOT NULL
+ AND room_id NOT IN (SELECT id FROM room WHERE id IS NOT NULL)
+ """)
+
+
+def downgrade() -> None:
+ # Cannot restore orphaned references - no operation needed
+ pass
diff --git a/server/migrations/versions/6dec9fb5b46c_make_meeting_room_id_required_and_add_.py b/server/migrations/versions/6dec9fb5b46c_make_meeting_room_id_required_and_add_.py
new file mode 100644
index 00000000..20828c65
--- /dev/null
+++ b/server/migrations/versions/6dec9fb5b46c_make_meeting_room_id_required_and_add_.py
@@ -0,0 +1,38 @@
+"""make meeting room_id required and add foreign key
+
+Revision ID: 6dec9fb5b46c
+Revises: 61882a919591
+Create Date: 2025-09-10 10:47:06.006819
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "6dec9fb5b46c"
+down_revision: Union[str, None] = "61882a919591"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=False)
+ batch_op.create_foreign_key(
+ None, "room", ["room_id"], ["id"], ondelete="CASCADE"
+ )
+
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.drop_constraint("meeting_room_id_fkey", type_="foreignkey")
+ batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=True)
+
+ # ### end Alembic commands ###
diff --git a/server/migrations/versions/def1b5867d4c_make_meeting_room_id_nullable_but_keep_.py b/server/migrations/versions/def1b5867d4c_make_meeting_room_id_nullable_but_keep_.py
new file mode 100644
index 00000000..982bea27
--- /dev/null
+++ b/server/migrations/versions/def1b5867d4c_make_meeting_room_id_nullable_but_keep_.py
@@ -0,0 +1,34 @@
+"""make meeting room_id nullable but keep foreign key
+
+Revision ID: def1b5867d4c
+Revises: 0ce521cda2ee
+Create Date: 2025-09-11 09:42:18.697264
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "def1b5867d4c"
+down_revision: Union[str, None] = "0ce521cda2ee"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=True)
+
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("meeting", schema=None) as batch_op:
+ batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=False)
+
+ # ### end Alembic commands ###
diff --git a/server/reflector/db/meetings.py b/server/reflector/db/meetings.py
index bb7366b1..c3821241 100644
--- a/server/reflector/db/meetings.py
+++ b/server/reflector/db/meetings.py
@@ -17,8 +17,12 @@ meetings = sa.Table(
sa.Column("host_room_url", sa.String),
sa.Column("start_date", sa.DateTime(timezone=True)),
sa.Column("end_date", sa.DateTime(timezone=True)),
- sa.Column("user_id", sa.String),
- sa.Column("room_id", sa.String),
+ sa.Column(
+ "room_id",
+ sa.String,
+ sa.ForeignKey("room.id", ondelete="CASCADE"),
+ nullable=True,
+ ),
sa.Column("is_locked", sa.Boolean, nullable=False, server_default=sa.false()),
sa.Column("room_mode", sa.String, nullable=False, server_default="normal"),
sa.Column("recording_type", sa.String, nullable=False, server_default="cloud"),
@@ -80,8 +84,7 @@ class Meeting(BaseModel):
host_room_url: str
start_date: datetime
end_date: datetime
- user_id: str | None = None
- room_id: str | None = None
+ room_id: str | None
is_locked: bool = False
room_mode: Literal["normal", "group"] = "normal"
recording_type: Literal["none", "local", "cloud"] = "cloud"
@@ -100,12 +103,8 @@ class MeetingController:
host_room_url: str,
start_date: datetime,
end_date: datetime,
- user_id: str,
room: Room,
):
- """
- Create a new meeting
- """
meeting = Meeting(
id=id,
room_name=room_name,
@@ -113,7 +112,6 @@ class MeetingController:
host_room_url=host_room_url,
start_date=start_date,
end_date=end_date,
- user_id=user_id,
room_id=room.id,
is_locked=room.is_locked,
room_mode=room.room_mode,
@@ -125,19 +123,13 @@ class MeetingController:
return meeting
async def get_all_active(self) -> list[Meeting]:
- """
- Get active meetings.
- """
query = meetings.select().where(meetings.c.is_active)
return await get_database().fetch_all(query)
async def get_by_room_name(
self,
room_name: str,
- ) -> Meeting:
- """
- Get a meeting by room name.
- """
+ ) -> Meeting | None:
query = meetings.select().where(meetings.c.room_name == room_name)
result = await get_database().fetch_one(query)
if not result:
@@ -145,10 +137,7 @@ class MeetingController:
return Meeting(**result)
- async def get_active(self, room: Room, current_time: datetime) -> Meeting:
- """
- Get latest active meeting for a room.
- """
+ async def get_active(self, room: Room, current_time: datetime) -> Meeting | None:
end_date = getattr(meetings.c, "end_date")
query = (
meetings.select()
@@ -168,9 +157,6 @@ class MeetingController:
return Meeting(**result)
async def get_by_id(self, meeting_id: str, **kwargs) -> Meeting | None:
- """
- Get a meeting by id
- """
query = meetings.select().where(meetings.c.id == meeting_id)
result = await get_database().fetch_one(query)
if not result:
@@ -201,7 +187,7 @@ class MeetingConsentController:
result = await get_database().fetch_one(query)
if result is None:
return None
- return MeetingConsent(**result) if result else None
+ return MeetingConsent(**result)
async def upsert(self, consent: MeetingConsent) -> MeetingConsent:
"""Create new consent or update existing one for authenticated users"""
diff --git a/server/reflector/views/rooms.py b/server/reflector/views/rooms.py
index 38b611d6..546c1dd3 100644
--- a/server/reflector/views/rooms.py
+++ b/server/reflector/views/rooms.py
@@ -209,7 +209,6 @@ async def rooms_create_meeting(
host_room_url=whereby_meeting["hostRoomUrl"],
start_date=parse_datetime_with_timezone(whereby_meeting["startDate"]),
end_date=parse_datetime_with_timezone(whereby_meeting["endDate"]),
- user_id=user_id,
room=room,
)
except (asyncpg.exceptions.UniqueViolationError, sqlite3.IntegrityError):
diff --git a/server/tests/test_cleanup.py b/server/tests/test_cleanup.py
index 3c5149ae..2cb8614c 100644
--- a/server/tests/test_cleanup.py
+++ b/server/tests/test_cleanup.py
@@ -105,7 +105,6 @@ async def test_cleanup_deletes_associated_meeting_and_recording():
host_room_url="https://example.com/meeting-host",
start_date=old_date,
end_date=old_date + timedelta(hours=1),
- user_id=None,
room_id=None,
)
)
@@ -241,7 +240,6 @@ async def test_meeting_consent_cascade_delete():
host_room_url="https://example.com/cascade-test-host",
start_date=datetime.now(timezone.utc),
end_date=datetime.now(timezone.utc) + timedelta(hours=1),
- user_id="test-user",
room_id=None,
)
)
From 5f143fe3640875dcb56c26694254a93189281d17 Mon Sep 17 00:00:00 2001
From: Mathieu Virbel
Date: Mon, 15 Sep 2025 10:49:20 -0600
Subject: [PATCH 18/20] fix: zulip and consent handler on the file pipeline
(#645)
---
.../reflector/pipelines/main_file_pipeline.py | 46 +++++++++++++------
1 file changed, 32 insertions(+), 14 deletions(-)
diff --git a/server/reflector/pipelines/main_file_pipeline.py b/server/reflector/pipelines/main_file_pipeline.py
index 5c57dddb..ce9d000e 100644
--- a/server/reflector/pipelines/main_file_pipeline.py
+++ b/server/reflector/pipelines/main_file_pipeline.py
@@ -12,7 +12,7 @@ from pathlib import Path
import av
import structlog
-from celery import shared_task
+from celery import chain, shared_task
from reflector.asynctask import asynctask
from reflector.db.rooms import rooms_controller
@@ -26,6 +26,8 @@ from reflector.logger import logger
from reflector.pipelines.main_live_pipeline import (
PipelineMainBase,
broadcast_to_sockets,
+ task_cleanup_consent,
+ task_pipeline_post_to_zulip,
)
from reflector.processors import (
AudioFileWriterProcessor,
@@ -379,6 +381,28 @@ class PipelineMainFile(PipelineMainBase):
await processor.flush()
+@shared_task
+@asynctask
+async def task_send_webhook_if_needed(*, transcript_id: str):
+ """Send webhook if this is a room recording with webhook configured"""
+ transcript = await transcripts_controller.get_by_id(transcript_id)
+ if not transcript:
+ return
+
+ if transcript.source_kind == SourceKind.ROOM and transcript.room_id:
+ room = await rooms_controller.get_by_id(transcript.room_id)
+ if room and room.webhook_url:
+ logger.info(
+ "Dispatching webhook",
+ transcript_id=transcript_id,
+ room_id=room.id,
+ webhook_url=room.webhook_url,
+ )
+ send_transcript_webhook.delay(
+ transcript_id, room.id, event_id=uuid.uuid4().hex
+ )
+
+
@shared_task
@asynctask
async def task_pipeline_file_process(*, transcript_id: str):
@@ -406,16 +430,10 @@ async def task_pipeline_file_process(*, transcript_id: str):
await pipeline.set_status(transcript_id, "error")
raise
- # Trigger webhook if this is a room recording with webhook configured
- if transcript.source_kind == SourceKind.ROOM and transcript.room_id:
- room = await rooms_controller.get_by_id(transcript.room_id)
- if room and room.webhook_url:
- logger.info(
- "Dispatching webhook task",
- transcript_id=transcript_id,
- room_id=room.id,
- webhook_url=room.webhook_url,
- )
- send_transcript_webhook.delay(
- transcript_id, room.id, event_id=uuid.uuid4().hex
- )
+ # Run post-processing chain: consent cleanup -> zulip -> webhook
+ post_chain = chain(
+ task_cleanup_consent.si(transcript_id=transcript_id),
+ task_pipeline_post_to_zulip.si(transcript_id=transcript_id),
+ task_send_webhook_if_needed.si(transcript_id=transcript_id),
+ )
+ post_chain.delay()
From 3f1fe8c9bf0a63f26555b6bdf22f73846d3f88b1 Mon Sep 17 00:00:00 2001
From: Igor Monadical
Date: Mon, 15 Sep 2025 14:19:10 -0400
Subject: [PATCH 19/20] chore: remove timeout-based auth session logic (#649)
* remove timeout-based auth session logic
* remove timeout-based auth session logic
---------
Co-authored-by: Igor Loskutov
---
www/app/lib/apiClient.tsx | 40 ++++++++++++++++++++-------------------
www/app/lib/types.ts | 10 ++++++----
2 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx
index 86f8f161..a5cec06b 100644
--- a/www/app/lib/apiClient.tsx
+++ b/www/app/lib/apiClient.tsx
@@ -3,8 +3,10 @@
import createClient from "openapi-fetch";
import type { paths } from "../reflector-api";
import createFetchClient from "openapi-react-query";
-import { assertExistsAndNonEmptyString } from "./utils";
+import { assertExistsAndNonEmptyString, parseNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";
+import { getSession } from "next-auth/react";
+import { assertExtendedToken } from "./types";
export const API_URL = !isBuildPhase
? assertExistsAndNonEmptyString(
@@ -21,29 +23,29 @@ export const client = createClient({
baseUrl: API_URL,
});
-const waitForAuthTokenDefinitivePresenceOrAbscence = async () => {
- let tries = 0;
- let time = 0;
- const STEP = 100;
- while (currentAuthToken === undefined) {
- await new Promise((resolve) => setTimeout(resolve, STEP));
- time += STEP;
- tries++;
- // most likely first try is more than enough, if it's more there's already something weird happens
- if (tries > 10) {
- // even when there's no auth assumed at all, we probably should explicitly call configureApiAuth(null)
- throw new Error(
- `Could not get auth token definitive presence/absence in ${time}ms. not calling configureApiAuth?`,
- );
- }
+// will assert presence/absence of login initially
+const initialSessionPromise = getSession();
+
+const waitForAuthTokenDefinitivePresenceOrAbsence = async () => {
+ const initialSession = await initialSessionPromise;
+ if (currentAuthToken === undefined) {
+ currentAuthToken =
+ initialSession === null
+ ? null
+ : assertExtendedToken(initialSession).accessToken;
}
+ // otherwise already overwritten by external forces
+ return currentAuthToken;
};
client.use({
async onRequest({ request }) {
- await waitForAuthTokenDefinitivePresenceOrAbscence();
- if (currentAuthToken) {
- request.headers.set("Authorization", `Bearer ${currentAuthToken}`);
+ const token = await waitForAuthTokenDefinitivePresenceOrAbsence();
+ if (token !== null) {
+ request.headers.set(
+ "Authorization",
+ `Bearer ${parseNonEmptyString(token)}`,
+ );
}
// XXX Only set Content-Type if not already set (FormData will set its own boundary)
// This is a work around for uploading file, we're passing a formdata
diff --git a/www/app/lib/types.ts b/www/app/lib/types.ts
index af5625ec..7bcb522b 100644
--- a/www/app/lib/types.ts
+++ b/www/app/lib/types.ts
@@ -21,7 +21,7 @@ export interface CustomSession extends Session {
// assumption that JWT is JWTWithAccessToken - we set it in jwt callback of auth; typing isn't strong around there
// but the assumption is crucial to auth working
export const assertExtendedToken = (
- t: T,
+ t: Exclude,
): T & {
accessTokenExpires: number;
accessToken: string;
@@ -45,7 +45,7 @@ export const assertExtendedToken = (
};
export const assertExtendedTokenAndUserId = (
- t: T,
+ t: Exclude,
): T & {
accessTokenExpires: number;
accessToken: string;
@@ -55,7 +55,7 @@ export const assertExtendedTokenAndUserId = (
} => {
const extendedToken = assertExtendedToken(t);
if (typeof (extendedToken.user as any)?.id === "string") {
- return t as T & {
+ return t as Exclude & {
accessTokenExpires: number;
accessToken: string;
user: U & {
@@ -67,7 +67,9 @@ export const assertExtendedTokenAndUserId = (
};
// best attempt to check the session is valid
-export const assertCustomSession = (s: S): CustomSession => {
+export const assertCustomSession = (
+ s: Exclude,
+): CustomSession => {
const r = assertExtendedTokenAndUserId(s);
// no other checks for now
return r as CustomSession;
From c546e69739e68bb74fbc877eb62609928e5b8de6 Mon Sep 17 00:00:00 2001
From: Mathieu Virbel
Date: Mon, 15 Sep 2025 12:34:51 -0600
Subject: [PATCH 20/20] fix: zulip stream and topic selection in share dialog
(#644)
* fix: zulip stream and topic selection in share dialog
Replace useListCollection with createListCollection to match the working
room edit implementation. This ensures collections update when data loads,
fixing the issue where streams and topics wouldn't appear until navigation.
* fix: wrap createListCollection in useMemo to prevent recreation on every render
Both streamCollection and topicCollection are now memoized to improve performance
and prevent unnecessary re-renders of Combobox components
---
www/app/(app)/transcripts/shareZulip.tsx | 43 +++++++++++-------------
1 file changed, 19 insertions(+), 24 deletions(-)
diff --git a/www/app/(app)/transcripts/shareZulip.tsx b/www/app/(app)/transcripts/shareZulip.tsx
index 5cee16c1..bee14822 100644
--- a/www/app/(app)/transcripts/shareZulip.tsx
+++ b/www/app/(app)/transcripts/shareZulip.tsx
@@ -14,8 +14,7 @@ import {
Checkbox,
Combobox,
Spinner,
- useFilter,
- useListCollection,
+ createListCollection,
} from "@chakra-ui/react";
import { TbBrandZulip } from "react-icons/tb";
import {
@@ -48,8 +47,6 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
const { data: topics = [] } = useZulipTopics(selectedStreamId);
const postToZulipMutation = useTranscriptPostToZulip();
- const { contains } = useFilter({ sensitivity: "base" });
-
const streamItems = useMemo(() => {
return streams.map((stream: Stream) => ({
label: stream.name,
@@ -64,17 +61,21 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
}));
}, [topics]);
- const { collection: streamItemsCollection, filter: streamItemsFilter } =
- useListCollection({
- initialItems: streamItems,
- filter: contains,
- });
+ const streamCollection = useMemo(
+ () =>
+ createListCollection({
+ items: streamItems,
+ }),
+ [streamItems],
+ );
- const { collection: topicItemsCollection, filter: topicItemsFilter } =
- useListCollection({
- initialItems: topicItems,
- filter: contains,
- });
+ const topicCollection = useMemo(
+ () =>
+ createListCollection({
+ items: topicItems,
+ }),
+ [topicItems],
+ );
// Update selected stream ID when stream changes
useEffect(() => {
@@ -156,15 +157,12 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
#
{
setTopic(undefined);
setStream(e.value[0]);
}}
- onInputValueChange={(e) =>
- streamItemsFilter(e.inputValue)
- }
openOnClick={true}
positioning={{
strategy: "fixed",
@@ -181,7 +179,7 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
No streams found
- {streamItemsCollection.items.map((item) => (
+ {streamItems.map((item) => (
{item.label}
@@ -197,12 +195,9 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
#
setTopic(e.value[0])}
- onInputValueChange={(e) =>
- topicItemsFilter(e.inputValue)
- }
openOnClick
selectionBehavior="replace"
skipAnimationOnMount={true}
@@ -222,7 +217,7 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
No topics found
- {topicItemsCollection.items.map((item) => (
+ {topicItems.map((item) => (
{item.label}