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") {