diff --git a/app/actions.ts b/app/actions.ts index 3d40e88..42fa1cd 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -4,6 +4,7 @@ import { revalidatePath } from 'next/cache' import { kv } from '@vercel/kv' import { type Chat } from '@/lib/types' +import { currentUser } from '@clerk/nextjs' export async function getChats(userId?: string | null) { if (!userId) { @@ -12,7 +13,9 @@ export async function getChats(userId?: string | null) { try { const pipeline = kv.pipeline() - const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1) + const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1, { + rev: true + }) for (const chat of chats) { pipeline.hgetall(chat) @@ -40,25 +43,20 @@ export async function getChat(id: string, userId: string) { return chat } -export async function removeChat({ - id, - path, - userId -}: { - id: string - userId: string - path: string -}) { - // @todo next-auth@v5 doesn't work in server actions yet - // const session = await auth(); +export async function removeChat({ id, path }: { id: string; path: string }) { + const user = await currentUser() + + if (!user) { + throw new Error('Unauthorized') + } const uid = await kv.hget(`chat:${id}`, 'userId') - if (uid !== userId) { + if (uid !== user.id) { throw new Error('Unauthorized') } await kv.del(`chat:${id}`) - await kv.zrem(`user:chat:${userId}`, `chat:${id}`) + await kv.zrem(`user:chat:${user.id}`, `chat:${id}`) revalidatePath('/') - revalidatePath('/chat/[id]') + revalidatePath(path) } diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index 4463a8e..0000000 --- a/app/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { GET, POST } from '@/auth' -// export const runtime = 'edge' diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 3a91d6e..6feb8e5 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,11 +1,11 @@ -import { auth } from '@/auth' import { kv } from '@vercel/kv' import { OpenAIStream, StreamingTextResponse } from 'ai-connector' import { Configuration, OpenAIApi } from 'openai-edge' import { nanoid } from '@/lib/utils' +import { currentUser } from '@clerk/nextjs' -// export const runtime = 'edge' +export const runtime = 'edge' const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY @@ -17,13 +17,15 @@ if (!process.env.OPENAI_API_KEY) { throw new Error('Missing env var from OpenAI') } -export const POST = auth(async function POST(req: Request) { +export async function POST(req: Request) { + const user = await currentUser() + if (user == null) { + return new Response('Unauthorized', { status: 401 }) + } + const json = await req.json() - // @ts-ignore - const messages = json.messages.map((m: any) => ({ - content: m.content, - role: m.role - })) + const { messages } = json + const res = await openai.createChatCompletion({ model: 'gpt-3.5-turbo', messages, @@ -35,14 +37,10 @@ export const POST = auth(async function POST(req: Request) { stream: true }) - const stream = await OpenAIStream(res, { + const stream = OpenAIStream(res, { async onCompletion(completion) { - // @ts-ignore - if (req.auth?.user?.email == null) { - return - } const title = json.messages[0].content.substring(0, 100) - const userId = (req as any).auth?.user?.email + const userId = user.id const id = json.id ?? nanoid() const createdAt = Date.now() const payload = { @@ -67,4 +65,4 @@ export const POST = auth(async function POST(req: Request) { }) return new StreamingTextResponse(stream) -}) +} diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 728ef16..c856cb0 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -1,5 +1,5 @@ import { type Metadata } from 'next' -import { auth } from '@/auth' +import { auth } from '@clerk/nextjs' import { Chat } from '@/components/chat' import { getChat } from '@/app/actions' @@ -16,16 +16,16 @@ export interface ChatPageProps { export async function generateMetadata({ params }: ChatPageProps): Promise { - const session = await auth() - const chat = await getChat(params.id, session?.user?.email ?? '') + const { user } = await auth() + const chat = await getChat(params.id, user?.id ?? '') return { title: chat?.title.slice(0, 50) ?? 'Chat' } } export default async function ChatPage({ params }: ChatPageProps) { - const session = await auth() - const chat = await getChat(params.id, session?.user?.email ?? '') + const { user } = await auth() + const chat = await getChat(params.id, user?.id ?? '') return } diff --git a/app/layout.tsx b/app/layout.tsx index 4d366d0..0c80dfb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,11 +1,13 @@ import { Metadata } from 'next' +import { ClerkProvider } from '@clerk/nextjs' + import '@/app/globals.css' import { fontMono, fontSans } from '@/lib/fonts' import { cn } from '@/lib/utils' -import { Header } from '@/components/header' import { TailwindIndicator } from '@/components/tailwind-indicator' import { ThemeProvider } from '@/components/theme-provider' +import { Header } from '@/components/header' export const metadata: Metadata = { title: { @@ -30,24 +32,26 @@ interface RootLayoutProps { export default function RootLayout({ children }: RootLayoutProps) { return ( - - - - -
- {/* @ts-ignore */} -
-
{children}
-
- -
- - + + + + + +
+ {/* @ts-ignore */} +
+
{children}
+
+ +
+ + +
) } diff --git a/app/page.tsx b/app/page.tsx index ee1ff4c..30cfb6c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,8 +1,11 @@ +import { nanoid } from '@/lib/utils' import { Chat } from '@/components/chat' // export const runtime = 'edge' export const preferredRegion = 'home' -export default async function IndexPage() { - return +export default function IndexPage() { + const id = nanoid() + + return } diff --git a/app/sign-in/[[...sign-in]]/page.tsx b/app/sign-in/[[...sign-in]]/page.tsx new file mode 100644 index 0000000..e5fc141 --- /dev/null +++ b/app/sign-in/[[...sign-in]]/page.tsx @@ -0,0 +1,42 @@ +import { ClerkLoaded, ClerkLoading, SignIn } from '@clerk/nextjs' + +import { cn } from '@/lib/utils' +import { ExternalLink } from '@/components/external-link' +import { buttonVariants } from '@/components/ui/button' +import { IconSpinner } from '@/components/ui/icons' + +export default function SignInPage() { + return ( +
+
+ +
+ Loading... +
+
+ + +

+ Open source AI chatbot app built with{' '} + Next.js and{' '} + + Vercel KV + + . +

+
+
+
+ ) +} diff --git a/app/sign-up/[[...sign-up]]/page.tsx b/app/sign-up/[[...sign-up]]/page.tsx new file mode 100644 index 0000000..aaadbcb --- /dev/null +++ b/app/sign-up/[[...sign-up]]/page.tsx @@ -0,0 +1,43 @@ +import { ClerkLoaded, ClerkLoading, SignUp } from '@clerk/nextjs' + +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' +import { ExternalLink } from '@/components/external-link' +import { IconSpinner } from '@/components/ui/icons' + +export default function Page() { + return ( +
+
+ +
+ Loading... +
+
+ + +

