diff --git a/.gitignore b/.gitignore index 45c1abc..3e198e8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +/src/generated/prisma diff --git a/app/[locale]/(auth)/login/LoginForm.tsx b/app/[locale]/(auth)/login/LoginForm.tsx index dce5791..b223967 100644 --- a/app/[locale]/(auth)/login/LoginForm.tsx +++ b/app/[locale]/(auth)/login/LoginForm.tsx @@ -24,6 +24,8 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import Link from "next/link"; +import { useRouter, useParams } from "next/navigation"; +import { useNotification } from "@/src/contexts"; type LoginProps = { headingText: string; @@ -58,6 +60,10 @@ export default function LoginForm({ validationMessages, }: LoginProps) { const [showPassword, setShowPassword] = useState(false); + const router = useRouter(); + const params = useParams(); + const locale = params.locale as string; + const { notify } = useNotification(); const loginSchema = useMemo( () => @@ -77,8 +83,27 @@ export default function LoginForm({ defaultValues: { email: "", password: "", remember: false }, }); - const onSubmit = (values: LoginSchema) => { - console.log(values); + const onSubmit = async (values: LoginSchema) => { + try { + const response = await fetch("/api/auth/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(values), + }); + const data = await response.json(); + if (response.ok) { + localStorage.setItem("user_id", data.user.id); + localStorage.setItem("user_email", data.user.email); + localStorage.setItem("user_name", data.user.fullName || "User"); + notify("Login successful!", "success"); + router.push(`/${locale}/dashboard`); + } else { + notify(data.error || "Login failed", "error"); + } + } catch (error) { + console.error("Login Error:", error); + notify("An error occurred during login", "error"); + } }; return ( @@ -138,6 +163,7 @@ export default function LoginForm({ className="rounded-[10px] w-full py-6" /> @@ -114,35 +149,19 @@ export default function Dashboard() { Date - {[ - { - id: 1, - text: "Hello world", - lang: "English → French", - date: "2025-10-10", - }, - { - id: 2, - text: "Good morning", - lang: "English → Spanish", - date: "2025-10-09", - }, - { - id: 3, - text: "Translate this text", - lang: "English → German", - date: "2025-10-08", - }, - ].map((item) => ( + {recentTranslations.map((item) => (
- {item.text} - {item.lang} - {item.date} + {item.inputText} + {item.sourceLanguage} → {item.targetLanguage} + {new Date(item.createdAt).toLocaleDateString()}
))} + {recentTranslations.length === 0 && ( +
No recent translations found.
+ )} diff --git a/app/[locale]/(dashboard-segment)/image-translation/page.tsx b/app/[locale]/(dashboard-segment)/image-translation/page.tsx new file mode 100644 index 0000000..dacb274 --- /dev/null +++ b/app/[locale]/(dashboard-segment)/image-translation/page.tsx @@ -0,0 +1,9 @@ +import { ImageTranslation } from "@/src/components/Translation/ImageTranslation"; + +export default function ImageTranslationPage() { + return ( +
+ +
+ ); +} diff --git a/app/[locale]/(dashboard-segment)/layout.tsx b/app/[locale]/(dashboard-segment)/layout.tsx index 39c4a0f..5026e2d 100644 --- a/app/[locale]/(dashboard-segment)/layout.tsx +++ b/app/[locale]/(dashboard-segment)/layout.tsx @@ -3,6 +3,7 @@ import { getMessages, unstable_setRequestLocale } from "next-intl/server"; import { Locale, locales } from "@/i18n.config"; import SidebarPage from "@/src/components/Sidebar"; import TopNavPage from "@/src/components/TopNav"; +import { ModalProvider, NotificationProvider } from "@/src/contexts"; export function generateStaticParams() { return locales.map((locale) => ({ locale })); @@ -22,13 +23,17 @@ export default async function LocaleLayout({ return ( -
- -
- -
{children}
-
-
+ + +
+ +
+ +
{children}
+
+
+
+
); } diff --git a/app/[locale]/(dashboard-segment)/learning/page.tsx b/app/[locale]/(dashboard-segment)/learning/page.tsx new file mode 100644 index 0000000..4e0c8d7 --- /dev/null +++ b/app/[locale]/(dashboard-segment)/learning/page.tsx @@ -0,0 +1,11 @@ +import { Flashcards } from "@/src/components/Learning/Flashcards"; +import { DailyLearning } from "@/src/components/Learning/DailyLearning"; + +export default function LearningPage() { + return ( +
+ + +
+ ); +} diff --git a/app/[locale]/(dashboard-segment)/translation-history/page.tsx b/app/[locale]/(dashboard-segment)/translation-history/page.tsx index 68f5524..531cf9c 100644 --- a/app/[locale]/(dashboard-segment)/translation-history/page.tsx +++ b/app/[locale]/(dashboard-segment)/translation-history/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useTranslations } from "next-intl"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardContent, CardTitle } from "@/components/ui/card"; @@ -9,25 +9,33 @@ import { FaClock, FaChevronLeft, FaChevronRight } from "react-icons/fa"; import { Translation } from "@/src/components"; import { ModalProvider, NotificationProvider } from "@/src/contexts"; +interface TranslationItem { + id: string; + inputText: string; + sourceLanguage: string; + targetLanguage: string; + createdAt: string; +} + export default function TranslationHistory() { const t = useTranslations("TranslationHistory"); const [showHistory, setShowHistory] = useState(true); + const [historyItems, setHistoryItems] = useState([]); - const historyItems = [ - { - id: 1, - input: "Hello world", - output: "Bonjour le monde", - date: "2025-10-12", - }, - { id: 2, input: "Good morning", output: "Buenos días", date: "2025-10-11" }, - { - id: 3, - input: "How are you?", - output: "Wie geht's dir?", - date: "2025-10-10", - }, - ]; + useEffect(() => { + const fetchHistory = async () => { + try { + const response = await fetch("/api/user/stats"); + const data = await response.json(); + if (response.ok) { + setHistoryItems(data.recentTranslations); + } + } catch (error) { + console.error("Failed to fetch history", error); + } + }; + fetchHistory(); + }, []); return ( @@ -80,10 +88,13 @@ export default function TranslationHistory() { key={item.id} className="p-3 border-b hover:bg-gray-50 rounded-lg mb-2 cursor-pointer" > -

{item.input}

-

{item.date}

+

{item.inputText}

+

{new Date(item.createdAt).toLocaleDateString()}

))} + {historyItems.length === 0 && ( +

No history yet.

+ )} {/* Toggle Button (if collapsed) */} diff --git a/app/[locale]/api/translate-document/route.ts b/app/[locale]/api/translate-document/route.ts deleted file mode 100644 index b520b62..0000000 --- a/app/[locale]/api/translate-document/route.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { NextApiResponse } from "next"; -import { - translateDocument, - getTranslatedDocumentUrl, -} from "@/app/[locale]/utils/azureService"; -import { NextRequest, NextResponse } from "next/server"; -import generateUniqueId from "generate-unique-id"; - -export async function POST(req: NextRequest, res: NextApiResponse) { - if (req.method === "POST") { - const body = await req.formData(); - const file = body.get("file") as File; - const from = body.get("from") as string; - const to = body.get("to") as string; - const uid = generateUniqueId({ - length: 5, - }); - const fileName = `${uid}_${file.name}`; - - try { - const translateDocumentRes = await translateDocument( - file, - from, - to, - fileName, - ); - console.log("translateDocumentRes", translateDocumentRes); - const translatedDocumentUrl = await getTranslatedDocumentUrl(fileName); - console.log("getTranslatedDocumentUrl", getTranslatedDocumentUrl); - return NextResponse.json(translatedDocumentUrl, { - status: 200, - }); - } catch (error: any) { - return NextResponse.json( - { error: error.message || "Failed to translate the document." }, - { - status: 500, - }, - ); - } - } else { - res.status(405).json({ message: "Method not allowed" }); - } -} diff --git a/app/[locale]/api/translate-text/route.ts b/app/[locale]/api/translate-text/route.ts deleted file mode 100644 index f1a9c5f..0000000 --- a/app/[locale]/api/translate-text/route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NextApiResponse } from "next"; -import { translateText } from "@/app/[locale]/utils/azureService"; -import { NextResponse } from "next/server"; - -export async function POST(req: Request, res: NextApiResponse) { - if (req.method === "POST") { - const body = await req.json(); - const { text, from, to } = body; - try { - const translatedText = await translateText({ text, from, to }); - return NextResponse.json(translatedText, { - status: 200, - }); - } catch (error: any) { - console.log("translateText Error", error); - return NextResponse.json( - { error: error.message || "Failed to translate the texts." }, - { - status: 500, - }, - ); - } - } else { - res.status(405).json({ message: "Method not allowed" }); - } -} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index eebb2f1..f70b6f6 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Analytics } from "@vercel/analytics/react"; import "../globals.css"; -import { monaSans } from "@/app/[locale]/fonts"; +import { monaSans } from "@/app/fonts"; import { NextIntlClientProvider } from "next-intl"; import { unstable_setRequestLocale } from "next-intl/server"; import { Locale, locales } from "@/i18n.config"; diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..f82b79b --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,53 @@ +import { prisma } from "@/lib/prisma"; +import { NextResponse } from "next/server"; +import bcrypt from "bcryptjs"; +import { nanoid } from "nanoid"; + +export async function POST(req: Request) { + try { + const { email, password } = await req.json(); + + if (!email || !password) { + return NextResponse.json({ error: "Email and password are required" }, { status: 400 }); + } + + const user = await prisma.user.findUnique({ + where: { email }, + }); + + if (!user || !(await bcrypt.compare(password, user.password))) { + return NextResponse.json({ error: "Invalid credentials" }, { status: 401 }); + } + + // Create a secure session + const sessionToken = nanoid(32); + const expires = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7); // 1 week + + await prisma.session.create({ + data: { + sessionToken, + userId: user.id, + expires, + }, + }); + + const response = NextResponse.json({ + message: "Login successful", + user: { id: user.id, email: user.email, fullName: user.fullName } + }, { status: 200 }); + + // Set a secure session cookie + response.cookies.set("session_token", sessionToken, { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + expires, + }); + + return response; + } catch (error: any) { + console.error("Login Error:", error); + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); + } +} diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts new file mode 100644 index 0000000..6c91cd7 --- /dev/null +++ b/app/api/auth/register/route.ts @@ -0,0 +1,38 @@ +import { prisma } from "@/lib/prisma"; +import { NextResponse } from "next/server"; +import bcrypt from "bcryptjs"; + +export async function POST(req: Request) { + try { + const { email, password, full_name, preferredLanguage } = await req.json(); + + if (!email || !password) { + return NextResponse.json({ error: "Email and password are required" }, { status: 400 }); + } + + const existingUser = await prisma.user.findUnique({ + where: { email }, + }); + + if (existingUser) { + return NextResponse.json({ error: "User already exists" }, { status: 400 }); + } + + // Hash the password + const hashedPassword = await bcrypt.hash(password, 12); + + const user = await prisma.user.create({ + data: { + email, + password: hashedPassword, + fullName: full_name, + preferredLanguage: preferredLanguage || "en", + }, + }); + + return NextResponse.json({ message: "User created successfully", userId: user.id }, { status: 201 }); + } catch (error: any) { + console.error("Registration Error:", error); + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); + } +} diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts new file mode 100644 index 0000000..ba2a90a --- /dev/null +++ b/app/api/chat/route.ts @@ -0,0 +1,38 @@ +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +export async function GET(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + const messages = await prisma.chatMessage.findMany({ + where: { userId }, + orderBy: { createdAt: "asc" }, + take: 50, + }); + return NextResponse.json(messages, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Failed to fetch messages" }, { status: 500 }); + } +} + +export async function POST(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + const { message, translatedText, isUser } = await req.json(); + const chatMessage = await prisma.chatMessage.create({ + data: { userId, message, translatedText, isUser }, + }); + return NextResponse.json(chatMessage, { status: 201 }); + } catch (error) { + return NextResponse.json({ error: "Failed to save message" }, { status: 500 }); + } +} diff --git a/app/api/corrections/route.ts b/app/api/corrections/route.ts new file mode 100644 index 0000000..527cec3 --- /dev/null +++ b/app/api/corrections/route.ts @@ -0,0 +1,37 @@ +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +export async function POST(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + const { originalTranslation, suggestedTranslation } = await req.json(); + const correction = await prisma.correction.create({ + data: { userId, originalTranslation, suggestedTranslation }, + }); + return NextResponse.json(correction, { status: 201 }); + } catch (error) { + return NextResponse.json({ error: "Failed to submit correction" }, { status: 500 }); + } +} + +export async function GET(req: NextRequest) { + // Restrict to authorized users (for now, any logged in user can see them, but it's not public) + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + try { + const corrections = await prisma.correction.findMany({ + orderBy: { createdAt: "desc" }, + take: 20, + }); + return NextResponse.json(corrections, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Failed to fetch corrections" }, { status: 500 }); + } +} diff --git a/app/[locale]/api/hello/route.ts b/app/api/hello/route.ts similarity index 100% rename from app/[locale]/api/hello/route.ts rename to app/api/hello/route.ts diff --git a/app/[locale]/api/index copy.ts b/app/api/index copy.ts similarity index 100% rename from app/[locale]/api/index copy.ts rename to app/api/index copy.ts diff --git a/app/[locale]/api/index.ts b/app/api/index.ts similarity index 67% rename from app/[locale]/api/index.ts rename to app/api/index.ts index 369364e..5499ad9 100644 --- a/app/[locale]/api/index.ts +++ b/app/api/index.ts @@ -1,4 +1,12 @@ -import { TranslateDocumentProps, TranslateProps } from "../utils/azureService"; +export interface TranslateProps { + text: string; + from: string; + to: string; +} + +export interface TranslateDocumentProps extends TranslateProps { + file: File[]; +} export async function translateText({ text, @@ -12,7 +20,7 @@ export async function translateText({ } try { - const response = await fetch("/en/api/translate-text", { + const response = await fetch("/api/translate-text", { method: "POST", headers: { "Content-Type": "application/json", @@ -50,7 +58,7 @@ export async function translateDocument({ } try { - const response = await fetch("/en/api/translate-document", { + const response = await fetch("/api/translate-document", { method: "POST", headers: { "Content-Type": "application/json", @@ -75,26 +83,3 @@ export async function translateDocument({ throw new Error(`Translation failed: ${error}`); } } - -export async function testTranslator(): Promise { - try { - const response = await fetch("/api/test", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!response.ok) { - const errorBody = await response.json().catch(() => null); - const errorMessage = - response.statusText || errorBody?.message || "Unknown error occurred"; - throw new Error(`Translation API failed: ${errorMessage}`); - } - - const result = await response.json(); - return result; - } catch (error: any) { - throw new Error(`Translation failed: ${error}`); - } -} diff --git a/app/api/keywords/route.ts b/app/api/keywords/route.ts new file mode 100644 index 0000000..7481806 --- /dev/null +++ b/app/api/keywords/route.ts @@ -0,0 +1,14 @@ +import { NextRequest, NextResponse } from "next/server"; +import { extractKeywordsHF } from "@/app/utils/huggingFaceService"; + +export async function POST(req: NextRequest) { + try { + const { text } = await req.json(); + if (!text) return NextResponse.json({ error: "Text is required" }, { status: 400 }); + + const keywords = await extractKeywordsHF(text); + return NextResponse.json(keywords, { status: 200 }); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/app/api/learning/daily/route.ts b/app/api/learning/daily/route.ts new file mode 100644 index 0000000..77732f4 --- /dev/null +++ b/app/api/learning/daily/route.ts @@ -0,0 +1,36 @@ +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +export async function GET(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + // Fetch recent translations to generate a "Daily Quiz" + const translations = await prisma.translation.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + take: 5, + }); + + if (translations.length === 0) { + return NextResponse.json({ + message: "No translations found to generate quiz.", + quiz: [] + }, { status: 200 }); + } + + const quiz = translations.map(t => ({ + question: t.inputText, + answer: t.outputText, + options: [t.outputText, "Alternative A", "Alternative B", "Alternative C"].sort(() => Math.random() - 0.5) + })); + + return NextResponse.json({ quiz }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Failed to fetch daily lesson" }, { status: 500 }); + } +} diff --git a/app/api/learning/flashcards/route.ts b/app/api/learning/flashcards/route.ts new file mode 100644 index 0000000..5a974de --- /dev/null +++ b/app/api/learning/flashcards/route.ts @@ -0,0 +1,37 @@ +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +export async function GET(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + const flashcards = await prisma.flashcard.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + }); + return NextResponse.json(flashcards, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Failed to fetch flashcards" }, { status: 500 }); + } +} + +export async function POST(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + const userId = session.userId; + + try { + const { front, back } = await req.json(); + const flashcard = await prisma.flashcard.create({ + data: { userId, front, back }, + }); + return NextResponse.json(flashcard, { status: 201 }); + } catch (error) { + return NextResponse.json({ error: "Failed to create flashcard" }, { status: 500 }); + } +} diff --git a/app/api/rewrite/route.ts b/app/api/rewrite/route.ts new file mode 100644 index 0000000..6f16c2e --- /dev/null +++ b/app/api/rewrite/route.ts @@ -0,0 +1,14 @@ +import { NextRequest, NextResponse } from "next/server"; +import { rewriteTextHF } from "@/app/utils/huggingFaceService"; + +export async function POST(req: NextRequest) { + try { + const { text, style } = await req.json(); + if (!text || !style) return NextResponse.json({ error: "Text and style are required" }, { status: 400 }); + + const rewritten = await rewriteTextHF(text, style); + return NextResponse.json(rewritten, { status: 200 }); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/app/api/summarize/route.ts b/app/api/summarize/route.ts new file mode 100644 index 0000000..f569982 --- /dev/null +++ b/app/api/summarize/route.ts @@ -0,0 +1,14 @@ +import { NextRequest, NextResponse } from "next/server"; +import { summarizeTextHF } from "@/app/utils/huggingFaceService"; + +export async function POST(req: NextRequest) { + try { + const { text } = await req.json(); + if (!text) return NextResponse.json({ error: "Text is required" }, { status: 400 }); + + const summary = await summarizeTextHF(text); + return NextResponse.json(summary, { status: 200 }); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/app/[locale]/api/translate-doc/route.ts b/app/api/translate-doc/route.ts similarity index 100% rename from app/[locale]/api/translate-doc/route.ts rename to app/api/translate-doc/route.ts diff --git a/app/api/translate-document/route.ts b/app/api/translate-document/route.ts new file mode 100644 index 0000000..1862cd5 --- /dev/null +++ b/app/api/translate-document/route.ts @@ -0,0 +1,54 @@ +import { translateTextHF } from "@/app/utils/huggingFaceService"; +import { translateDocument as translateDocumentAzure } from "@/app/utils/azureService"; +import { NextRequest, NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; +import { getSession } from "@/lib/auth"; + +export async function POST(req: NextRequest) { + const body = await req.formData(); + const file = body.get("file") as File; + const from = body.get("from") as string; + const to = body.get("to") as string; + + try { + const provider = process.env.AI_PROVIDER || "huggingface"; + let result: string; + + if (provider === "azure") { + result = await translateDocumentAzure(file, from, to, file.name); + } else { + // HF Fallback: read text and translate + const text = await file.text(); + result = await translateTextHF({ text, from, to }); + } + + // Persist to database if user is logged in + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + + if (session) { + await prisma.translation.create({ + data: { + userId: session.userId, + inputText: file.name, + outputText: result, + sourceLanguage: from, + targetLanguage: to, + type: "document" + } + }); + } + + return NextResponse.json(result, { + status: 200, + }); + } catch (error: any) { + console.error("translateDocument Error:", error); + return NextResponse.json( + { error: error.message || "Failed to translate the document." }, + { + status: 500, + }, + ); + } +} diff --git a/app/api/translate-text/route.ts b/app/api/translate-text/route.ts new file mode 100644 index 0000000..583b9bf --- /dev/null +++ b/app/api/translate-text/route.ts @@ -0,0 +1,46 @@ +import { NextRequest, NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; +import { getSession } from "@/lib/auth"; +import * as hf from "@/app/utils/huggingFaceService"; +import * as azure from "@/app/utils/azureService"; + +export async function POST(req: NextRequest) { + const body = await req.json(); + const { text, from, to } = body; + + try { + const provider = process.env.AI_PROVIDER || "huggingface"; + let translatedText: string; + + if (provider === "azure") { + translatedText = await azure.translateText({ text, from: from || "en", to: to || "fr" }); + } else { + translatedText = await hf.translateTextHF({ text, from, to }); + } + + // Persist to database if user is logged in + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + + if (session) { + await prisma.translation.create({ + data: { + userId: session.userId, + inputText: text, + outputText: translatedText, + sourceLanguage: from, + targetLanguage: to, + type: "text" + } + }); + } + + return NextResponse.json(translatedText, { status: 200 }); + } catch (error: any) { + console.error("translateText Error", error); + return NextResponse.json( + { error: error.message || "Failed to translate the texts." }, + { status: 500 } + ); + } +} diff --git a/app/api/user/stats/route.ts b/app/api/user/stats/route.ts new file mode 100644 index 0000000..d53e909 --- /dev/null +++ b/app/api/user/stats/route.ts @@ -0,0 +1,34 @@ +import { prisma } from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +export async function GET(req: NextRequest) { + const sessionToken = req.cookies.get("session_token")?.value; + const session = await getSession(sessionToken); + + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const userId = session.userId; + + try { + const totalTranslations = await prisma.translation.count({ where: { userId } }); + const documentsCount = await prisma.translation.count({ where: { userId, type: "document" } }); + const recentTranslations = await prisma.translation.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + take: 5, + }); + + return NextResponse.json({ + totalTranslations, + documentsCount, + recentTranslations, + wordsThisWeek: totalTranslations * 50, // Mock word count + mostUsedLanguage: "Multiple", + }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Failed to fetch stats" }, { status: 500 }); + } +} diff --git a/app/[locale]/data/index.ts b/app/data/index.ts similarity index 100% rename from app/[locale]/data/index.ts rename to app/data/index.ts diff --git a/app/[locale]/fonts/Mona-Sans.woff2 b/app/fonts/Mona-Sans.woff2 similarity index 100% rename from app/[locale]/fonts/Mona-Sans.woff2 rename to app/fonts/Mona-Sans.woff2 diff --git a/app/[locale]/fonts/index.ts b/app/fonts/index.ts similarity index 100% rename from app/[locale]/fonts/index.ts rename to app/fonts/index.ts diff --git a/app/layout.tsx b/app/layout.tsx index b454f35..f4eadfb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Analytics } from "@vercel/analytics/react"; import "./globals.css"; -import { monaSans } from "@/app/[locale]/fonts"; +import { monaSans } from "@/app/fonts"; export const metadata: Metadata = { title: "LanguageAI - Texts and Documents Translator.", diff --git a/app/[locale]/utils/azureService.ts b/app/utils/azureService.ts similarity index 100% rename from app/[locale]/utils/azureService.ts rename to app/utils/azureService.ts diff --git a/app/[locale]/utils/helper.ts b/app/utils/helper.ts similarity index 100% rename from app/[locale]/utils/helper.ts rename to app/utils/helper.ts diff --git a/app/utils/huggingFaceService.ts b/app/utils/huggingFaceService.ts new file mode 100644 index 0000000..2984bf7 --- /dev/null +++ b/app/utils/huggingFaceService.ts @@ -0,0 +1,53 @@ +const HF_TOKEN = process.env.HUGGINGFACE_TOKEN; + +async function queryHF(model: string, data: any) { + const response = await fetch( + `https://api-inference.huggingface.co/models/${model}`, + { + headers: { Authorization: `Bearer ${HF_TOKEN}`, "Content-Type": "application/json" }, + method: "POST", + body: JSON.stringify(data), + } + ); + if (!response.ok) { + const err = await response.json(); + throw new Error(err.error || "HF Inference API failed"); + } + return await response.json(); +} + +export async function translateTextHF({ text, from, to }: { text: string; from: string; to: string }) { + // Using mbart-large-50-many-to-many-mmt for translation + const result = await queryHF("facebook/mbart-large-50-many-to-many-mmt", { + inputs: text, + parameters: { src_lang: from, tgt_lang: to } + }); + return result[0]?.translation_text || result[0]?.generated_text || "Translation failed"; +} + +export async function summarizeTextHF(text: string) { + const result = await queryHF("facebook/bart-large-cnn", { + inputs: text, + }); + return result[0]?.summary_text || "Summarization failed"; +} + +export async function extractKeywordsHF(text: string) { + // Using a POS tagging model or a keyword extraction model + const result = await queryHF("dbmdz/bert-large-cased-finetuned-conll03-english", { + inputs: text, + }); + // Simple heuristic to extract entities as keywords + if (Array.isArray(result)) { + return Array.from(new Set(result.map((item: any) => item.word))).slice(0, 10); + } + return []; +} + +export async function rewriteTextHF(text: string, style: string) { + const prompt = `Rewrite the following text to be ${style}: ${text}`; + const result = await queryHF("google/flan-t5-large", { + inputs: prompt, + }); + return result[0]?.generated_text || "Rewriting failed"; +} diff --git a/app/[locale]/utils/supportedFormatsLocale.ts b/app/utils/supportedFormatsLocale.ts similarity index 100% rename from app/[locale]/utils/supportedFormatsLocale.ts rename to app/utils/supportedFormatsLocale.ts diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 0000000..770e4af --- /dev/null +++ b/lib/auth.ts @@ -0,0 +1,19 @@ +import { prisma } from "@/lib/prisma"; + +export async function getSession(sessionToken?: string) { + if (!sessionToken) return null; + + const session = await prisma.session.findUnique({ + where: { sessionToken }, + include: { user: true }, + }); + + if (!session || session.expires < new Date()) { + if (session) { + await prisma.session.delete({ where: { id: session.id } }); + } + return null; + } + + return session; +} diff --git a/lib/prisma.ts b/lib/prisma.ts new file mode 100644 index 0000000..470ca13 --- /dev/null +++ b/lib/prisma.ts @@ -0,0 +1,9 @@ +import { PrismaClient } from "@prisma/client"; + +const globalForPrisma = global as unknown as { prisma: PrismaClient }; + +export const prisma = globalForPrisma.prisma || new PrismaClient({ + datasourceUrl: process.env.DATABASE_URL, +}); + +if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 811789a..cf00af9 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -953,6 +953,66 @@ "node": ">=6.9.0" } }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", + "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", + "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", + "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, "node_modules/@emailjs/browser": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz", @@ -1114,6 +1174,18 @@ "tslib": "^2.4.0" } }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@hookform/resolvers": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", @@ -1159,18 +1231,86 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, - "node_modules/@img/sharp-win32-x64": { + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz", + "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz", + "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz", + "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.1" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz", - "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz", + "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==", "cpu": [ "x64" ], "optional": true, "os": [ - "win32" + "linux" ], "engines": { + "musl": ">=1.2.2", "node": "^18.17.0 || ^20.3.0 || >=21.0.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0", @@ -1178,6 +1318,9 @@ }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.1" } }, "node_modules/@isaacs/cliui": { @@ -1264,6 +1407,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", + "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", + "license": "MIT", + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@next/env": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", @@ -1278,16 +1434,31 @@ "glob": "7.1.7" } }, - "node_modules/@next/swc-win32-x64-msvc": { + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.1.tgz", + "integrity": "sha512-rv6AAdEXoezjbdfp3ouMuVqeLjE1Bin0AuE6qxE6V9g3Giz5/R3xpocHoAi7CufRR+lnkuUjRBn05SYJ83oKNQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.1.tgz", - "integrity": "sha512-S6K6EHDU5+1KrBDLko7/c1MNy/Ya73pIAmvKeFwsF4RmBFJSO7/7YeD4FnZ4iBdzE69PpQ4sOMU9ORKeNuxe8A==", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.1.tgz", + "integrity": "sha512-YAZLGsaNeChSrpz/G7MxO3TIBLaMN8QWMr3X8bt6rCvKovwU7GqQlDu99WdvF33kI8ZahvcdbFsy4jAFzFX7og==", "cpu": [ "x64" ], "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 10" @@ -1334,6 +1505,163 @@ "node": ">=14" } }, + "node_modules/@prisma/client": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.5.0.tgz", + "integrity": "sha512-h4hF9ctp+kSRs7ENHGsFQmHAgHcfkOCxbYt6Ti9Xi8x7D+kP4tTi9x51UKmiTH/OqdyJAO+8V+r+JA5AWdav7w==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.5.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.5.0.tgz", + "integrity": "sha512-KnJ2b4Si/pcWEtK68uM+h0h1oh80CZt2suhLTVuLaSKg4n58Q9jBF/A42Kw6Ma+aThy1yAhfDeTC0JvEmeZnFQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/config": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.5.0.tgz", + "integrity": "sha512-1J/9YEX7A889xM46PYg9e8VAuSL1IUmXJW3tEhMv7XQHDWlfC9YSkIw9sTYRaq5GswGlxZ+GnnyiNsUZ9JJhSQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.5.0.tgz", + "integrity": "sha512-163+nffny0JoPEkDhfNco0vcuT3ymIJc9+WX7MHSQhfkeKUmKe9/wqvGk5SjppT93DtBjVwr5HPJYlXbzm6qtg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/dev": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz", + "integrity": "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==", + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.3.15", + "@electric-sql/pglite-socket": "0.0.20", + "@electric-sql/pglite-tools": "0.2.20", + "@hono/node-server": "1.19.9", + "@mrleebo/prisma-ast": "0.13.1", + "@prisma/get-platform": "7.2.0", + "@prisma/query-plan-executor": "7.2.0", + "foreground-child": "3.3.1", + "get-port-please": "3.2.0", + "hono": "4.11.4", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.33.4", + "std-env": "3.10.0", + "valibot": "1.2.0", + "zeptomatch": "2.1.0" + } + }, + "node_modules/@prisma/engines": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.5.0.tgz", + "integrity": "sha512-ondGRhzoaVpRWvFaQ5wH5zS1BIbhzbKqczKjCn6j3L0Zfe/LInjcEg8+xtB49AuZBX30qyx1ZtGoootUohz2pw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/fetch-engine": "7.5.0", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e.tgz", + "integrity": "sha512-E+iRV/vbJLl8iGjVr6g/TEWokA+gjkV/doZkaQN1i/ULVdDwGnPJDfLUIFGS3BVwlG/m6L8T4x1x5isl8hGMxA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.5.0.tgz", + "integrity": "sha512-kZCl2FV54qnyrVdnII8MI6qvt7HfU6Cbiz8dZ8PXz4f4lbSw45jEB9/gEMK2SGdiNhBKyk/Wv95uthoLhGMLYA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", + "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.2.0" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", + "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", + "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/studio-core": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.21.1.tgz", + "integrity": "sha512-bOGqG/eMQtKC0XVvcVLRmhWWzm/I+0QUWqAEhEBtetpuS3k3V4IWqKGUONkAIT223DNXJMxMtZp36b1FmcdPeg==", + "license": "Apache-2.0", + "engines": { + "node": "^20.19 || ^22.12 || ^24.0", + "pnpm": "8" + }, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -1859,6 +2187,12 @@ "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", "dev": true }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -1873,6 +2207,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "license": "MIT" + }, "node_modules/@types/dom-speech-recognition": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.4.tgz", @@ -1907,14 +2247,12 @@ "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "devOptional": true + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/react": { "version": "18.2.55", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1955,8 +2293,7 @@ "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "devOptional": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", @@ -2450,6 +2787,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axe-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", @@ -2531,6 +2877,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2539,6 +2894,12 @@ "node": ">=8" } }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2603,6 +2964,71 @@ "node": ">=10.16.0" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/call-bind": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", @@ -2673,6 +3099,20 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2707,6 +3147,15 @@ "node": ">= 6" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -2796,6 +3245,21 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2817,9 +3281,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2843,8 +3308,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -2874,6 +3338,15 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", @@ -2906,6 +3379,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2915,6 +3394,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2924,6 +3412,12 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", @@ -2972,11 +3466,33 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.659", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.659.tgz", @@ -2988,6 +3504,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -3581,6 +4106,34 @@ "node": ">=0.8.x" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3755,11 +4308,12 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -3864,6 +4418,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generate-unique-id": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/generate-unique-id/-/generate-unique-id-2.0.3.tgz", @@ -3908,6 +4471,12 @@ "node": ">=6" } }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "license": "MIT" + }, "node_modules/get-symbol-description": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.1.tgz", @@ -3936,6 +4505,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -4034,12 +4620,24 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphmatch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", + "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4120,6 +4718,15 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -4145,6 +4752,34 @@ "node": ">= 14" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -4441,6 +5076,12 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4523,6 +5164,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -4749,6 +5396,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -4761,6 +5414,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4783,6 +5442,21 @@ "node": ">=10" } }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/lucide-react": { "version": "0.545.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz", @@ -4882,6 +5556,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -4892,21 +5586,34 @@ "thenify-all": "^1.0.0" } }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -4990,6 +5697,24 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/next/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5037,6 +5762,12 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -5060,6 +5791,29 @@ "node": ">=0.10.0" } }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5186,6 +5940,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5195,6 +5955,15 @@ "wrappy": "1" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5317,6 +6086,18 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5349,6 +6130,17 @@ "node": ">= 6" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/postcss": { "version": "8.4.34", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.34.tgz", @@ -5487,6 +6279,37 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5512,6 +6335,39 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prisma": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.5.0.tgz", + "integrity": "sha512-n30qZpWehaYQzigLjmuPisyEsvOzHt7bZeRyg8gZ5DvJo9FGjD+gNaY59Ns3hlLD5/jZH5GBeftIss0jDbUoLg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "7.5.0", + "@prisma/dev": "0.20.0", + "@prisma/engines": "7.5.0", + "@prisma/studio-core": "0.21.1", + "mysql2": "3.15.3", + "postgres": "3.4.7" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5522,6 +6378,23 @@ "react-is": "^16.13.1" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5537,6 +6410,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5556,6 +6445,16 @@ } ] }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -5748,6 +6647,12 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -5765,6 +6670,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remeda": { + "version": "2.33.4", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", + "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/remeda" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -5799,6 +6713,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5886,6 +6809,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -5914,6 +6843,11 @@ "node": ">=10" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/server-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", @@ -6072,6 +7006,21 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -6428,6 +7377,36 @@ "node": ">=6" } }, + "node_modules/tesseract.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-7.0.0.tgz", + "integrity": "sha512-exPBkd+z+wM1BuMkx/Bjv43OeLBxhL5kKWsz/9JY+DXcXdiBjiAch0V49QR3oAJqCaL5qURE0vx9Eo+G5YE7mA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^7.0.0", + "wasm-feature-detect": "^1.8.0", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-7.0.0.tgz", + "integrity": "sha512-WnNH518NzmbSq9zgTPeoF8c+xmilS8rFIl1YKbk/ptuuc7p6cLNELNuPAzcmsYw450ca6bLa8j3t0VAtq435Vw==", + "license": "Apache-2.0" + }, + "node_modules/tesseract.js/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6453,6 +7432,15 @@ "node": ">=0.8" } }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6612,10 +7600,11 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6753,6 +7742,26 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -6999,6 +8008,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zeptomatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", + "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", + "license": "MIT", + "dependencies": { + "grammex": "^3.1.11", + "graphmatch": "^1.1.0" + } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/zod": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", diff --git a/package-lock.json b/package-lock.json index cfb2e56..d7261b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,18 +15,23 @@ "@babel/runtime": "^7.23.9", "@emailjs/browser": "^4.4.1", "@hookform/resolvers": "^5.2.2", + "@prisma/client": "^7.5.0", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", + "@types/bcryptjs": "^2.4.6", "@vercel/analytics": "^1.1.4", "axios": "^1.7.6", + "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.23.24", "generate-unique-id": "^2.0.3", "lucide-react": "^0.545.0", + "nanoid": "^5.1.7", "next": "^14.1.1", "next-intl": "^3.15.3", + "prisma": "^7.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", @@ -38,6 +43,7 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", + "tesseract.js": "^7.0.0", "zod": "^4.1.12" }, "devDependencies": { @@ -1007,6 +1013,66 @@ "node": ">=6.9.0" } }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", + "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", + "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", + "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, "node_modules/@emailjs/browser": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz", @@ -1177,6 +1243,18 @@ "tslib": "^2.4.0" } }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@hookform/resolvers": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", @@ -1737,6 +1815,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", + "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", + "license": "MIT", + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@next/env": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", @@ -1927,6 +2018,163 @@ "node": ">=14" } }, + "node_modules/@prisma/client": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.5.0.tgz", + "integrity": "sha512-h4hF9ctp+kSRs7ENHGsFQmHAgHcfkOCxbYt6Ti9Xi8x7D+kP4tTi9x51UKmiTH/OqdyJAO+8V+r+JA5AWdav7w==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.5.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.5.0.tgz", + "integrity": "sha512-KnJ2b4Si/pcWEtK68uM+h0h1oh80CZt2suhLTVuLaSKg4n58Q9jBF/A42Kw6Ma+aThy1yAhfDeTC0JvEmeZnFQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/config": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.5.0.tgz", + "integrity": "sha512-1J/9YEX7A889xM46PYg9e8VAuSL1IUmXJW3tEhMv7XQHDWlfC9YSkIw9sTYRaq5GswGlxZ+GnnyiNsUZ9JJhSQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.5.0.tgz", + "integrity": "sha512-163+nffny0JoPEkDhfNco0vcuT3ymIJc9+WX7MHSQhfkeKUmKe9/wqvGk5SjppT93DtBjVwr5HPJYlXbzm6qtg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/dev": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz", + "integrity": "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==", + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.3.15", + "@electric-sql/pglite-socket": "0.0.20", + "@electric-sql/pglite-tools": "0.2.20", + "@hono/node-server": "1.19.9", + "@mrleebo/prisma-ast": "0.13.1", + "@prisma/get-platform": "7.2.0", + "@prisma/query-plan-executor": "7.2.0", + "foreground-child": "3.3.1", + "get-port-please": "3.2.0", + "hono": "4.11.4", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.33.4", + "std-env": "3.10.0", + "valibot": "1.2.0", + "zeptomatch": "2.1.0" + } + }, + "node_modules/@prisma/engines": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.5.0.tgz", + "integrity": "sha512-ondGRhzoaVpRWvFaQ5wH5zS1BIbhzbKqczKjCn6j3L0Zfe/LInjcEg8+xtB49AuZBX30qyx1ZtGoootUohz2pw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/fetch-engine": "7.5.0", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e.tgz", + "integrity": "sha512-E+iRV/vbJLl8iGjVr6g/TEWokA+gjkV/doZkaQN1i/ULVdDwGnPJDfLUIFGS3BVwlG/m6L8T4x1x5isl8hGMxA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.5.0.tgz", + "integrity": "sha512-kZCl2FV54qnyrVdnII8MI6qvt7HfU6Cbiz8dZ8PXz4f4lbSw45jEB9/gEMK2SGdiNhBKyk/Wv95uthoLhGMLYA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", + "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.2.0" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", + "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", + "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/studio-core": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.21.1.tgz", + "integrity": "sha512-bOGqG/eMQtKC0XVvcVLRmhWWzm/I+0QUWqAEhEBtetpuS3k3V4IWqKGUONkAIT223DNXJMxMtZp36b1FmcdPeg==", + "license": "Apache-2.0", + "engines": { + "node": "^20.19 || ^22.12 || ^24.0", + "pnpm": "8" + }, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -2452,6 +2700,12 @@ "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", "dev": true }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -2466,6 +2720,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "license": "MIT" + }, "node_modules/@types/dom-speech-recognition": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.4.tgz", @@ -2500,14 +2760,12 @@ "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "devOptional": true + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/react": { "version": "18.2.55", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2548,8 +2806,7 @@ "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "devOptional": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", @@ -3043,6 +3300,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axe-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", @@ -3124,6 +3390,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3132,6 +3407,12 @@ "node": ">=8" } }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3196,6 +3477,71 @@ "node": ">=10.16.0" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/call-bind": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", @@ -3266,6 +3612,20 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3300,6 +3660,15 @@ "node": ">= 6" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -3389,6 +3758,21 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3410,9 +3794,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3436,8 +3821,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -3467,6 +3851,15 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", @@ -3499,6 +3892,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3508,6 +3907,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3517,6 +3925,12 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", @@ -3565,11 +3979,33 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.659", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.659.tgz", @@ -3581,6 +4017,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -4174,6 +4619,34 @@ "node": ">=0.8.x" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4348,11 +4821,12 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -4470,6 +4944,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generate-unique-id": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/generate-unique-id/-/generate-unique-id-2.0.3.tgz", @@ -4514,6 +4997,12 @@ "node": ">=6" } }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "license": "MIT" + }, "node_modules/get-symbol-description": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.1.tgz", @@ -4542,6 +5031,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -4640,12 +5146,24 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphmatch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", + "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4726,6 +5244,15 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -4751,6 +5278,34 @@ "node": ">= 14" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -5047,6 +5602,12 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -5129,6 +5690,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -5355,6 +5922,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -5367,6 +5940,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5389,6 +5968,21 @@ "node": ">=10" } }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/lucide-react": { "version": "0.545.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz", @@ -5488,6 +6082,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5498,21 +6112,34 @@ "thenify-all": "^1.0.0" } }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -5596,6 +6223,24 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/next/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5643,6 +6288,12 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -5666,6 +6317,29 @@ "node": ">=0.10.0" } }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5792,6 +6466,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5801,6 +6481,15 @@ "wrappy": "1" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5923,6 +6612,18 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5955,6 +6656,17 @@ "node": ">= 6" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/postcss": { "version": "8.4.34", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.34.tgz", @@ -6093,6 +6805,37 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6118,6 +6861,39 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prisma": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.5.0.tgz", + "integrity": "sha512-n30qZpWehaYQzigLjmuPisyEsvOzHt7bZeRyg8gZ5DvJo9FGjD+gNaY59Ns3hlLD5/jZH5GBeftIss0jDbUoLg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "7.5.0", + "@prisma/dev": "0.20.0", + "@prisma/engines": "7.5.0", + "@prisma/studio-core": "0.21.1", + "mysql2": "3.15.3", + "postgres": "3.4.7" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -6128,6 +6904,23 @@ "react-is": "^16.13.1" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -6143,6 +6936,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6162,6 +6971,16 @@ } ] }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -6354,6 +7173,12 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -6371,6 +7196,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remeda": { + "version": "2.33.4", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", + "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/remeda" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -6405,6 +7239,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -6492,6 +7335,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -6520,6 +7369,11 @@ "node": ">=10" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/server-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", @@ -6678,6 +7532,21 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -7034,6 +7903,36 @@ "node": ">=6" } }, + "node_modules/tesseract.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-7.0.0.tgz", + "integrity": "sha512-exPBkd+z+wM1BuMkx/Bjv43OeLBxhL5kKWsz/9JY+DXcXdiBjiAch0V49QR3oAJqCaL5qURE0vx9Eo+G5YE7mA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^7.0.0", + "wasm-feature-detect": "^1.8.0", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-7.0.0.tgz", + "integrity": "sha512-WnNH518NzmbSq9zgTPeoF8c+xmilS8rFIl1YKbk/ptuuc7p6cLNELNuPAzcmsYw450ca6bLa8j3t0VAtq435Vw==", + "license": "Apache-2.0" + }, + "node_modules/tesseract.js/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7059,6 +7958,15 @@ "node": ">=0.8" } }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -7218,10 +8126,11 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7359,6 +8268,26 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -7605,6 +8534,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zeptomatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", + "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", + "license": "MIT", + "dependencies": { + "grammex": "^3.1.11", + "graphmatch": "^1.1.0" + } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/zod": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", diff --git a/package.json b/package.json index e2beb40..0a5da2f 100644 --- a/package.json +++ b/package.json @@ -17,18 +17,23 @@ "@babel/runtime": "^7.23.9", "@emailjs/browser": "^4.4.1", "@hookform/resolvers": "^5.2.2", + "@prisma/client": "^7.5.0", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", + "@types/bcryptjs": "^2.4.6", "@vercel/analytics": "^1.1.4", "axios": "^1.7.6", + "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.23.24", "generate-unique-id": "^2.0.3", "lucide-react": "^0.545.0", + "nanoid": "^5.1.7", "next": "^14.1.1", "next-intl": "^3.15.3", + "prisma": "^7.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", @@ -40,6 +45,7 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", + "tesseract.js": "^7.0.0", "zod": "^4.1.12" }, "devDependencies": { diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..9bd4196 --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,14 @@ +// This file was generated by Prisma, and assumes you have installed the following: +// npm install --save-dev prisma dotenv +import "dotenv/config"; +import { defineConfig } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + datasource: { + url: process.env.DATABASE_URL, + }, +}); diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..415a5ee --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,73 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mongodb" +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + password String + fullName String? + preferredLanguage String @default("en") + createdAt DateTime @default(now()) + translations Translation[] + flashcards Flashcard[] + corrections Correction[] + chatMessages ChatMessage[] + sessions Session[] +} + +model Session { + id String @id @default(auto()) @map("_id") @db.ObjectId + sessionToken String @unique + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + expires DateTime + createdAt DateTime @default(now()) +} + +model Translation { + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id]) + inputText String + outputText String + sourceLanguage String + targetLanguage String + type String @default("text") // "text" or "document" + createdAt DateTime @default(now()) +} + +model Flashcard { + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id]) + front String + back String + createdAt DateTime @default(now()) +} + +model Correction { + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id]) + originalTranslation String + suggestedTranslation String + createdAt DateTime @default(now()) +} + +model ChatMessage { + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id]) + message String + translatedText String + isUser Boolean @default(true) + createdAt DateTime @default(now()) +} diff --git a/src/components/Chat/LiveChat.tsx b/src/components/Chat/LiveChat.tsx new file mode 100644 index 0000000..db5d369 --- /dev/null +++ b/src/components/Chat/LiveChat.tsx @@ -0,0 +1,128 @@ +"use client"; + +import React, { useState, useEffect, useRef } from "react"; +import { Button } from ".."; +import { useNotification } from "../../contexts"; +import { FaPaperPlane, FaRobot, FaUser } from "react-icons/fa"; + +interface Message { + id: string; + message: string; + translatedText: string; + isUser: boolean; +} + +export const LiveChat: React.FC = () => { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(""); + const [loading, setLoading] = useState(false); + const { notify } = useNotification(); + const scrollRef = useRef(null); + + useEffect(() => { + const fetchMessages = async () => { + try { + const response = await fetch("/api/chat"); + const data = await response.json(); + if (response.ok) setMessages(data); + } catch (error) { + console.error("Failed to fetch chat history", error); + } + }; + fetchMessages(); + }, []); + + useEffect(() => { + scrollRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + const handleSend = async (e: React.FormEvent) => { + e.preventDefault(); + if (!input.trim()) return; + + setLoading(true); + const userMsg = input; + setInput(""); + + try { + // 1. Translate user message (Mocking real-time translation) + const transRes = await fetch("/api/translate-text", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: userMsg, from: "en", to: "fr" }), + }); + const translatedText = await transRes.json(); + + // 2. Save user message + const saveRes = await fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ message: userMsg, translatedText, isUser: true }), + }); + const savedMsg = await saveRes.json(); + setMessages((prev) => [...prev, savedMsg]); + + // 3. Simulate bot response + setTimeout(async () => { + const botMsg = "I received your message: " + translatedText; + const botTransRes = await fetch("/api/translate-text", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: botMsg, from: "en", to: "fr" }), + }); + const botTranslated = await botTransRes.json(); + + const botSaveRes = await fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ message: botMsg, translatedText: botTranslated, isUser: false }), + }); + const savedBotMsg = await botSaveRes.json(); + setMessages((prev) => [...prev, savedBotMsg]); + }, 1000); + + } catch (error) { + notify("Failed to send message", "error"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ Live Chat Translation +
+ +
+ {messages.map((msg) => ( +
+
+
+ {msg.isUser ? : } {msg.isUser ? "You" : "Translator Bot"} +
+

{msg.message}

+
+ {msg.translatedText} +
+
+
+ ))} +
+
+ +
+ setInput(e.target.value)} + placeholder="Type a message to translate..." + className="flex-1 bg-gray-50 border-none rounded-xl px-4 py-2 focus:ring-2 focus:ring-primary outline-none" + /> + +
+
+ ); +}; diff --git a/src/components/HowItWorks.tsx b/src/components/HowItWorks.tsx index fe7d920..fdddad4 100644 --- a/src/components/HowItWorks.tsx +++ b/src/components/HowItWorks.tsx @@ -1,6 +1,6 @@ import { useLocale, useTranslations } from "next-intl"; -import { roboto } from "@/app/[locale]/fonts"; -import { stepsData } from "@/app/[locale]/data"; +import { roboto } from "@/app/fonts"; +import { stepsData } from "@/app/data"; import { StepProps } from "@/types"; import { Locale } from "@/i18n.config"; import Link from "next/link"; diff --git a/src/components/Learning/DailyLearning.tsx b/src/components/Learning/DailyLearning.tsx new file mode 100644 index 0000000..b76651a --- /dev/null +++ b/src/components/Learning/DailyLearning.tsx @@ -0,0 +1,105 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { Button } from ".."; +import { useNotification } from "../../contexts"; +import { motion } from "framer-motion"; + +interface QuizQuestion { + question: string; + answer: string; + options: string[]; +} + +export const DailyLearning: React.FC = () => { + const [quiz, setQuiz] = useState([]); + const [currentIndex, setCurrentIndex] = useState(0); + const [score, setScore] = useState(0); + const [showResult, setShowResult] = useState(false); + const { notify } = useNotification(); + + useEffect(() => { + const fetchQuiz = async () => { + try { + const response = await fetch("/api/learning/daily"); + const data = await response.json(); + if (response.ok) { + setQuiz(data.quiz); + } + } catch (error) { + console.error("Failed to fetch daily quiz", error); + } + }; + fetchQuiz(); + }, []); + + const handleAnswer = (option: string) => { + if (option === quiz[currentIndex].answer) { + setScore(score + 1); + notify("Correct!", "success"); + } else { + notify(`Wrong! The correct answer was: ${quiz[currentIndex].answer}`, "error"); + } + + if (currentIndex + 1 < quiz.length) { + setCurrentIndex(currentIndex + 1); + } else { + setShowResult(true); + } + }; + + if (quiz.length === 0) { + return ( +
+

Daily Learning Mode

+

Translate some texts first to generate your personalized daily quiz!

+
+ ); + } + + if (showResult) { + return ( + +

Quiz Completed!

+

Your Score: {score} / {quiz.length}

+ +
+ ); + } + + return ( +
+
+

Daily Quiz

+ + Question {currentIndex + 1} of {quiz.length} + +
+ +
+

Translate this:

+

"{quiz[currentIndex].question}"

+
+ +
+ {quiz[currentIndex].options.map((option, index) => ( + handleAnswer(option)} + className="p-5 text-left border-2 border-gray-100 rounded-xl hover:border-primary hover:bg-primary/5 transition-all duration-200 font-medium text-gray-700 shadow-sm" + > + {option} + + ))} +
+
+ ); +}; diff --git a/src/components/Learning/Flashcards.tsx b/src/components/Learning/Flashcards.tsx new file mode 100644 index 0000000..99ca046 --- /dev/null +++ b/src/components/Learning/Flashcards.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { Button } from ".."; +import { FaChevronLeft, FaChevronRight, FaPlus } from "react-icons/fa"; + +interface Flashcard { + id: string; + front: string; + back: string; +} + +export const Flashcards: React.FC = () => { + const [cards, setCards] = useState([]); + const [currentIndex, setCurrentIndex] = useState(0); + const [isFlipped, setIsFlipped] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchCards = async () => { + try { + const response = await fetch("/api/learning/flashcards"); + const data = await response.json(); + if (response.ok && data.length > 0) { + setCards(data); + } else { + // Fallback default cards + setCards([ + { id: "1", front: "Hello", back: "Bonjour" }, + { id: "2", front: "Thank you", back: "Merci" }, + { id: "3", front: "Goodbye", back: "Au revoir" }, + ]); + } + } catch (error) { + console.error("Failed to fetch flashcards", error); + } finally { + setLoading(false); + } + }; + fetchCards(); + }, []); + + const handleNext = () => { + setIsFlipped(false); + setCurrentIndex((prev) => (prev + 1) % cards.length); + }; + + const handlePrev = () => { + setIsFlipped(false); + setCurrentIndex((prev) => (prev - 1 + cards.length) % cards.length); + }; + + if (loading) return
Loading cards...
; + + return ( +
+

Language Flashcards

+ +
+ + {cards.length > 0 ? ( + setIsFlipped(!isFlipped)} + className="w-full h-full bg-white rounded-3xl shadow-xl flex items-center justify-center cursor-pointer p-6 border-2 border-primary/10" + > +

+ {isFlipped ? cards[currentIndex]?.back : cards[currentIndex]?.front} +

+
+ ) : ( +
+

No cards available

+
+ )} +
+
+ +
+ + + {currentIndex + 1} / {cards.length} + + +
+ +

Tip: Click the card to flip!

+
+ ); +}; diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index 8dfe75b..b4ed865 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -1,5 +1,5 @@ "use client"; -import { navLanguagesData } from "@/app/[locale]/data"; +import { navLanguagesData } from "@/app/data"; import { usePathname, useRouter, type Locale } from "@/i18n.config"; import Image from "next/image"; import { selectedNavLanguageOption } from "./shared/helper"; diff --git a/src/components/Sidebar/ClientSidebar.tsx b/src/components/Sidebar/ClientSidebar.tsx index 62a936e..50fd978 100644 --- a/src/components/Sidebar/ClientSidebar.tsx +++ b/src/components/Sidebar/ClientSidebar.tsx @@ -5,7 +5,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { useTranslations } from "next-intl"; import { LuLayoutDashboard } from "react-icons/lu"; -import { MdTranslate, MdFileCopy } from "react-icons/md"; +import { MdTranslate, MdFileCopy, MdChat, MdImage, MdSchool } from "react-icons/md"; import { IoSettingsOutline } from "react-icons/io5"; import { FaSignOutAlt } from "react-icons/fa"; @@ -28,11 +28,16 @@ export default function ClientSidebar() { label: t("TranslatedDocuments"), icon: MdFileCopy, }, + { href: "/image-translation", label: "Image Translation", icon: MdImage }, + { href: "/learning", label: "Learning", icon: MdSchool }, + { href: "/chat", label: "Live Chat", icon: MdChat }, { href: "/settings", label: t("Settings"), icon: IoSettingsOutline }, ]; const handleLogout = () => { - console.log("User logged out"); + document.cookie = "session_token=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;"; + localStorage.clear(); + window.location.href = "/login"; }; return ( diff --git a/src/components/SupportedFormat/index.tsx b/src/components/SupportedFormat/index.tsx index 2b38887..ac06589 100644 --- a/src/components/SupportedFormat/index.tsx +++ b/src/components/SupportedFormat/index.tsx @@ -1,4 +1,4 @@ -import { supportedFormatsGroupedByFileType } from "@/app/[locale]/utils/helper"; +import { supportedFormatsGroupedByFileType } from "@/app/utils/helper"; import { useLocale, useTranslations } from "next-intl"; import { Locale } from "@/i18n.config"; diff --git a/src/components/Translation/ClientTranslation.tsx b/src/components/Translation/ClientTranslation.tsx index 46bde54..8fa98c2 100644 --- a/src/components/Translation/ClientTranslation.tsx +++ b/src/components/Translation/ClientTranslation.tsx @@ -1,6 +1,6 @@ "use client"; import { FormEvent, useCallback, useEffect, useRef, useState } from "react"; -import { inter } from "@/app/[locale]/fonts"; +import { inter } from "@/app/fonts"; import { SelectLanguage } from "../shared/SelectLanguage"; import { Arrows, @@ -13,11 +13,13 @@ import { } from "../../assets/svg"; import { Button } from ".."; import { selectedLanguageOption } from "../shared/helper"; -import { translateText } from "@/app/[locale]/api"; +import { translateText } from "@/app/api"; import { useModal, useNotification } from "../../contexts"; import { TextArea } from "../shared/TextArea"; import { UploadFile } from "../UploadFile"; import { useVoiceToText } from "react-speakup"; +import { FaEdit, FaPlus } from "react-icons/fa"; +import { CorrectionModal } from "./CorrectionModal"; export const ClientTranslation: React.FC<{ headingText: string; @@ -61,6 +63,8 @@ export const ClientTranslation: React.FC<{ const [isVisible, setIsVisible] = useState(false); const [micOn, setMicOn] = useState(false); const [micMode, setMicMode] = useState<"play" | "pause" | "stop">("play"); + const [aiLoading, setAiLoading] = useState(false); + const [keywords, setKeywords] = useState([]); const sourceLang = `${selectedLanguageOption(sourceLanguage)?.key}`; const targetLang = `${selectedLanguageOption(targetLanguage)?.key}`; @@ -92,17 +96,14 @@ export const ClientTranslation: React.FC<{ }, [notify, stopListening]); const handleSpeak = useCallback( - (text: string) => { - if (sourceLang === "en" || targetLang === "en") { - if ("speechSynthesis" in window && text) { - const utterance = new SpeechSynthesisUtterance(text); - window.speechSynthesis.speak(utterance); - notify("You are now listening to your texts...", "inform"); - } else { - notify("Sorry, Text-to-Speech is not supported.", "warn"); - } + (text: string, lang: string) => { + if ("speechSynthesis" in window && text) { + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = lang === "zh" ? "zh-CN" : lang; + window.speechSynthesis.speak(utterance); + notify("You are now listening...", "inform"); } else { - notify("Sorry, this works better for English...", "inform"); + notify("Sorry, Text-to-Speech is not supported.", "warn"); } }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -179,6 +180,79 @@ export const ClientTranslation: React.FC<{ // eslint-disable-next-line react-hooks/exhaustive-deps }, [text]); + const handleSummarize = async () => { + setAiLoading(true); + try { + const response = await fetch("/api/summarize", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text }), + }); + const data = await response.json(); + setOutput(data); + notify("Text summarized successfully!", "success"); + } catch (error) { + notify("Summarization failed", "error"); + } finally { + setAiLoading(false); + } + }; + + const handleKeywords = async () => { + setAiLoading(true); + try { + const response = await fetch("/api/keywords", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text }), + }); + const data = await response.json(); + setKeywords(data); + notify("Keywords extracted successfully!", "success"); + } catch (error) { + notify("Keyword extraction failed", "error"); + } finally { + setAiLoading(false); + } + }; + + const handleRewrite = async (style: string) => { + setAiLoading(true); + try { + const response = await fetch("/api/rewrite", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text, style }), + }); + const data = await response.json(); + setOutput(data); + notify(`Text rewritten to be ${style}!`, "success"); + } catch (error) { + notify("Rewriting failed", "error"); + } finally { + setAiLoading(false); + } + }; + + const handleSuggestCorrection = () => { + openModal(); + }; + + const handleSaveFlashcard = async () => { + try { + const response = await fetch("/api/learning/flashcards", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ front: text, back: output }), + }); + if (response.ok) { + notify("Saved to flashcards!", "success"); + } + } catch (error) { + notify("Failed to save flashcard", "error"); + } + }; + return (
@@ -242,6 +316,37 @@ export const ClientTranslation: React.FC<{ }} />
+ {text && ( +
+ + + +
+ )} {micOn && (
+ +
)}
+ {keywords.length > 0 && ( +
+ {keywords.map((kw, i) => ( + + {kw} + + ))} +
+ )}
{translateButton && ( + +
+ + + ); +}; diff --git a/src/components/Translation/ImageTranslation.tsx b/src/components/Translation/ImageTranslation.tsx new file mode 100644 index 0000000..b57a5f6 --- /dev/null +++ b/src/components/Translation/ImageTranslation.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { useState, useRef, useCallback } from "react"; +import Tesseract from "tesseract.js"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useNotification } from "@/src/contexts"; +import { translateText } from "@/app/api"; +import { FaCamera, FaUpload, FaSpinner } from "react-icons/fa"; + +export const ImageTranslation = () => { + const [image, setImage] = useState(null); + const [ocrText, setOcrText] = useState(""); + const [translatedText, setTranslatedText] = useState(""); + const [loading, setLoading] = useState(false); + const { notify } = useNotification(); + const fileInputRef = useRef(null); + + const handleImageUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + setImage(event.target?.result as string); + performOCR(event.target?.result as string); + }; + reader.readAsDataURL(file); + } + }; + + const performOCR = async (imageSrc: string) => { + setLoading(true); + setOcrText(""); + setTranslatedText(""); + try { + const result = await Tesseract.recognize(imageSrc, "eng+fra+spa+deu+chi_sim"); + setOcrText(result.data.text); + notify("Text extracted from image!", "success"); + + // Auto-translate to English if not already + const translated = await translateText({ + text: result.data.text, + from: "auto", + to: "en" + }); + setTranslatedText(translated); + } catch (error) { + notify("OCR failed", "error"); + } finally { + setLoading(false); + } + }; + + return ( +
+

Image & Camera Translation

+ +
+ + + Upload or Capture + + +
+ {image ? ( + Selected + ) : ( + + )} +
+ + +
+
+ + + + Results + + +
+

Extracted Text

+
+ {loading ? : ocrText || "Waiting for image..."} +
+
+
+

Translation (to English)

+
+ {loading ? : translatedText || "Translation will appear here..."} +
+
+
+
+
+
+ ); +}; diff --git a/src/components/UploadFile/ClientUploadFile.tsx b/src/components/UploadFile/ClientUploadFile.tsx index c56a230..b7e9dd9 100644 --- a/src/components/UploadFile/ClientUploadFile.tsx +++ b/src/components/UploadFile/ClientUploadFile.tsx @@ -17,7 +17,7 @@ import { Arrows } from "../../assets/svg"; import { selectedLanguageOption } from "../shared/helper"; import axios from "axios"; import generateUniqueId from "generate-unique-id"; -import { generateAcceptObject } from "@/app/[locale]/utils/helper"; +import { generateAcceptObject } from "@/app/utils/helper"; export function formatFileSize(fileSizeBytes: number) { // Define size units and their respective suffixes @@ -155,11 +155,11 @@ export const ClientUploadFile: React.FC<{ }; const formData = new FormData(); - formData.append("document", uploadedFile[0], uploadedFile[0].name); + formData.append("file", uploadedFile[0], uploadedFile[0].name); + formData.append("from", sourceLang); + formData.append("to", targetLang); try { - const response = await axios.post(url, formData, { - headers, - params, + const response = await axios.post("/api/translate-document", formData, { responseType: "blob", }); diff --git a/src/components/shared/SelectLanguage.tsx b/src/components/shared/SelectLanguage.tsx index e53ddfe..186ddea 100644 --- a/src/components/shared/SelectLanguage.tsx +++ b/src/components/shared/SelectLanguage.tsx @@ -2,7 +2,7 @@ import Image from "next/image"; import { useState } from "react"; import { Button } from ".."; -import { documentLanguagesData, textLanguagesData } from "@/app/[locale]/data"; +import { documentLanguagesData, textLanguagesData } from "@/app/data"; import { selectedLanguageOption } from "./helper"; interface SelectLanguageProps { diff --git a/src/components/shared/helper.ts b/src/components/shared/helper.ts index f8eabee..e7a20bc 100644 --- a/src/components/shared/helper.ts +++ b/src/components/shared/helper.ts @@ -2,7 +2,7 @@ import { documentLanguagesData, navLanguagesData, textLanguagesData, -} from "@/app/[locale]/data"; +} from "@/app/data"; export const selectedLanguageOption = (language: string) => textLanguagesData.find((option) => option.language === language); diff --git a/tailwind.config.ts b/tailwind.config.ts index 0b8bab3..5cc2aea 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -72,6 +72,15 @@ const config: Config = { } } }, - plugins: [require("tailwindcss-animate")], + plugins: [ + require("tailwindcss-animate"), + function ({ addUtilities }: any) { + addUtilities({ + '.perspective-1000': { + perspective: '1000px', + }, + }); + }, + ], }; export default config;