mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
feat: add stream info editing functionality
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
webpack: (config) => {
|
||||
config.externals.push("@node-rs/argon2");
|
||||
return config;
|
||||
}
|
||||
webpack: (config) => {
|
||||
config.externals.push('@node-rs/argon2');
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "docker compose --file dev/docker-compose.yml up -d && next dev --turbo --experimental-https",
|
||||
"dev": "docker compose --file dev/docker-compose.yml up -d && next dev --experimental-https --turbo",
|
||||
"setup": "docker compose --file dev/docker-compose.yml up -d && prisma migrate deploy",
|
||||
"build": "prisma generate && next build",
|
||||
"start": "next start",
|
||||
@@ -32,7 +32,7 @@
|
||||
"livekit-server-sdk": "^2.9.7",
|
||||
"lucia": "^3.2.2",
|
||||
"lucide-react": "^0.473.0",
|
||||
"next": "^15.1.2",
|
||||
"next": "^15.1.6",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "19",
|
||||
"react-dom": "19",
|
||||
@@ -40,6 +40,7 @@
|
||||
"sonner": "^1.4.41",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"valtio": "^2.1.2",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
15
prisma/migrations/20250118220020_stream_infov1/migration.sql
Normal file
15
prisma/migrations/20250118220020_stream_infov1/migration.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "StreamInfo" (
|
||||
"id" TEXT NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"thumbnail" TEXT NOT NULL,
|
||||
"viewers" INTEGER NOT NULL,
|
||||
"category" TEXT NOT NULL,
|
||||
"startedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "StreamInfo_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "StreamInfo_username_key" ON "StreamInfo"("username");
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `isLive` to the `StreamInfo` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "StreamInfo" ADD COLUMN "isLive" BOOLEAN NOT NULL;
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `userId` to the `StreamInfo` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "StreamInfo" ADD COLUMN "userId" TEXT NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "StreamInfo" ADD CONSTRAINT "StreamInfo_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -19,6 +19,7 @@ model User {
|
||||
pfpUrl String
|
||||
username String @unique
|
||||
sessions Session[]
|
||||
streams StreamInfo[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
@@ -26,4 +27,18 @@ model Session {
|
||||
userId String
|
||||
expiresAt DateTime
|
||||
user User @relation(references: [id], fields: [userId], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model StreamInfo {
|
||||
id String @id @default(cuid())
|
||||
username String @unique
|
||||
title String
|
||||
thumbnail String
|
||||
viewers Int
|
||||
category String
|
||||
startedAt DateTime
|
||||
isLive Boolean
|
||||
|
||||
ownedBy User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
import LiveStream from "@/components/app/Livestream/Livestream";
|
||||
import prisma from "@/lib/db";
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ username: string }> }) {
|
||||
const { username } = await params;
|
||||
const streamInfo = await prisma.streamInfo.findUnique({
|
||||
where: { username },
|
||||
include: { ownedBy: true },
|
||||
});
|
||||
if (!streamInfo) {
|
||||
return <div>Stream not found</div>;
|
||||
}
|
||||
return (
|
||||
<LiveStream username={username} />
|
||||
<LiveStream username={username} streamInfo={streamInfo} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import { Toaster } from '@/components/ui/sonner';
|
||||
import { ThemeProvider } from '@/lib/providers/ThemeProvider';
|
||||
import { SidebarProvider } from '@/components/ui/sidebar';
|
||||
import Sidebar from '@/components/app/Sidebar/Sidebar';
|
||||
import { cn } from '@/lib/utils';
|
||||
import EditLivestream from '@/components/app/EditLivestream/EditLivestream';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
@@ -24,7 +26,7 @@ export default async function RootLayout({
|
||||
const sessionData = await validateRequest();
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className="flex flex-col h-screen">
|
||||
<body className={cn("flex flex-col h-screen", inter.className)}>
|
||||
<SessionProvider value={sessionData}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
@@ -33,7 +35,8 @@ export default async function RootLayout({
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SidebarProvider>
|
||||
<Navbar />
|
||||
{/* this promise is ugly but i'm lazy to fix the type errors */}
|
||||
<Navbar editLivestream={Promise.resolve(<EditLivestream />)}/>
|
||||
<div className="flex flex-1 pt-16"> {/* pt-16 for navbar height */}
|
||||
<Sidebar className='pt-16' />
|
||||
<main className="flex-1 overflow-auto">
|
||||
|
||||
63
src/components/app/EditLivestream/EditLivestream.tsx
Normal file
63
src/components/app/EditLivestream/EditLivestream.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog';
|
||||
import { validateRequest } from '@/lib/auth';
|
||||
import prisma from '@/lib/db';
|
||||
import { roomService } from '@/lib/services/livekit';
|
||||
import { UniversalForm } from '../UniversalForm/UniversalForm';
|
||||
import { editStreamInfo } from '@/lib/form/actions';
|
||||
|
||||
export default async function EditLivestream() {
|
||||
const { user } = await validateRequest();
|
||||
if ((await prisma.streamInfo.count({ where: { username: user!.username } })) === 0) {
|
||||
const isLive =
|
||||
(await roomService.listRooms()).filter((r) => r.name === user!.username)[0].numPublishers >= 1;
|
||||
await prisma.streamInfo.create({
|
||||
data: {
|
||||
username: user!.username,
|
||||
title: 'Untitled',
|
||||
category: 'Uncategorized',
|
||||
startedAt: new Date(),
|
||||
thumbnail: 'https://placehold.co/150',
|
||||
viewers: 0,
|
||||
isLive,
|
||||
ownedBy: { connect: { username: user!.username } },
|
||||
},
|
||||
});
|
||||
console.log('created');
|
||||
}
|
||||
const streamInfo = await prisma.streamInfo.findUnique({
|
||||
where: { username: user!.username! },
|
||||
});
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">Edit Livestream</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit livestream</DialogTitle>
|
||||
<DialogDescription>Regenerate a key or edit your stream metadata</DialogDescription>
|
||||
</DialogHeader>
|
||||
<UniversalForm
|
||||
fields={[
|
||||
{ name: 'username', label: 'Username', value: user?.username!, type: 'hidden' },
|
||||
{ name: 'title', label: 'Title', type: 'text', value: streamInfo?.title },
|
||||
{ name: 'category', label: 'Category', type: 'text', value: streamInfo?.category },
|
||||
]}
|
||||
schemaName="streamInfoEdit"
|
||||
action={editStreamInfo}
|
||||
submitButtonDivClassname="float-right"
|
||||
submitText="Save"
|
||||
key={streamInfo?.id}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -5,15 +5,16 @@ import { useEffect, useState } from 'react';
|
||||
import StreamPlayer from '../StreamPlayer/StreamPlayer';
|
||||
import UserInfoCard from '../UserInfoCard/UserInfoCard';
|
||||
import ChatPanel from '../ChatPanel/ChatPanel';
|
||||
import type { StreamInfo, User } from '@prisma/client';
|
||||
|
||||
export default function LiveStream({ username }: { username: string }) {
|
||||
export default function LiveStream(props: Props) {
|
||||
const [token, setToken] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`/api/livekit/viewerToken?room=${username}`)
|
||||
fetch(`/api/livekit/viewerToken?room=${props.username}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => setToken(data.token));
|
||||
}, [username]);
|
||||
}, [props.username]);
|
||||
|
||||
if (!token) return <div>Loading...</div>;
|
||||
|
||||
@@ -22,10 +23,15 @@ export default function LiveStream({ username }: { username: string }) {
|
||||
<div className="flex h-[calc(100vh-64px)] w-full">
|
||||
<div className="flex-1">
|
||||
<StreamPlayer />
|
||||
<UserInfoCard />
|
||||
<UserInfoCard streamInfo={props.streamInfo} />
|
||||
</div>
|
||||
<ChatPanel />
|
||||
</div>
|
||||
</LiveKitRoom>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
streamInfo: StreamInfo & { ownedBy: User };
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
'use client';
|
||||
'use client'
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -19,9 +19,7 @@ import { ThemeSwitcher } from '../ThemeSwitcher/ThemeSwitcher';
|
||||
import { Slack } from 'lucide-react';
|
||||
import { SidebarTrigger } from '@/components/ui/sidebar';
|
||||
|
||||
export const links = [
|
||||
{ href: '/srizan', name: 'test stream' },
|
||||
];
|
||||
export const links = [{ href: '/srizan', name: 'test stream' }];
|
||||
|
||||
function NavbarLinks() {
|
||||
return (
|
||||
@@ -35,12 +33,12 @@ function NavbarLinks() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function Navbar() {
|
||||
export default function Navbar(props: Props) {
|
||||
const { user } = useSession();
|
||||
return (
|
||||
<>
|
||||
<nav className="flex items-center h-16 px-4 border-b gap-3 w-full z-20 fixed top-0 left-0 shadow-md bg-mantle">
|
||||
<div className='flex items-center'>
|
||||
<div className="flex items-center">
|
||||
<SidebarTrigger />
|
||||
<Link href="/" className="flex items-center">
|
||||
<Button>hackclub.tv</Button>
|
||||
@@ -52,6 +50,7 @@ export default function Navbar() {
|
||||
<NavbarLinks />
|
||||
</div>
|
||||
<div className="flex-1" />
|
||||
{props.editLivestream}
|
||||
<ThemeSwitcher />
|
||||
{user ? (
|
||||
<DropdownMenu>
|
||||
@@ -88,3 +87,7 @@ export default function Navbar() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
editLivestream: Promise<JSX.Element>;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import useFullscreen from '@/lib/hooks/useFullscreen';
|
||||
import {
|
||||
useTracks,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Path, PathValue, useForm } from 'react-hook-form';
|
||||
import { Path, useForm } from 'react-hook-form';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -19,11 +19,10 @@ import React from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { accountSchema } from '@/lib/form/zod';
|
||||
import { streamInfoEditSchema } from '@/lib/form/zod';
|
||||
|
||||
export const schemaDb = [
|
||||
{ name: 'login', zod: accountSchema },
|
||||
{ name: 'register', zod: accountSchema },
|
||||
{ name: 'streamInfoEdit', zod: streamInfoEditSchema }
|
||||
] as const;
|
||||
|
||||
export function UniversalForm<T extends z.ZodType>({
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import type { StreamInfo, User } from '@prisma/client';
|
||||
|
||||
export default function UserInfoCard() {
|
||||
export default function UserInfoCard(props: Props) {
|
||||
return (
|
||||
<div className="bg-mantle rounded-lg p-4">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Avatar className="h-16 w-16">
|
||||
<AvatarImage src="https://ca.slack-edge.com/T0266FRGM-U079VBNLTPD-1df1edc198bf-192" alt="Bartosz" />
|
||||
<AvatarImage src={props.streamInfo.ownedBy.pfpUrl} alt={props.streamInfo.ownedBy.username} />
|
||||
<AvatarFallback>CM</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">test stream</h1>
|
||||
<p>probably bartosz</p>
|
||||
<h1 className="text-2xl font-bold">{props.streamInfo.title}</h1>
|
||||
<p>{props.streamInfo.username}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button>Follow</Button>
|
||||
@@ -35,3 +36,7 @@ export default function UserInfoCard() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
streamInfo: StreamInfo & { ownedBy: User };
|
||||
}
|
||||
122
src/components/ui/dialog.tsx
Normal file
122
src/components/ui/dialog.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Dialog = DialogPrimitive.Root
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger
|
||||
|
||||
const DialogPortal = DialogPrimitive.Portal
|
||||
|
||||
const DialogClose = DialogPrimitive.Close
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
))
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DialogHeader.displayName = "DialogHeader"
|
||||
|
||||
const DialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DialogFooter.displayName = "DialogFooter"
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogClose,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PrismaAdapter } from '@lucia-auth/adapter-prisma';
|
||||
import { Lucia, Session, User } from 'lucia';
|
||||
import { Lucia } from 'lucia';
|
||||
import prisma from '../db';
|
||||
import { cache } from 'react';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
30
src/lib/form/actions.ts
Normal file
30
src/lib/form/actions.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
'use server';
|
||||
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { validateRequest } from '../auth';
|
||||
import prisma from '../db';
|
||||
import zodVerify from '../zodVerify';
|
||||
import { streamInfoEditSchema } from './zod';
|
||||
|
||||
export async function editStreamInfo(prev: any, formData: FormData) {
|
||||
const { user } = await validateRequest();
|
||||
if (!user) {
|
||||
return { success: false, error: 'Unauthorized' };
|
||||
}
|
||||
const zod = await zodVerify(streamInfoEditSchema, formData);
|
||||
if (!zod.success) {
|
||||
return zod;
|
||||
}
|
||||
|
||||
await prisma.streamInfo.update({
|
||||
where: { username: user.username },
|
||||
data: zod.data,
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
return { success: false, error: 'Internal server error' };
|
||||
});
|
||||
|
||||
revalidatePath(`/${user.username}`);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
@@ -3,4 +3,10 @@ import { z } from 'zod'
|
||||
export const accountSchema = z.object({
|
||||
username: z.string().min(3, { message: 'Mininum 3 characters' }).max(31, { message: 'Maximum 31 characters' }).regex(/^[a-z0-9_-]+$/, { message: 'Only characters from a-z, 0-9, underscores and dashes' }),
|
||||
password: z.string().min(6, { message: 'Minimum 6 characters' }).max(255, { message: 'Maximum 255 characters' }),
|
||||
})
|
||||
})
|
||||
|
||||
export const streamInfoEditSchema = z.object({
|
||||
username: z.string().min(1),
|
||||
title: z.string().min(1),
|
||||
category: z.string().min(1),
|
||||
});
|
||||
110
yarn.lock
110
yarn.lock
@@ -555,10 +555,10 @@
|
||||
"@emnapi/runtime" "^1.3.1"
|
||||
"@tybys/wasm-util" "^0.9.0"
|
||||
|
||||
"@next/env@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.4.tgz#f727cc4f46527a5223ae894f9476466db9132e21"
|
||||
integrity sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==
|
||||
"@next/env@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.6.tgz#2fa863d8c568a56b1c8328a86e621b8bdd4f2a20"
|
||||
integrity sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==
|
||||
|
||||
"@next/eslint-plugin-next@15.1.3":
|
||||
version "15.1.3"
|
||||
@@ -567,45 +567,45 @@
|
||||
dependencies:
|
||||
fast-glob "3.3.1"
|
||||
|
||||
"@next/swc-darwin-arm64@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.4.tgz#49faf320d44d1a813e09501abfd952b0315385c9"
|
||||
integrity sha512-wBEMBs+np+R5ozN1F8Y8d/Dycns2COhRnkxRc+rvnbXke5uZBHkUGFgWxfTXn5rx7OLijuUhyfB+gC/ap58dDw==
|
||||
"@next/swc-darwin-arm64@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.6.tgz#92f99badab6cb41f4c5c11a3feffa574bd6a9276"
|
||||
integrity sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==
|
||||
|
||||
"@next/swc-darwin-x64@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.4.tgz#3c234986481e7db8957562b8dfeab2d44fe4c0a7"
|
||||
integrity sha512-7sgf5rM7Z81V9w48F02Zz6DgEJulavC0jadab4ZsJ+K2sxMNK0/BtF8J8J3CxnsJN3DGcIdC260wEKssKTukUw==
|
||||
"@next/swc-darwin-x64@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.6.tgz#f56f4f8d5f6cb5d3915912ac95590d387f897da5"
|
||||
integrity sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.4.tgz#d71c5a55106327b50ff194dd40e11c3ca7f744a7"
|
||||
integrity sha512-JaZlIMNaJenfd55kjaLWMfok+vWBlcRxqnRoZrhFQrhM1uAehP3R0+Aoe+bZOogqlZvAz53nY/k3ZyuKDtT2zQ==
|
||||
"@next/swc-linux-arm64-gnu@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.6.tgz#0aaffae519c93d1006419d7b98c34ebfd80ecacd"
|
||||
integrity sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==
|
||||
|
||||
"@next/swc-linux-arm64-musl@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.4.tgz#c6b4cef0f5b943d6308fdb429ecb43be79afce31"
|
||||
integrity sha512-7EBBjNoyTO2ipMDgCiORpwwOf5tIueFntKjcN3NK+GAQD7OzFJe84p7a2eQUeWdpzZvhVXuAtIen8QcH71ZCOQ==
|
||||
"@next/swc-linux-arm64-musl@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.6.tgz#e7398d3d31ca60033f708a718cd6c31edcee2e9a"
|
||||
integrity sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==
|
||||
|
||||
"@next/swc-linux-x64-gnu@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.4.tgz#2b15f959ac6653800c0df8247489d54982926786"
|
||||
integrity sha512-9TGEgOycqZFuADyFqwmK/9g6S0FYZ3tphR4ebcmCwhL8Y12FW8pIBKJvSwV+UBjMkokstGNH+9F8F031JZKpHw==
|
||||
"@next/swc-linux-x64-gnu@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.6.tgz#d76c72508f4d79d6016cab0c52640b93e590cffb"
|
||||
integrity sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==
|
||||
|
||||
"@next/swc-linux-x64-musl@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.4.tgz#2b9d3ca3c3f506e3515b13c00e19d3031535bb44"
|
||||
integrity sha512-0578bLRVDJOh+LdIoKvgNDz77+Bd85c5JrFgnlbI1SM3WmEQvsjxTA8ATu9Z9FCiIS/AliVAW2DV/BDwpXbtiQ==
|
||||
"@next/swc-linux-x64-musl@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.6.tgz#0b8ba80a53e65bf8970ed11ea923001e2512c7cb"
|
||||
integrity sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.4.tgz#74928a6e824b258a071d30872c00417ee79d4ee7"
|
||||
integrity sha512-JgFCiV4libQavwII+kncMCl30st0JVxpPOtzWcAI2jtum4HjYaclobKhj+JsRu5tFqMtA5CJIa0MvYyuu9xjjQ==
|
||||
"@next/swc-win32-arm64-msvc@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.6.tgz#81b5dbbfdada2c05deef688e799af4a24097b65f"
|
||||
integrity sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==
|
||||
|
||||
"@next/swc-win32-x64-msvc@15.1.4":
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.4.tgz#188bce4c231f5ab0e7eaca55170764f7e8229db2"
|
||||
integrity sha512-xxsJy9wzq7FR5SqPCUqdgSXiNXrMuidgckBa8nH9HtjjxsilgcN6VgXF6tZ3uEWuVEadotQJI8/9EQ6guTC4Yw==
|
||||
"@next/swc-win32-x64-msvc@15.1.6":
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.6.tgz#131993c45ffd124fb4b15258e2f3f9669c143e3c"
|
||||
integrity sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==
|
||||
|
||||
"@node-rs/argon2-android-arm-eabi@2.0.2":
|
||||
version "2.0.2"
|
||||
@@ -3254,12 +3254,12 @@ next-themes@^0.4.4:
|
||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.4.tgz#ce6f68a4af543821bbc4755b59c0d3ced55c2d13"
|
||||
integrity sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==
|
||||
|
||||
next@^15.1.2:
|
||||
version "15.1.4"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-15.1.4.tgz#cb2aee3fbb772e20b98ccc2f0806249c09f780a2"
|
||||
integrity sha512-mTaq9dwaSuwwOrcu3ebjDYObekkxRnXpuVL21zotM8qE2W0HBOdVIdg2Li9QjMEZrj73LN96LcWcz62V19FjAg==
|
||||
next@^15.1.6:
|
||||
version "15.1.6"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-15.1.6.tgz#ce22fd0a8f36da1fc4aba86e3ec7e98eb248c555"
|
||||
integrity sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==
|
||||
dependencies:
|
||||
"@next/env" "15.1.4"
|
||||
"@next/env" "15.1.6"
|
||||
"@swc/counter" "0.1.3"
|
||||
"@swc/helpers" "0.5.15"
|
||||
busboy "1.6.0"
|
||||
@@ -3267,14 +3267,14 @@ next@^15.1.2:
|
||||
postcss "8.4.31"
|
||||
styled-jsx "5.1.6"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "15.1.4"
|
||||
"@next/swc-darwin-x64" "15.1.4"
|
||||
"@next/swc-linux-arm64-gnu" "15.1.4"
|
||||
"@next/swc-linux-arm64-musl" "15.1.4"
|
||||
"@next/swc-linux-x64-gnu" "15.1.4"
|
||||
"@next/swc-linux-x64-musl" "15.1.4"
|
||||
"@next/swc-win32-arm64-msvc" "15.1.4"
|
||||
"@next/swc-win32-x64-msvc" "15.1.4"
|
||||
"@next/swc-darwin-arm64" "15.1.6"
|
||||
"@next/swc-darwin-x64" "15.1.6"
|
||||
"@next/swc-linux-arm64-gnu" "15.1.6"
|
||||
"@next/swc-linux-arm64-musl" "15.1.6"
|
||||
"@next/swc-linux-x64-gnu" "15.1.6"
|
||||
"@next/swc-linux-x64-musl" "15.1.6"
|
||||
"@next/swc-win32-arm64-msvc" "15.1.6"
|
||||
"@next/swc-win32-x64-msvc" "15.1.6"
|
||||
sharp "^0.33.5"
|
||||
|
||||
node-domexception@^1.0.0:
|
||||
@@ -3632,6 +3632,11 @@ prop-types@^15.8.1:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
proxy-compare@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-3.0.1.tgz#3262cff3a25a6dedeaa299f6cf2369d6f7588a94"
|
||||
integrity sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||
@@ -4507,6 +4512,13 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
valtio@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/valtio/-/valtio-2.1.2.tgz#1785eb6c72ff50f42d8909a1c1f451f68bc59112"
|
||||
integrity sha512-fhekN5Rq7dvHULHHBlJeXHrQDl0Jj9GXfNavCm3gkD06crGchaG1nf/J7gSlfZU2wPcRdVS5jBKWHtE2NNz97A==
|
||||
dependencies:
|
||||
proxy-compare "^3.0.0"
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
|
||||
Reference in New Issue
Block a user