+ Open source AI chatbot app built with{' '} + Next.js and{' '} + + Vercel KV + + . +

+
+
+
+ ) +} diff --git a/auth.ts b/auth.ts deleted file mode 100644 index aee24e3..0000000 --- a/auth.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextResponse } from 'next/server' -import NextAuth from '@auth/nextjs' -import GitHub from '@auth/nextjs/providers/github' - -export const { - handlers: { GET, POST }, - auth, - CSRF_experimental -} = NextAuth({ - // @ts-ignore - providers: [GitHub], - session: { strategy: 'jwt' }, - async authorized({ request, auth }: any) { - const url = request.nextUrl - - if (request.method === 'POST') { - const { authToken } = (await request.json()) ?? {} - // If the request has a valid auth token, it is authorized - const valid = true - if (valid) return true - return NextResponse.json('Invalid auth token', { status: 401 }) - } - - // Logged in users are authenticated, otherwise redirect to login page - return !!auth - } -}) diff --git a/components/button-scroll-to-bottom.tsx b/components/button-scroll-to-bottom.tsx index b94f34a..436a6c0 100644 --- a/components/button-scroll-to-bottom.tsx +++ b/components/button-scroll-to-bottom.tsx @@ -3,26 +3,12 @@ import * as React from 'react' import { cn } from '@/lib/utils' +import { useAtBottom } from '@/lib/hooks/use-at-bottom' import { Button, type ButtonProps } from '@/components/ui/button' import { IconArrowDown } from '@/components/ui/icons' export function ButtonScrollToBottom({ className, ...props }: ButtonProps) { - const [isAtBottom, setIsAtBottom] = React.useState(false) - - React.useEffect(() => { - const handleScroll = () => { - setIsAtBottom( - window.innerHeight + window.scrollY > document.body.offsetHeight - 100 - ) - } - - window.addEventListener('scroll', handleScroll, { passive: true }) - handleScroll() - - return () => { - window.removeEventListener('scroll', handleScroll) - } - }, []) + const isAtBottom = useAtBottom() return ( + - + Copy code + - ) { xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" - className={cn('h-4 w-4', className)} + className={cn('h-4 w-4 animate-spin', className)} {...props} > @@ -394,7 +394,28 @@ function IconClose({ className, ...props }: React.ComponentProps<'svg'>) { ) } +function IconEdit({ className, ...props }: React.ComponentProps<'svg'>) { + return ( + + + + ) +} + export { + IconEdit, IconNextChat, IconOpenAI, IconVercel, diff --git a/components/user-menu.tsx b/components/user-menu.tsx deleted file mode 100644 index c5b96e2..0000000 --- a/components/user-menu.tsx +++ /dev/null @@ -1,24 +0,0 @@ -'use client' - -import * as React from 'react' -import { type Session } from '@auth/nextjs/types' - -import { LoginButton } from '@/components/login-button' - -export interface UserMenuProps { - session?: Session -} - -export function UserMenu({ session }: UserMenuProps) { - if (!session?.user) { - return ( - - ) - } - - return ( -

- {session.user.name} -

- ) -} diff --git a/lib/hooks/use-at-bottom.tsx b/lib/hooks/use-at-bottom.tsx new file mode 100644 index 0000000..d37c8cf --- /dev/null +++ b/lib/hooks/use-at-bottom.tsx @@ -0,0 +1,23 @@ +import * as React from 'react' + +export function useAtBottom(offset = 0) { + const [isAtBottom, setIsAtBottom] = React.useState(false) + + React.useEffect(() => { + const handleScroll = () => { + setIsAtBottom( + window.innerHeight + window.scrollY >= + document.body.offsetHeight - offset + ) + } + + window.addEventListener('scroll', handleScroll, { passive: true }) + handleScroll() + + return () => { + window.removeEventListener('scroll', handleScroll) + } + }, [offset]) + + return isAtBottom +} diff --git a/middleware.ts b/middleware.ts index 4afe145..813dafe 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1 +1,8 @@ -export { auth as middleware } from './auth' +import { authMiddleware } from '@clerk/nextjs' + +// @see https://clerk.dev +export default authMiddleware() + +export const config = { + matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'] +} diff --git a/package.json b/package.json index 96a74ed..6da22d3 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,7 @@ "format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache" }, "dependencies": { - "@auth/core": "0.0.0-manual.527fff6c", - "@auth/nextjs": "0.0.0-manual.030e8328", + "@clerk/nextjs": "^4.21.1", "@radix-ui/react-alert-dialog": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-separator": "^1.0.3", @@ -23,7 +22,7 @@ "@radix-ui/react-tooltip": "^1.0.6", "@vercel/analytics": "^1.0.0", "@vercel/kv": "^0.2.1", - "ai-connector": "^0.0.5", + "ai-connector": "^0.0.12", "body-scroll-lock": "4.0.0-beta.0", "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", @@ -35,6 +34,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", + "react-intersection-observer": "^9.4.4", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", "react-textarea-autosize": "^8.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a765a30..0fd8386 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,9 @@ overrides: '@auth/core': 0.0.0-manual.527fff6c dependencies: - '@auth/core': - specifier: 0.0.0-manual.527fff6c - version: 0.0.0-manual.527fff6c - '@auth/nextjs': - specifier: 0.0.0-manual.030e8328 - version: 0.0.0-manual.030e8328(next@13.4.5-canary.12)(react@18.2.0) + '@clerk/nextjs': + specifier: ^4.21.1 + version: 4.21.1(next@13.4.5-canary.12)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-alert-dialog': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) @@ -36,8 +33,8 @@ dependencies: specifier: ^0.2.1 version: 0.2.1 ai-connector: - specifier: ^0.0.5 - version: 0.0.5(react@18.2.0) + specifier: ^0.0.12 + version: 0.0.12(react@18.2.0)(svelte@3.59.1) body-scroll-lock: specifier: 4.0.0-beta.0 version: 4.0.0-beta.0 @@ -71,6 +68,9 @@ dependencies: react-hot-toast: specifier: ^2.4.1 version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) + react-intersection-observer: + specifier: ^9.4.4 + version: 9.4.4(react@18.2.0) react-markdown: specifier: ^8.0.7 version: 8.0.7(@types/react@18.2.6)(react@18.2.0) @@ -153,41 +153,90 @@ packages: engines: {node: '>=10'} dev: true - /@auth/core@0.0.0-manual.527fff6c: - resolution: {integrity: sha512-NYWuH+6VAAMV05juyarbMm6rCDUGvS5vPFsE+V13NIfbvbwbEZPj3JfcHVv7vMDrUrPtJ4EhTh00Svejp1Moqg==} - peerDependencies: - nodemailer: ^6.8.0 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@panva/hkdf': 1.1.1 - cookie: 0.5.0 - jose: 4.14.4 - oauth4webapi: 2.3.0 - preact: 10.11.3 - preact-render-to-string: 5.2.3(preact@10.11.3) - dev: false - - /@auth/nextjs@0.0.0-manual.030e8328(next@13.4.5-canary.12)(react@18.2.0): - resolution: {integrity: sha512-AAtUl9EVCKqLRneDAq6KfUprWmsBRzsBoQ+8ytz/Nfgs8Uax5CWvCJs2CsnYNk04bXSB4L7eyMJmc1zTzZKaHg==} - peerDependencies: - next: ^13.4.2 - react: ^18.2.0 - dependencies: - '@auth/core': 0.0.0-manual.527fff6c - next: 13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - transitivePeerDependencies: - - nodemailer - dev: false - /@babel/runtime@7.21.5: resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 + /@clerk/backend@0.23.0: + resolution: {integrity: sha512-2s8tRAL9nB+aQQUofrFCDGlz2Kv7pP+lR5CvQdMKTL9nPTlUBcvYFinjO3s6q3A7dwSk81erLKwYEXZEKA8R1w==} + engines: {node: '>=14'} + dependencies: + '@clerk/types': 3.42.0 + '@peculiar/webcrypto': 1.4.1 + '@types/node': 16.18.6 + deepmerge: 4.2.2 + node-fetch-native: 1.0.1 + snakecase-keys: 5.4.4 + tslib: 2.4.1 + dev: false + + /@clerk/clerk-react@4.20.0(react@18.2.0): + resolution: {integrity: sha512-3KiDycTHv+KzqS/If1un9dzJ5nrV32VPPVmNntjjZIWJDO7aJGy5v0+LiatSKMQbXGMVxuOmtZKHe3X7xkg+6g==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16' + dependencies: + '@clerk/shared': 0.19.0(react@18.2.0) + '@clerk/types': 3.42.0 + react: 18.2.0 + swr: 1.3.0(react@18.2.0) + tslib: 2.4.1 + dev: false + + /@clerk/clerk-sdk-node@4.10.6: + resolution: {integrity: sha512-62nk65yRBXvuOv28eTAq3Gs/9b/+S3gQAVmyDO/67QqGETw+d5TAEUvFrOtLg/EyP7tpGQKthP8xeH2bii7OIg==} + engines: {node: '>=14'} + dependencies: + '@clerk/backend': 0.23.0 + '@clerk/types': 3.42.0 + '@types/cookies': 0.7.7 + '@types/express': 4.17.14 + '@types/node-fetch': 2.6.2 + camelcase-keys: 6.2.2 + cookie: 0.5.0 + snakecase-keys: 3.2.1 + tslib: 2.4.1 + dev: false + + /@clerk/nextjs@4.21.1(next@13.4.5-canary.12)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Kkx8rp48jm0kWrFhMtFUTolitA4PVd2OEjxanhfmzlIhYcnH2ji/8UnH8U/dG3BvFcSeRwM1QO9wM4RsdXIKQg==} + engines: {node: '>=14'} + peerDependencies: + next: '>=10' + react: ^17.0.2 || ^18.0.0-0 + react-dom: ^17.0.2 || ^18.0.0-0 + dependencies: + '@clerk/backend': 0.23.0 + '@clerk/clerk-react': 4.20.0(react@18.2.0) + '@clerk/clerk-sdk-node': 4.10.6 + '@clerk/types': 3.42.0 + next: 13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0) + path-to-regexp: 6.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.4.1 + dev: false + + /@clerk/shared@0.19.0(react@18.2.0): + resolution: {integrity: sha512-yTm3XZBVvRhnUY4ax6SiNc/vZ4/lfqgUl+DLBxRKaJtsWcijKEddOCmFUcSgCS0wu1QcyvwNZ4PUYkVq9UWcxg==} + peerDependencies: + react: '>=16' + dependencies: + glob-to-regexp: 0.4.1 + js-cookie: 3.0.1 + react: 18.2.0 + swr: 1.3.0(react@18.2.0) + dev: false + + /@clerk/types@3.42.0: + resolution: {integrity: sha512-wEiBJFT3/Itzmu3jQfHK4zCMTqiE4fUheB1fpYx8JE2q498wzjTEHpglgxl88c0tudtWomUKqXIMJnBBEVVZBg==} + engines: {node: '>=14'} + dependencies: + csstype: 3.1.1 + dev: false + /@esbuild/android-arm@0.15.18: resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} @@ -430,8 +479,30 @@ packages: fastq: 1.15.0 dev: true - /@panva/hkdf@1.1.1: - resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + /@peculiar/asn1-schema@2.3.6: + resolution: {integrity: sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==} + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.2 + tslib: 2.5.0 + dev: false + + /@peculiar/json-schema@1.1.12: + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + dependencies: + tslib: 2.5.0 + dev: false + + /@peculiar/webcrypto@1.4.1: + resolution: {integrity: sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw==} + engines: {node: '>=10.12.0'} + dependencies: + '@peculiar/asn1-schema': 2.3.6 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.2 + tslib: 2.5.0 + webcrypto-core: 1.7.7 dev: false /@pkgr/utils@2.4.0: @@ -937,12 +1008,52 @@ packages: tailwindcss: 3.3.2 dev: true + /@types/body-parser@1.19.2: + resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + dependencies: + '@types/connect': 3.4.35 + '@types/node': 17.0.45 + dev: false + + /@types/connect@3.4.35: + resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + dependencies: + '@types/node': 17.0.45 + dev: false + + /@types/cookies@0.7.7: + resolution: {integrity: sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==} + dependencies: + '@types/connect': 3.4.35 + '@types/express': 4.17.14 + '@types/keygrip': 1.0.2 + '@types/node': 17.0.45 + dev: false + /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: '@types/ms': 0.7.31 dev: false + /@types/express-serve-static-core@4.17.35: + resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} + dependencies: + '@types/node': 17.0.45 + '@types/qs': 6.9.7 + '@types/range-parser': 1.2.4 + '@types/send': 0.17.1 + dev: false + + /@types/express@4.17.14: + resolution: {integrity: sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==} + dependencies: + '@types/body-parser': 1.19.2 + '@types/express-serve-static-core': 4.17.35 + '@types/qs': 6.9.7 + '@types/serve-static': 1.15.1 + dev: false + /@types/hast@2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} dependencies: @@ -957,23 +1068,53 @@ packages: resolution: {integrity: sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==} dev: false + /@types/keygrip@1.0.2: + resolution: {integrity: sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==} + dev: false + /@types/mdast@3.0.11: resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==} dependencies: '@types/unist': 2.0.6 dev: false + /@types/mime@1.3.2: + resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} + dev: false + + /@types/mime@3.0.1: + resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} + dev: false + /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} dev: false + /@types/node-fetch@2.6.2: + resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} + dependencies: + '@types/node': 17.0.45 + form-data: 3.0.1 + dev: false + + /@types/node@16.18.6: + resolution: {integrity: sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==} + dev: false + /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - dev: true /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + /@types/qs@6.9.7: + resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + dev: false + + /@types/range-parser@1.2.4: + resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + dev: false + /@types/react-dom@18.2.4: resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==} dependencies: @@ -995,6 +1136,20 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + /@types/send@0.17.1: + resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} + dependencies: + '@types/mime': 1.3.2 + '@types/node': 17.0.45 + dev: false + + /@types/serve-static@1.15.1: + resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} + dependencies: + '@types/mime': 3.0.1 + '@types/node': 17.0.45 + dev: false + /@types/unist@2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: false @@ -1096,15 +1251,23 @@ packages: hasBin: true dev: true - /ai-connector@0.0.5(react@18.2.0): - resolution: {integrity: sha512-byHDAVQt3zlQ61uEBDKlOlY3uX2O+ahNVV1bnnHhnv4fNIlu+NWefxlRUqAxVM80tlCrS/+aXA8AlpkIZsa1tg==} + /ai-connector@0.0.12(react@18.2.0)(svelte@3.59.1): + resolution: {integrity: sha512-sUnGgl3w7dd+j1TQSH9SifQv6eoei7f7kqOgnY2OhOVOoUyQ1vJeum7jkO38OAEQ8hU0vA0yYsR3G4TkcsMFEg==} engines: {node: '>=14.6'} peerDependencies: react: ^18.0.0 + svelte: ^3.29.0 + peerDependenciesMeta: + react: + optional: true + svelte: + optional: true dependencies: eventsource-parser: 1.0.0 nanoid: 3.3.6 react: 18.2.0 + sswr: 1.10.0(svelte@3.59.1) + svelte: 3.59.1 swr: 2.1.5(react@18.2.0) dev: false @@ -1215,10 +1378,23 @@ packages: get-intrinsic: 1.2.0 dev: true + /asn1js@3.0.5: + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} + dependencies: + pvtsutils: 1.3.2 + pvutils: 1.1.3 + tslib: 2.5.0 + dev: false + /ast-types-flow@0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + /autoprefixer@10.4.14(postcss@8.4.23): resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} @@ -1342,6 +1518,20 @@ packages: engines: {node: '>= 6'} dev: true + /camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + dev: false + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: false + /camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} @@ -1440,6 +1630,13 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + /comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} dev: false @@ -1487,6 +1684,10 @@ packages: hasBin: true dev: true + /csstype@3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: false + /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} @@ -1556,6 +1757,11 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /deepmerge@4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + engines: {node: '>=0.10.0'} + dev: false + /default-browser-id@3.0.0: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} engines: {node: '>=12'} @@ -1587,6 +1793,11 @@ packages: object-keys: 1.1.1 dev: true + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1636,6 +1847,13 @@ packages: esutils: 2.0.3 dev: true + /dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + dev: false + /dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} @@ -2472,6 +2690,15 @@ packages: is-callable: 1.2.7 dev: true + /form-data@3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + /format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -3036,8 +3263,9 @@ packages: hasBin: true dev: true - /jose@4.14.4: - resolution: {integrity: sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==} + /js-cookie@3.0.1: + resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==} + engines: {node: '>=12'} dev: false /js-sdsl@4.4.0: @@ -3158,6 +3386,12 @@ packages: dependencies: js-tokens: 4.0.0 + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.5.0 + dev: false + /lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} dependencies: @@ -3178,6 +3412,11 @@ packages: es5-ext: 0.10.62 dev: true + /map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: false + /markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} dev: false @@ -3611,6 +3850,18 @@ packages: picomatch: 2.3.1 dev: true + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3739,6 +3990,17 @@ packages: - babel-plugin-macros dev: false + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.5.0 + dev: false + + /node-fetch-native@1.0.1: + resolution: {integrity: sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==} + dev: false + /node-fetch@2.6.11: resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==} engines: {node: 4.x || >=6.0.0} @@ -3779,10 +4041,6 @@ packages: path-key: 4.0.0 dev: true - /oauth4webapi@2.3.0: - resolution: {integrity: sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA==} - dev: false - /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3956,6 +4214,10 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4066,19 +4328,6 @@ packages: source-map-js: 1.0.2 dev: true - /preact-render-to-string@5.2.3(preact@10.11.3): - resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} - peerDependencies: - preact: '>=10' - dependencies: - preact: 10.11.3 - pretty-format: 3.8.0 - dev: false - - /preact@10.11.3: - resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} - dev: false - /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4090,10 +4339,6 @@ packages: hasBin: true dev: true - /pretty-format@3.8.0: - resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} - dev: false - /prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} engines: {node: '>=6'} @@ -4126,10 +4371,26 @@ packages: engines: {node: '>=6'} dev: true + /pvtsutils@1.3.2: + resolution: {integrity: sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ==} + dependencies: + tslib: 2.5.0 + dev: false + + /pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -4154,6 +4415,14 @@ packages: - csstype dev: false + /react-intersection-observer@9.4.4(react@18.2.0): + resolution: {integrity: sha512-KQr8ezJscRSQZR0/TjogG4mZttCzdPZj8fhDzAK+xrPYOVvFjabRNy+4EY3UhS3fJaJyIedGjA8y4yKX+nc08w==} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4466,6 +4735,30 @@ packages: engines: {node: '>=12'} dev: true + /snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: false + + /snakecase-keys@3.2.1: + resolution: {integrity: sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==} + engines: {node: '>=8'} + dependencies: + map-obj: 4.3.0 + to-snake-case: 1.0.0 + dev: false + + /snakecase-keys@5.4.4: + resolution: {integrity: sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==} + engines: {node: '>=12'} + dependencies: + map-obj: 4.3.0 + snake-case: 3.0.4 + type-fest: 2.19.0 + dev: false + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -4478,6 +4771,15 @@ packages: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: false + /sswr@1.10.0(svelte@3.59.1): + resolution: {integrity: sha512-nLWAJSQy3h8t7rrbTXanRyVHuQPj4PwKIVGe4IMlxJFdhyaxnN/JGACnvQKGDeWiTGYIZIx/jRuUsPEF0867Pg==} + peerDependencies: + svelte: ^3.29.0 + dependencies: + svelte: 3.59.1 + swrev: 3.0.0 + dev: false + /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} @@ -4604,6 +4906,19 @@ packages: engines: {node: '>= 0.4'} dev: true + /svelte@3.59.1: + resolution: {integrity: sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==} + engines: {node: '>= 8'} + dev: false + + /swr@1.3.0(react@18.2.0): + resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /swr@2.1.5(react@18.2.0): resolution: {integrity: sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==} peerDependencies: @@ -4613,6 +4928,10 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false + /swrev@3.0.0: + resolution: {integrity: sha512-QJuZiptdOmbDY45pECBRVEgnoBlOKjeT2MWVz04wKHpWX15hM3P7EjcIbHDg5yLoPCMQ7to3349MEE+l9QF5HA==} + dev: false + /synckit@0.8.5: resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4703,6 +5022,10 @@ packages: engines: {node: '>=12'} dev: true + /to-no-case@1.0.2: + resolution: {integrity: sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4710,6 +5033,18 @@ packages: is-number: 7.0.0 dev: true + /to-snake-case@1.0.0: + resolution: {integrity: sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==} + dependencies: + to-space-case: 1.0.0 + dev: false + + /to-space-case@1.0.0: + resolution: {integrity: sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==} + dependencies: + to-no-case: 1.0.2 + dev: false + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: false @@ -4739,6 +5074,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib@2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: false + /tslib@2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} @@ -4764,6 +5103,11 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: false + /type@1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} dev: true @@ -4978,6 +5322,16 @@ packages: graceful-fs: 4.2.11 dev: false + /webcrypto-core@1.7.7: + resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} + dependencies: + '@peculiar/asn1-schema': 2.3.6 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.5 + pvtsutils: 1.3.2 + tslib: 2.5.0 + dev: false + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..0eb831f Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..620c7d8 Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7452b5d Binary files /dev/null and b/public/favicon.ico differ