├── public └── favicon.ico ├── postcss.config.js ├── src ├── i18n │ ├── config.ts │ └── request.ts ├── lib │ ├── auth-client.ts │ ├── utils.ts │ └── auth.ts ├── app │ ├── api │ │ ├── auth │ │ │ └── [...all] │ │ │ │ └── route.ts │ │ └── trpc │ │ │ └── [trpc] │ │ │ └── route.ts │ ├── login │ │ └── page.tsx │ ├── page.tsx │ ├── register │ │ └── page.tsx │ └── layout.tsx ├── components │ ├── theme │ │ ├── provider.tsx │ │ ├── toast-container.tsx │ │ └── toggle.tsx │ ├── top-loader.tsx │ ├── bear.tsx │ ├── toast.tsx │ ├── ui │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ └── select.tsx │ ├── language │ │ └── toggle.tsx │ ├── auth │ │ ├── user-button.tsx │ │ ├── login-form.tsx │ │ └── register-form.tsx │ └── post.tsx ├── store │ └── bear.ts ├── server │ ├── db.ts │ ├── locale.ts │ └── api │ │ ├── root.ts │ │ ├── routers │ │ └── post.ts │ │ └── trpc.ts ├── trpc │ ├── query-client.ts │ ├── server.ts │ └── react.tsx ├── env.js └── styles │ └── globals.css ├── .vscode ├── extensions.json └── settings.json ├── prisma ├── migrations │ ├── migration_lock.toml │ ├── 20250516075647_ │ │ └── migration.sql │ └── 20250701170628_ │ │ └── migration.sql └── schema.prisma ├── lefthook.yml ├── next.config.js ├── components.json ├── .github └── dependabot.yml ├── .gitignore ├── .env.example ├── tsconfig.json ├── README.md ├── messages ├── zh.json └── en.json ├── package.json ├── biome.jsonc ├── start-database.sh └── bun.lock /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun0225SUN/nextjs-starter/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /src/i18n/config.ts: -------------------------------------------------------------------------------- 1 | export type Locale = 'en' | 'zh' 2 | 3 | export const defaultLocale: Locale = 'en' 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["biomejs.biome"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/lib/auth-client.ts: -------------------------------------------------------------------------------- 1 | import { createAuthClient } from 'better-auth/react' 2 | 3 | export const authClient = createAuthClient() 4 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" 4 | -------------------------------------------------------------------------------- /src/app/api/auth/[...all]/route.ts: -------------------------------------------------------------------------------- 1 | import { toNextJsHandler } from 'better-auth/next-js' 2 | import { auth } from '~/lib/auth' 3 | 4 | export const { POST, GET } = toNextJsHandler(auth) 5 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | commands: 3 | check: 4 | glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" 5 | run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} 6 | stage_fixed: true 7 | -------------------------------------------------------------------------------- /src/i18n/request.ts: -------------------------------------------------------------------------------- 1 | import { getRequestConfig } from 'next-intl/server' 2 | import { getUserLocale } from '~/server/locale' 3 | 4 | export default getRequestConfig(async () => { 5 | const locale = await getUserLocale() 6 | 7 | return { 8 | locale, 9 | messages: (await import(`../../messages/${locale}.json`)).default, 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/components/theme/provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 4 | import type * as React from 'react' 5 | 6 | export function ThemeProvider({ 7 | children, 8 | ...props 9 | }: React.ComponentProps) { 10 | return {children} 11 | } 12 | -------------------------------------------------------------------------------- /src/components/theme/toast-container.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useTheme } from 'next-themes' 4 | import { ToastContainer } from 'react-toastify' 5 | import 'react-toastify/dist/ReactToastify.css' 6 | 7 | export function ThemeToastContainer() { 8 | const { resolvedTheme } = useTheme() 9 | 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /prisma/migrations/20250516075647_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Post" ( 3 | "id" SERIAL NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 | "updatedAt" TIMESTAMP(3) NOT NULL, 7 | 8 | CONSTRAINT "Post_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE INDEX "Post_name_idx" ON "Post"("name"); 13 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | import './src/env.js' 6 | import createNextIntlPlugin from 'next-intl/plugin' 7 | 8 | /** @type {import("next").NextConfig} */ 9 | const nextConfig = {} 10 | 11 | const withNextIntl = createNextIntlPlugin() 12 | export default withNextIntl(nextConfig) 13 | -------------------------------------------------------------------------------- /src/components/top-loader.tsx: -------------------------------------------------------------------------------- 1 | import NextTopLoader from 'nextjs-toploader' 2 | 3 | export function TopLoader() { 4 | return ( 5 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/store/bear.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand' 2 | import { persist } from 'zustand/middleware' 3 | 4 | interface BearStore { 5 | bears: number 6 | addABear: () => void 7 | removeABear: () => void 8 | } 9 | 10 | export const useBearStore = create()( 11 | persist( 12 | (set, get) => ({ 13 | bears: 0, 14 | addABear: () => set({ bears: get().bears + 1 }), 15 | removeABear: () => set({ bears: get().bears - 1 }), 16 | }), 17 | { 18 | name: 'bears', 19 | }, 20 | ), 21 | ) 22 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "~/components", 15 | "utils": "~/lib/utils", 16 | "ui": "~/components/ui", 17 | "lib": "~/lib", 18 | "hooks": "~/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /src/server/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client' 2 | 3 | import { env } from '~/env' 4 | 5 | const createPrismaClient = () => 6 | new PrismaClient({ 7 | log: 8 | env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], 9 | }) 10 | 11 | const globalForPrisma = globalThis as unknown as { 12 | prisma: ReturnType | undefined 13 | } 14 | 15 | export const db = globalForPrisma.prisma ?? createPrismaClient() 16 | 17 | if (env.NODE_ENV !== 'production') globalForPrisma.prisma = db 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /src/server/locale.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { cookies } from 'next/headers' 4 | import { defaultLocale, type Locale } from '~/i18n/config' 5 | 6 | // In this example the locale is read from a cookie. You could alternatively 7 | // also read it from a database, backend service, or any other source. 8 | const COOKIE_NAME = 'NEXT_LOCALE' 9 | 10 | export async function getUserLocale() { 11 | return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale 12 | } 13 | 14 | export async function setUserLocale(locale: Locale) { 15 | ;(await cookies()).set(COOKIE_NAME, locale) 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/auth.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client' 2 | import { betterAuth } from 'better-auth' 3 | import { prismaAdapter } from 'better-auth/adapters/prisma' 4 | import { headers } from 'next/headers' 5 | 6 | const prisma = new PrismaClient() 7 | 8 | export const auth = betterAuth({ 9 | emailAndPassword: { 10 | enabled: true, 11 | verifyEmail: false, 12 | }, 13 | database: prismaAdapter(prisma, { 14 | provider: 'postgresql', 15 | }), 16 | }) 17 | 18 | export async function getServerSession() { 19 | return await auth.api.getSession({ 20 | headers: await headers(), 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/bear.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Button } from '~/components/ui/button' 4 | import { useBearStore } from '~/store/bear' 5 | 6 | export function Bear() { 7 | const { bears, addABear, removeABear } = useBearStore() 8 | return ( 9 | <> 10 | {bears} 11 |
12 | 18 | 24 |
25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/components/toast.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { toast, Zoom } from 'react-toastify' 4 | import { Button } from '~/components/ui/button' 5 | 6 | export function Toast() { 7 | return ( 8 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/server/api/root.ts: -------------------------------------------------------------------------------- 1 | import { postRouter } from '~/server/api/routers/post' 2 | import { createCallerFactory, createTRPCRouter } from '~/server/api/trpc' 3 | 4 | /** 5 | * This is the primary router for your server. 6 | * 7 | * All routers added in /api/routers should be manually added here. 8 | */ 9 | export const appRouter = createTRPCRouter({ 10 | post: postRouter, 11 | }) 12 | 13 | // export type definition of API 14 | export type AppRouter = typeof appRouter 15 | 16 | /** 17 | * Create a server-side caller for the tRPC API. 18 | * @example 19 | * const trpc = createCaller(createContext); 20 | * const res = await trpc.post.all(); 21 | * ^? Post[] 22 | */ 23 | export const createCaller = createCallerFactory(appRouter) 24 | -------------------------------------------------------------------------------- /src/trpc/query-client.ts: -------------------------------------------------------------------------------- 1 | import { defaultShouldDehydrateQuery, QueryClient } from '@tanstack/react-query' 2 | import SuperJSON from 'superjson' 3 | 4 | export const createQueryClient = () => 5 | new QueryClient({ 6 | defaultOptions: { 7 | queries: { 8 | // With SSR, we usually want to set some default staleTime 9 | // above 0 to avoid refetching immediately on the client 10 | staleTime: 30 * 1000, 11 | }, 12 | dehydrate: { 13 | serializeData: SuperJSON.serialize, 14 | shouldDehydrateQuery: (query) => 15 | defaultShouldDehydrateQuery(query) || 16 | query.state.status === 'pending', 17 | }, 18 | hydrate: { 19 | deserializeData: SuperJSON.deserialize, 20 | }, 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | db.sqlite 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | next-env.d.ts 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | .pnpm-debug.log* 33 | 34 | # local env files 35 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 36 | .env 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | 45 | # idea files 46 | .idea -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as LabelPrimitive from "@radix-ui/react-label" 4 | import { cva, type VariantProps } from "class-variance-authority" 5 | import * as React from "react" 6 | 7 | import { cn } from "~/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /src/server/api/routers/post.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import { createTRPCRouter, publicProcedure } from '~/server/api/trpc' 4 | 5 | export const postRouter = createTRPCRouter({ 6 | hello: publicProcedure 7 | .input(z.object({ text: z.string() })) 8 | .query(({ input }) => { 9 | return { 10 | greeting: `Hello ${input.text}`, 11 | } 12 | }), 13 | 14 | create: publicProcedure 15 | .input(z.object({ name: z.string().min(1) })) 16 | .mutation(async ({ ctx, input }) => { 17 | return ctx.db.post.create({ 18 | data: { 19 | name: input.name, 20 | }, 21 | }) 22 | }), 23 | 24 | getLatest: publicProcedure.query(async ({ ctx }) => { 25 | const post = await ctx.db.post.findFirst({ 26 | orderBy: { createdAt: 'desc' }, 27 | }) 28 | 29 | return post ?? null 30 | }), 31 | }) 32 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Since the ".env" file is gitignored, you can use the ".env.example" file to 2 | # build a new ".env" file when you clone the repo. Keep this file up-to-date 3 | # when you add new variables to `.env`. 4 | 5 | # This file will be committed to version control, so make sure not to have any 6 | # secrets in it. If you are cloning this repo, create a copy of this file named 7 | # ".env" and populate it with your secrets. 8 | 9 | # When adding additional environment variables, the schema in "/src/env.js" 10 | # should be updated accordingly. 11 | 12 | # Prisma 13 | # https://www.prisma.io/docs/reference/database-reference/connection-urls#env 14 | DATABASE_URL="postgresql://postgres:password@localhost:5432/nextjs-starter-template" 15 | 16 | # Auth 17 | # https://www.better-auth.com/docs/installation#set-environment-variables 18 | # openssl rand -base64 32 19 | BETTER_AUTH_SECRET="ShF5vSuOm5xWCgnuqvHsx6lj7VhceIlkT9NebI0QO0w=" 20 | BETTER_AUTH_URL="http://localhost:3000" -------------------------------------------------------------------------------- /src/trpc/server.ts: -------------------------------------------------------------------------------- 1 | import 'server-only' 2 | 3 | import { createHydrationHelpers } from '@trpc/react-query/rsc' 4 | import { headers } from 'next/headers' 5 | import { cache } from 'react' 6 | 7 | import { type AppRouter, createCaller } from '~/server/api/root' 8 | import { createTRPCContext } from '~/server/api/trpc' 9 | import { createQueryClient } from './query-client' 10 | 11 | /** 12 | * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when 13 | * handling a tRPC call from a React Server Component. 14 | */ 15 | const createContext = cache(async () => { 16 | const heads = new Headers(await headers()) 17 | heads.set('x-trpc-source', 'rsc') 18 | 19 | return createTRPCContext({ 20 | headers: heads, 21 | }) 22 | }) 23 | 24 | const getQueryClient = cache(createQueryClient) 25 | const caller = createCaller(createContext) 26 | 27 | export const { trpc: api, HydrateClient } = createHydrationHelpers( 28 | caller, 29 | getQueryClient, 30 | ) 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Base Options: */ 4 | "esModuleInterop": true, 5 | "skipLibCheck": true, 6 | "target": "es2022", 7 | "allowJs": true, 8 | "resolveJsonModule": true, 9 | "moduleDetection": "force", 10 | "isolatedModules": true, 11 | "verbatimModuleSyntax": true, 12 | 13 | /* Strictness */ 14 | "strict": true, 15 | "noUncheckedIndexedAccess": true, 16 | "checkJs": true, 17 | 18 | /* Bundled projects */ 19 | "lib": ["dom", "dom.iterable", "ES2022"], 20 | "noEmit": true, 21 | "module": "ESNext", 22 | "moduleResolution": "Bundler", 23 | "jsx": "preserve", 24 | "plugins": [{ "name": "next" }], 25 | "incremental": true, 26 | 27 | /* Path Aliases */ 28 | "baseUrl": ".", 29 | "paths": { 30 | "~/*": ["./src/*"] 31 | } 32 | }, 33 | "include": [ 34 | "next-env.d.ts", 35 | "**/*.ts", 36 | "**/*.tsx", 37 | "**/*.cjs", 38 | "**/*.js", 39 | ".next/types/**/*.ts" 40 | ], 41 | "exclude": ["node_modules"] 42 | } 43 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "~/lib/utils" 4 | 5 | function Input({ className, type, ...props }: React.ComponentProps<"input">) { 6 | return ( 7 | 18 | ) 19 | } 20 | 21 | export { Input } 22 | -------------------------------------------------------------------------------- /src/app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import { getTranslations } from 'next-intl/server' 3 | import { LoginForm } from '~/components/auth/login-form' 4 | 5 | export default async function LoginPage() { 6 | const t = await getTranslations('Auth') 7 | 8 | return ( 9 |
10 |
11 |
12 |

{t('login')}

13 |

14 | {t('loginDescription')} 15 |

16 |
17 | 18 | 19 | 20 |

21 | {t('dontHaveAccount')}{' '} 22 | 26 | {t('register')} 27 | 28 |

29 |
30 |
31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getTranslations } from 'next-intl/server' 2 | import { UserButton } from '~/components/auth/user-button' 3 | import { Bear } from '~/components/bear' 4 | import { LanguageToggle } from '~/components/language/toggle' 5 | import { LatestPost } from '~/components/post' 6 | import { ThemeToggle } from '~/components/theme/toggle' 7 | import { Toast } from '~/components/toast' 8 | import { getServerSession } from '~/lib/auth' 9 | import { api, HydrateClient } from '~/trpc/server' 10 | 11 | export default async function HomePage() { 12 | const t = await getTranslations('HomePage') 13 | const session = await getServerSession() 14 | 15 | void api.post.getLatest.prefetch() 16 | 17 | return ( 18 | 19 |
20 | 21 | 22 |

{t('title')}

23 | 24 | 25 | 26 | 27 |
28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/app/register/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import { getTranslations } from 'next-intl/server' 3 | import { RegisterForm } from '~/components/auth/register-form' 4 | 5 | export default async function RegisterPage() { 6 | const t = await getTranslations('Auth') 7 | 8 | return ( 9 |
10 |
11 |
12 |

{t('register')}

13 |

14 | {t('registerDescription')} 15 |

16 |
17 | 18 | 19 | 20 |

21 | {t('alreadyHaveAccount')}{' '} 22 | 26 | {t('login')} 27 | 28 |

29 |
30 |
31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /src/app/api/trpc/[trpc]/route.ts: -------------------------------------------------------------------------------- 1 | import { fetchRequestHandler } from '@trpc/server/adapters/fetch' 2 | import type { NextRequest } from 'next/server' 3 | 4 | import { env } from '~/env' 5 | import { appRouter } from '~/server/api/root' 6 | import { createTRPCContext } from '~/server/api/trpc' 7 | 8 | /** 9 | * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when 10 | * handling a HTTP request (e.g. when you make requests from Client Components). 11 | */ 12 | const createContext = async (req: NextRequest) => { 13 | return createTRPCContext({ 14 | headers: req.headers, 15 | }) 16 | } 17 | 18 | const handler = (req: NextRequest) => 19 | fetchRequestHandler({ 20 | endpoint: '/api/trpc', 21 | req, 22 | router: appRouter, 23 | createContext: () => createContext(req), 24 | onError: 25 | env.NODE_ENV === 'development' 26 | ? ({ path, error }) => { 27 | console.error( 28 | `❌ tRPC failed on ${path ?? ''}: ${error.message}`, 29 | ) 30 | } 31 | : undefined, 32 | }) 33 | 34 | export { handler as GET, handler as POST } 35 | -------------------------------------------------------------------------------- /src/components/theme/toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Moon, Sun } from 'lucide-react' 4 | import { useTheme } from 'next-themes' 5 | import { cn } from '~/lib/utils' 6 | 7 | interface ThemeToggleProps { 8 | className?: string 9 | size?: number 10 | strokeWidth?: number 11 | } 12 | 13 | export function ThemeToggle({ 14 | size = 20, 15 | strokeWidth = 2, 16 | className, 17 | }: ThemeToggleProps) { 18 | const { setTheme, resolvedTheme } = useTheme() 19 | 20 | const toggleTheme = () => { 21 | const newTheme = resolvedTheme === 'light' ? 'dark' : 'light' 22 | 23 | if (!document.startViewTransition) { 24 | setTheme(newTheme) 25 | } else { 26 | document.startViewTransition(() => setTheme(newTheme)) 27 | } 28 | } 29 | 30 | return ( 31 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false, 3 | "prettier.enable": false, 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "quickfix.biome": "explicit", 7 | "source.organizeImports.biome": "explicit", 8 | "source.formatDocument.biome": "explicit" 9 | }, 10 | "editor.defaultFormatter": "biomejs.biome", 11 | "[typescriptreact]": { 12 | "editor.defaultFormatter": "biomejs.biome" 13 | }, 14 | "[typescript]": { 15 | "editor.defaultFormatter": "biomejs.biome" 16 | }, 17 | "[javascript]": { 18 | "editor.defaultFormatter": "biomejs.biome" 19 | }, 20 | "[javascriptreact]": { 21 | "editor.defaultFormatter": "biomejs.biome" 22 | }, 23 | "[json]": { 24 | "editor.defaultFormatter": "biomejs.biome" 25 | }, 26 | "[jsonc]": { 27 | "editor.defaultFormatter": "biomejs.biome" 28 | }, 29 | "[cjs]": { 30 | "editor.defaultFormatter": "biomejs.biome" 31 | }, 32 | "[mjs]": { 33 | "editor.defaultFormatter": "biomejs.biome" 34 | }, 35 | "[cts]": { 36 | "editor.defaultFormatter": "biomejs.biome" 37 | }, 38 | "[mts]": { 39 | "editor.defaultFormatter": "biomejs.biome" 40 | }, 41 | "[css]": { 42 | "editor.defaultFormatter": "biomejs.biome" 43 | }, 44 | "i18n-ally.localesPaths": ["messages", "src/i18n", "src/components/language"] 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Tech Stack 2 | 3 | - Framework: [Next.js](https://nextjs.org) 4 | - Language - [TypeScript]() 5 | - Styling: [Tailwind CSS](https://tailwindcss.com) 6 | - UI Library: [shadcn/ui](https://ui.shadcn.com) 7 | - State Management - [Zustand](https://zustand-demo.pmnd.rs) 8 | - Database: [Postgres](https://www.postgresql.org) 9 | - ORM: [Prisma](https://prisma.io) 10 | - Auth - [Better Auth](https://www.better-auth.com) 11 | - Schema Validations - [Zod](https://zod.dev) 12 | - formatter and linter - [Biome](https://biomejs.dev) 13 | - Git hooks - [Husky](https://lefthook.dev) 14 | 15 | ## Local Development 16 | 17 | ### Environment Variables 18 | 19 | Create a `.env` file at the project root and copy the following content: 20 | 21 | ```bash 22 | # Prisma Database Configuration 23 | DATABASE_URL="postgresql://postgres:password@localhost:5432/nextjs-starter-template" 24 | 25 | # Better Auth Configuration 26 | BETTER_AUTH_SECRET="ShF5vSuOm5xWCgnuqvHsx6lj7VhceIlkT9NebI0QO0w=" 27 | BETTER_AUTH_URL="http://localhost:3000" 28 | ``` 29 | 30 | ### Start the database and initialize 31 | 32 | ```bash 33 | ./start-database.sh 34 | ``` 35 | 36 | ```bash 37 | bun run db:push 38 | ``` 39 | 40 | ### Install dependencies 41 | 42 | ```bash 43 | bun install 44 | ``` 45 | 46 | ### Run the development server 47 | 48 | ```bash 49 | bun run dev 50 | ``` 51 | 52 | Open: `http://localhost:3000` -------------------------------------------------------------------------------- /src/components/language/toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useLocale, useTranslations } from 'next-intl' 4 | import { useTransition } from 'react' 5 | import { 6 | Select, 7 | SelectContent, 8 | SelectItem, 9 | SelectTrigger, 10 | SelectValue, 11 | } from '~/components/ui/select' 12 | import type { Locale } from '~/i18n/config' 13 | import { setUserLocale } from '~/server/locale' 14 | 15 | export function LanguageToggle() { 16 | const t = useTranslations('Language') 17 | const locale = useLocale() 18 | const [, startTransition] = useTransition() 19 | 20 | const handleLocaleChange = (value: string) => { 21 | const locale = value as Locale 22 | startTransition(() => { 23 | setUserLocale(locale) 24 | }) 25 | } 26 | 27 | return ( 28 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /messages/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "HomePage": { 3 | "title": "你好,世界!" 4 | }, 5 | "Language": { 6 | "en": "English", 7 | "zh": "简体中文" 8 | }, 9 | "Post": { 10 | "latestPost": "你最近的帖子:{name}", 11 | "noPost": "你还没有发布任何帖子。", 12 | "titlePlaceholder": "标题", 13 | "submitting": "提交中...", 14 | "submit": "提交" 15 | }, 16 | "Auth": { 17 | "login": "登录", 18 | "register": "注册", 19 | "logout": "退出登录", 20 | "email": "邮箱", 21 | "password": "密码", 22 | "username": "用户名", 23 | "loginLoading": "登录中...", 24 | "registerLoading": "注册中...", 25 | "loginError": "登录失败,请检查邮箱和密码", 26 | "registerError": "注册失败,请检查输入信息", 27 | "networkError": "网络错误,请稍后重试", 28 | "emailInvalid": "请输入有效的邮箱地址", 29 | "passwordRequired": "请输入密码", 30 | "usernameRequired": "用户名必须在2-50个字符之间", 31 | "passwordRequirements": { 32 | "length": "密码至少需要8个字符", 33 | "uppercase": "密码需要包含至少一个大写字母", 34 | "lowercase": "密码需要包含至少一个小写字母", 35 | "number": "密码需要包含至少一个数字" 36 | }, 37 | "autoLoginFailed": "自动登录失败,请尝试手动登录", 38 | "togglePassword": "切换密码可见性", 39 | "dontHaveAccount": "还没有账号?", 40 | "alreadyHaveAccount": "已有账号?", 41 | "loginDescription": "请输入您的邮箱和密码登录", 42 | "registerDescription": "请填写以下信息完成注册", 43 | "emailPlaceholder": "请输入邮箱地址", 44 | "usernamePlaceholder": "请输入用户名", 45 | "passwordPlaceholder": "请输入密码", 46 | "forgotPassword": "忘记密码?", 47 | "loginWithGoogle": "使用 Google 账号登录", 48 | "registerWithGoogle": "使用 Google 账号注册" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/auth/user-button.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { LogOut } from 'lucide-react' 4 | import Link from 'next/link' 5 | import { useRouter } from 'next/navigation' 6 | import { useTranslations } from 'next-intl' 7 | import { Button } from '~/components/ui/button' 8 | import { authClient } from '~/lib/auth-client' 9 | 10 | interface UserButtonProps { 11 | user?: { 12 | name?: string | null 13 | email?: string | null 14 | } | null 15 | } 16 | 17 | export function UserButton({ user }: UserButtonProps) { 18 | const router = useRouter() 19 | const t = useTranslations('Auth') 20 | 21 | const handleSignOut = async () => { 22 | await authClient.signOut() 23 | router.refresh() 24 | } 25 | 26 | if (!user) { 27 | return ( 28 | 34 | ) 35 | } 36 | 37 | return ( 38 |
39 |
40 | {user.name &&

{user.name}

} 41 | {user.email && ( 42 |

{user.email}

43 | )} 44 |
45 | 46 | 55 |
56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/globals.css' 2 | import type { Metadata } from 'next' 3 | import { Geist } from 'next/font/google' 4 | import { NextIntlClientProvider } from 'next-intl' 5 | import { getLocale } from 'next-intl/server' 6 | import { ThemeProvider } from '~/components/theme/provider' 7 | import { ThemeToastContainer } from '~/components/theme/toast-container' 8 | import { TopLoader } from '~/components/top-loader' 9 | import { cn } from '~/lib/utils' 10 | import { TRPCReactProvider } from '~/trpc/react' 11 | 12 | export const metadata: Metadata = { 13 | title: 'Nextjs Starter', 14 | description: 'Nextjs Starter', 15 | icons: [{ rel: 'icon', url: '/favicon.ico' }], 16 | } 17 | 18 | const geist = Geist({ 19 | subsets: ['latin'], 20 | variable: '--font-geist-sans', 21 | }) 22 | 23 | export default async function RootLayout({ 24 | children, 25 | }: Readonly<{ children: React.ReactNode }>) { 26 | const locale = await getLocale() 27 | 28 | return ( 29 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | {children} 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/components/post.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useTranslations } from 'next-intl' 4 | import { useState } from 'react' 5 | import { Button } from '~/components/ui/button' 6 | import { Input } from '~/components/ui/input' 7 | import { api } from '~/trpc/react' 8 | 9 | export function LatestPost() { 10 | const t = useTranslations('Post') 11 | const [latestPost] = api.post.getLatest.useSuspenseQuery() 12 | 13 | const utils = api.useUtils() 14 | const [name, setName] = useState('') 15 | const createPost = api.post.create.useMutation({ 16 | onSuccess: async () => { 17 | await utils.post.invalidate() 18 | setName('') 19 | }, 20 | }) 21 | 22 | return ( 23 |
24 |
{ 26 | e.preventDefault() 27 | createPost.mutate({ name }) 28 | }} 29 | className='flex flex-col gap-6 text-center' 30 | > 31 | {latestPost ? ( 32 |

33 | {t('latestPost', { name: latestPost.name })} 34 |

35 | ) : ( 36 |

{t('noPost')}

37 | )} 38 | setName(e.target.value)} 43 | required 44 | /> 45 | 51 |
52 |
53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /src/env.js: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | export const env = createEnv({ 5 | /** 6 | * Specify your server-side environment variables schema here. This way you can ensure the app 7 | * isn't built with invalid env vars. 8 | */ 9 | server: { 10 | DATABASE_URL: z.string().url(), 11 | BETTER_AUTH_URL: z.string().url(), 12 | NODE_ENV: z 13 | .enum(['development', 'test', 'production']) 14 | .default('development'), 15 | }, 16 | 17 | /** 18 | * Specify your client-side environment variables schema here. This way you can ensure the app 19 | * isn't built with invalid env vars. To expose them to the client, prefix them with 20 | * `NEXT_PUBLIC_`. 21 | */ 22 | client: { 23 | // NEXT_PUBLIC_CLIENTVAR: z.string(), 24 | }, 25 | 26 | /** 27 | * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. 28 | * middlewares) or client-side so we need to destruct manually. 29 | */ 30 | runtimeEnv: { 31 | DATABASE_URL: process.env.DATABASE_URL, 32 | BETTER_AUTH_URL: process.env.BETTER_AUTH_URL, 33 | NODE_ENV: process.env.NODE_ENV, 34 | // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, 35 | }, 36 | /** 37 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially 38 | * useful for Docker builds. 39 | */ 40 | skipValidation: !!process.env.SKIP_ENV_VALIDATION, 41 | /** 42 | * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and 43 | * `SOME_VAR=''` will throw an error. 44 | */ 45 | emptyStringAsUndefined: true, 46 | }) 47 | -------------------------------------------------------------------------------- /messages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "HomePage": { 3 | "title": "Hello world!" 4 | }, 5 | "Language": { 6 | "en": "English", 7 | "zh": "简体中文" 8 | }, 9 | "Post": { 10 | "latestPost": "Your most recent post: {name}", 11 | "noPost": "You have no posts yet.", 12 | "titlePlaceholder": "Title", 13 | "submitting": "Submitting...", 14 | "submit": "Submit" 15 | }, 16 | "Auth": { 17 | "login": "Login", 18 | "register": "Register", 19 | "logout": "Logout", 20 | "email": "Email", 21 | "password": "Password", 22 | "username": "Username", 23 | "loginLoading": "Logging in...", 24 | "registerLoading": "Registering...", 25 | "loginError": "Login failed, please check your email and password", 26 | "registerError": "Registration failed, please check your input", 27 | "networkError": "Network error, please try again later", 28 | "emailInvalid": "Please enter a valid email address", 29 | "passwordRequired": "Please enter your password", 30 | "usernameRequired": "Username must be between 2-50 characters", 31 | "passwordRequirements": { 32 | "length": "Password must be at least 8 characters", 33 | "uppercase": "Password must contain at least one uppercase letter", 34 | "lowercase": "Password must contain at least one lowercase letter", 35 | "number": "Password must contain at least one number" 36 | }, 37 | "autoLoginFailed": "Auto login failed, please try to login manually", 38 | "togglePassword": "Toggle password visibility", 39 | "dontHaveAccount": "Don't have an account?", 40 | "alreadyHaveAccount": "Already have an account?", 41 | "loginDescription": "Enter your email below to login to your account", 42 | "registerDescription": "Enter your details below to create your account", 43 | "emailPlaceholder": "m@example.com", 44 | "usernamePlaceholder": "Enter your name", 45 | "passwordPlaceholder": "Enter your password", 46 | "forgotPassword": "Forgot password?", 47 | "loginWithGoogle": "Login with Google", 48 | "registerWithGoogle": "Register with Google" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "next dev --turbo", 8 | "build": "next build", 9 | "vercel-build": "bun run db:migrate && next build", 10 | "preview": "next build && next start", 11 | "start": "next start", 12 | "check": "biome check .", 13 | "typecheck": "tsc --noEmit", 14 | "check:unsafe": "biome check --write --unsafe .", 15 | "check:write": "biome check --write .", 16 | "db:generate": "prisma migrate dev", 17 | "db:migrate": "prisma migrate deploy", 18 | "db:push": "prisma db push", 19 | "db:studio": "prisma studio", 20 | "postinstall": "prisma generate" 21 | }, 22 | "dependencies": { 23 | "@hookform/resolvers": "^5.2.1", 24 | "@prisma/client": "^6.14.0", 25 | "@radix-ui/react-label": "^2.1.7", 26 | "@radix-ui/react-select": "^2.2.6", 27 | "@radix-ui/react-slot": "^1.2.3", 28 | "@t3-oss/env-nextjs": "^0.13.8", 29 | "@tanstack/react-query": "^5.85.5", 30 | "@trpc/client": "^11.5.0", 31 | "@trpc/react-query": "^11.5.0", 32 | "@trpc/server": "^11.5.0", 33 | "better-auth": "^1.3.7", 34 | "class-variance-authority": "^0.7.1", 35 | "clsx": "^2.1.1", 36 | "lefthook": "^1.12.3", 37 | "lucide-react": "^0.542.0", 38 | "next": "^15.5.2", 39 | "next-intl": "^4.3.5", 40 | "next-themes": "^0.4.6", 41 | "nextjs-toploader": "^3.8.16", 42 | "react": "^19.1.1", 43 | "react-dom": "^19.1.1", 44 | "react-hook-form": "^7.62.0", 45 | "react-toastify": "^11.0.5", 46 | "server-only": "^0.0.1", 47 | "superjson": "^2.2.2", 48 | "tailwind-merge": "^3.3.1", 49 | "zod": "^4.1.3", 50 | "zustand": "^5.0.8" 51 | }, 52 | "devDependencies": { 53 | "@biomejs/biome": "2.2.2", 54 | "@tailwindcss/postcss": "^4.1.12", 55 | "@types/node": "^24.3.0", 56 | "@types/react": "^19.1.11", 57 | "@types/react-dom": "^19.1.8", 58 | "postcss": "^8.5.6", 59 | "prisma": "^6.14.0", 60 | "tailwindcss": "^4.1.12", 61 | "tw-animate-css": "^1.3.7", 62 | "typescript": "^5.9.2" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "postgresql" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model Post { 14 | id Int @id @default(autoincrement()) 15 | name String 16 | createdAt DateTime @default(now()) 17 | updatedAt DateTime @updatedAt 18 | 19 | @@index([name]) 20 | } 21 | 22 | model User { 23 | id String @id 24 | name String 25 | email String 26 | emailVerified Boolean 27 | image String? 28 | createdAt DateTime 29 | updatedAt DateTime 30 | username String? 31 | displayUsername String? 32 | sessions Session[] 33 | accounts Account[] 34 | 35 | @@unique([email]) 36 | @@unique([username]) 37 | @@map("user") 38 | } 39 | 40 | model Session { 41 | id String @id 42 | expiresAt DateTime 43 | token String 44 | createdAt DateTime 45 | updatedAt DateTime 46 | ipAddress String? 47 | userAgent String? 48 | userId String 49 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 50 | 51 | @@unique([token]) 52 | @@map("session") 53 | } 54 | 55 | model Account { 56 | id String @id 57 | accountId String 58 | providerId String 59 | userId String 60 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 61 | accessToken String? 62 | refreshToken String? 63 | idToken String? 64 | accessTokenExpiresAt DateTime? 65 | refreshTokenExpiresAt DateTime? 66 | scope String? 67 | password String? 68 | createdAt DateTime 69 | updatedAt DateTime 70 | 71 | @@map("account") 72 | } 73 | 74 | model Verification { 75 | id String @id 76 | identifier String 77 | value String 78 | expiresAt DateTime 79 | createdAt DateTime? 80 | updatedAt DateTime? 81 | 82 | @@map("verification") 83 | } 84 | -------------------------------------------------------------------------------- /prisma/migrations/20250701170628_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "user" ( 3 | "id" TEXT NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "email" TEXT NOT NULL, 6 | "emailVerified" BOOLEAN NOT NULL, 7 | "image" TEXT, 8 | "createdAt" TIMESTAMP(3) NOT NULL, 9 | "updatedAt" TIMESTAMP(3) NOT NULL, 10 | "username" TEXT, 11 | "displayUsername" TEXT, 12 | 13 | CONSTRAINT "user_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- CreateTable 17 | CREATE TABLE "session" ( 18 | "id" TEXT NOT NULL, 19 | "expiresAt" TIMESTAMP(3) NOT NULL, 20 | "token" TEXT NOT NULL, 21 | "createdAt" TIMESTAMP(3) NOT NULL, 22 | "updatedAt" TIMESTAMP(3) NOT NULL, 23 | "ipAddress" TEXT, 24 | "userAgent" TEXT, 25 | "userId" TEXT NOT NULL, 26 | 27 | CONSTRAINT "session_pkey" PRIMARY KEY ("id") 28 | ); 29 | 30 | -- CreateTable 31 | CREATE TABLE "account" ( 32 | "id" TEXT NOT NULL, 33 | "accountId" TEXT NOT NULL, 34 | "providerId" TEXT NOT NULL, 35 | "userId" TEXT NOT NULL, 36 | "accessToken" TEXT, 37 | "refreshToken" TEXT, 38 | "idToken" TEXT, 39 | "accessTokenExpiresAt" TIMESTAMP(3), 40 | "refreshTokenExpiresAt" TIMESTAMP(3), 41 | "scope" TEXT, 42 | "password" TEXT, 43 | "createdAt" TIMESTAMP(3) NOT NULL, 44 | "updatedAt" TIMESTAMP(3) NOT NULL, 45 | 46 | CONSTRAINT "account_pkey" PRIMARY KEY ("id") 47 | ); 48 | 49 | -- CreateTable 50 | CREATE TABLE "verification" ( 51 | "id" TEXT NOT NULL, 52 | "identifier" TEXT NOT NULL, 53 | "value" TEXT NOT NULL, 54 | "expiresAt" TIMESTAMP(3) NOT NULL, 55 | "createdAt" TIMESTAMP(3), 56 | "updatedAt" TIMESTAMP(3), 57 | 58 | CONSTRAINT "verification_pkey" PRIMARY KEY ("id") 59 | ); 60 | 61 | -- CreateIndex 62 | CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); 63 | 64 | -- CreateIndex 65 | CREATE UNIQUE INDEX "user_username_key" ON "user"("username"); 66 | 67 | -- CreateIndex 68 | CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); 69 | 70 | -- AddForeignKey 71 | ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 72 | 73 | -- AddForeignKey 74 | ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; 75 | -------------------------------------------------------------------------------- /src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import { Slot } from "@radix-ui/react-slot" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | import * as React from "react" 4 | 5 | import { cn } from "~/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", 16 | outline: 17 | "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", 20 | ghost: 21 | "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", 22 | link: "text-primary underline-offset-4 hover:underline", 23 | }, 24 | size: { 25 | default: "h-9 px-4 py-2 has-[>svg]:px-3", 26 | sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", 27 | lg: "h-10 rounded-md px-6 has-[>svg]:px-4", 28 | icon: "size-9", 29 | }, 30 | }, 31 | defaultVariants: { 32 | variant: "default", 33 | size: "default", 34 | }, 35 | } 36 | ) 37 | 38 | function Button({ 39 | className, 40 | variant, 41 | size, 42 | asChild = false, 43 | ...props 44 | }: React.ComponentProps<"button"> & 45 | VariantProps & { 46 | asChild?: boolean 47 | }) { 48 | const Comp = asChild ? Slot : "button" 49 | 50 | return ( 51 | 56 | ) 57 | } 58 | 59 | export { Button, buttonVariants } 60 | -------------------------------------------------------------------------------- /src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "~/lib/utils" 4 | 5 | function Card({ className, ...props }: React.ComponentProps<"div">) { 6 | return ( 7 |
15 | ) 16 | } 17 | 18 | function CardHeader({ className, ...props }: React.ComponentProps<"div">) { 19 | return ( 20 |
28 | ) 29 | } 30 | 31 | function CardTitle({ className, ...props }: React.ComponentProps<"div">) { 32 | return ( 33 |
38 | ) 39 | } 40 | 41 | function CardDescription({ className, ...props }: React.ComponentProps<"div">) { 42 | return ( 43 |
48 | ) 49 | } 50 | 51 | function CardAction({ className, ...props }: React.ComponentProps<"div">) { 52 | return ( 53 |
61 | ) 62 | } 63 | 64 | function CardContent({ className, ...props }: React.ComponentProps<"div">) { 65 | return ( 66 |
71 | ) 72 | } 73 | 74 | function CardFooter({ className, ...props }: React.ComponentProps<"div">) { 75 | return ( 76 |
81 | ) 82 | } 83 | 84 | export { 85 | Card, 86 | CardHeader, 87 | CardFooter, 88 | CardTitle, 89 | CardAction, 90 | CardDescription, 91 | CardContent, 92 | } 93 | -------------------------------------------------------------------------------- /src/trpc/react.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { type QueryClient, QueryClientProvider } from '@tanstack/react-query' 4 | import { httpBatchStreamLink, loggerLink } from '@trpc/client' 5 | import { createTRPCReact } from '@trpc/react-query' 6 | import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server' 7 | import { useState } from 'react' 8 | import SuperJSON from 'superjson' 9 | 10 | import type { AppRouter } from '~/server/api/root' 11 | import { createQueryClient } from './query-client' 12 | 13 | let clientQueryClientSingleton: QueryClient | undefined 14 | const getQueryClient = () => { 15 | if (typeof window === 'undefined') { 16 | // Server: always make a new query client 17 | return createQueryClient() 18 | } 19 | // Browser: use singleton pattern to keep the same query client 20 | clientQueryClientSingleton ??= createQueryClient() 21 | 22 | return clientQueryClientSingleton 23 | } 24 | 25 | export const api = createTRPCReact() 26 | 27 | /** 28 | * Inference helper for inputs. 29 | * 30 | * @example type HelloInput = RouterInputs['example']['hello'] 31 | */ 32 | export type RouterInputs = inferRouterInputs 33 | 34 | /** 35 | * Inference helper for outputs. 36 | * 37 | * @example type HelloOutput = RouterOutputs['example']['hello'] 38 | */ 39 | export type RouterOutputs = inferRouterOutputs 40 | 41 | export function TRPCReactProvider(props: { children: React.ReactNode }) { 42 | const queryClient = getQueryClient() 43 | 44 | const [trpcClient] = useState(() => 45 | api.createClient({ 46 | links: [ 47 | loggerLink({ 48 | enabled: (op) => 49 | process.env.NODE_ENV === 'development' || 50 | (op.direction === 'down' && op.result instanceof Error), 51 | }), 52 | httpBatchStreamLink({ 53 | transformer: SuperJSON, 54 | url: `${getBaseUrl()}/api/trpc`, 55 | headers: () => { 56 | const headers = new Headers() 57 | headers.set('x-trpc-source', 'nextjs-react') 58 | return headers 59 | }, 60 | }), 61 | ], 62 | }), 63 | ) 64 | 65 | return ( 66 | 67 | 71 | {props.children} 72 | 73 | 74 | ) 75 | } 76 | 77 | function getBaseUrl() { 78 | if (typeof window !== 'undefined') return window.location.origin 79 | if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}` 80 | return `http://localhost:${process.env.PORT ?? 3000}` 81 | } 82 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", 3 | "files": { 4 | "ignoreUnknown": false, 5 | "experimentalScannerIgnores": ["src/components/ui/**"] 6 | }, 7 | "vcs": { 8 | "enabled": false, 9 | "clientKind": "git", 10 | "useIgnoreFile": false 11 | }, 12 | "linter": { 13 | "enabled": true, 14 | "rules": { 15 | "recommended": true, 16 | "suspicious": { 17 | "noDebugger": "off", 18 | "noArrayIndexKey": "off" 19 | }, 20 | "style": { 21 | "useTemplate": "off", 22 | "noNonNullAssertion": "off" 23 | }, 24 | "a11y": { 25 | "noSvgWithoutTitle": "off", 26 | "useSemanticElements": "off", 27 | "useKeyWithClickEvents": "off", 28 | "useMediaCaption": "off" 29 | }, 30 | "correctness": { 31 | "noUnusedImports": "error" 32 | }, 33 | "nursery": { 34 | "useSortedClasses": { 35 | "level": "warn", 36 | "fix": "safe", 37 | "options": { 38 | "attributes": ["className"], 39 | "functions": ["clsx", "cva", "cn"] 40 | } 41 | } 42 | } 43 | } 44 | }, 45 | "formatter": { 46 | "enabled": true, 47 | "formatWithErrors": false, 48 | "indentStyle": "space", 49 | "indentWidth": 2, 50 | "lineEnding": "lf", 51 | "lineWidth": 80, 52 | "attributePosition": "auto", 53 | "useEditorconfig": false 54 | }, 55 | "javascript": { 56 | "formatter": { 57 | "enabled": true, 58 | "quoteStyle": "single", 59 | "jsxQuoteStyle": "single", 60 | "quoteProperties": "asNeeded", 61 | "trailingCommas": "all", 62 | "semicolons": "asNeeded", 63 | "arrowParentheses": "always", 64 | "indentStyle": "space", 65 | "indentWidth": 2, 66 | "lineEnding": "lf", 67 | "lineWidth": 80, 68 | "bracketSameLine": false, 69 | "bracketSpacing": true, 70 | "attributePosition": "multiline" 71 | } 72 | }, 73 | "json": { 74 | "parser": { 75 | "allowComments": true 76 | }, 77 | "formatter": { 78 | "enabled": true, 79 | "indentStyle": "space", 80 | "indentWidth": 2, 81 | "lineEnding": "lf", 82 | "lineWidth": 80 83 | } 84 | }, 85 | "css": { 86 | "parser": { 87 | "cssModules": false 88 | }, 89 | "formatter": { 90 | "enabled": true, 91 | "indentStyle": "space", 92 | "indentWidth": 2, 93 | "lineEnding": "lf", 94 | "lineWidth": 80, 95 | "quoteStyle": "single" 96 | } 97 | }, 98 | "overrides": [ 99 | { 100 | "includes": ["src/components/ui/**"], 101 | "linter": { 102 | "enabled": false 103 | }, 104 | "formatter": { 105 | "enabled": false 106 | } 107 | } 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /start-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Use this script to start a docker container for a local development database 3 | 4 | # TO RUN ON WINDOWS: 5 | # 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install 6 | # 2. Install Docker Desktop or Podman Deskop 7 | # - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/ 8 | # - Podman Desktop - https://podman.io/getting-started/installation 9 | # 3. Open WSL - `wsl` 10 | # 4. Run this script - `./start-database.sh` 11 | 12 | # On Linux and macOS you can run this script directly - `./start-database.sh` 13 | 14 | # import env variables from .env 15 | set -a 16 | source .env 17 | 18 | DB_PASSWORD=$(echo "$DATABASE_URL" | awk -F':' '{print $3}' | awk -F'@' '{print $1}') 19 | DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}') 20 | DB_NAME=$(echo "$DATABASE_URL" | awk -F'/' '{print $4}') 21 | DB_CONTAINER_NAME="$DB_NAME-postgres" 22 | 23 | if ! [ -x "$(command -v docker)" ] && ! [ -x "$(command -v podman)" ]; then 24 | echo -e "Docker or Podman is not installed. Please install docker or podman and try again.\nDocker install guide: https://docs.docker.com/engine/install/\nPodman install guide: https://podman.io/getting-started/installation" 25 | exit 1 26 | fi 27 | 28 | # determine which docker command to use 29 | if [ -x "$(command -v docker)" ]; then 30 | DOCKER_CMD="docker" 31 | elif [ -x "$(command -v podman)" ]; then 32 | DOCKER_CMD="podman" 33 | fi 34 | 35 | if ! $DOCKER_CMD info > /dev/null 2>&1; then 36 | echo "$DOCKER_CMD daemon is not running. Please start $DOCKER_CMD and try again." 37 | exit 1 38 | fi 39 | 40 | if command -v nc >/dev/null 2>&1; then 41 | if nc -z localhost "$DB_PORT" 2>/dev/null; then 42 | echo "Port $DB_PORT is already in use." 43 | exit 1 44 | fi 45 | else 46 | echo "Warning: Unable to check if port $DB_PORT is already in use (netcat not installed)" 47 | read -p "Do you want to continue anyway? [y/N]: " -r REPLY 48 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 49 | echo "Aborting." 50 | exit 1 51 | fi 52 | fi 53 | 54 | if [ "$($DOCKER_CMD ps -q -f name=$DB_CONTAINER_NAME)" ]; then 55 | echo "Database container '$DB_CONTAINER_NAME' already running" 56 | exit 0 57 | fi 58 | 59 | if [ "$($DOCKER_CMD ps -q -a -f name=$DB_CONTAINER_NAME)" ]; then 60 | $DOCKER_CMD start "$DB_CONTAINER_NAME" 61 | echo "Existing database container '$DB_CONTAINER_NAME' started" 62 | exit 0 63 | fi 64 | 65 | if [ "$DB_PASSWORD" = "password" ]; then 66 | echo "You are using the default database password" 67 | read -p "Should we generate a random password for you? [y/N]: " -r REPLY 68 | if ! [[ $REPLY =~ ^[Yy]$ ]]; then 69 | echo "Please change the default password in the .env file and try again" 70 | exit 1 71 | fi 72 | # Generate a random URL-safe password 73 | DB_PASSWORD=$(openssl rand -base64 12 | tr '+/' '-_') 74 | sed -i '' "s#:password@#:$DB_PASSWORD@#" .env 75 | fi 76 | 77 | $DOCKER_CMD run -d \ 78 | --name $DB_CONTAINER_NAME \ 79 | -e POSTGRES_USER="postgres" \ 80 | -e POSTGRES_PASSWORD="$DB_PASSWORD" \ 81 | -e POSTGRES_DB="$DB_NAME" \ 82 | -p "$DB_PORT":5432 \ 83 | docker.io/postgres && echo "Database container '$DB_CONTAINER_NAME' was successfully created" 84 | -------------------------------------------------------------------------------- /src/server/api/trpc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: 3 | * 1. You want to modify request context (see Part 1). 4 | * 2. You want to create a new middleware or type of procedure (see Part 3). 5 | * 6 | * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will 7 | * need to use are documented accordingly near the end. 8 | */ 9 | import { initTRPC } from '@trpc/server' 10 | import superjson from 'superjson' 11 | import { ZodError } from 'zod' 12 | 13 | import { db } from '~/server/db' 14 | 15 | /** 16 | * 1. CONTEXT 17 | * 18 | * This section defines the "contexts" that are available in the backend API. 19 | * 20 | * These allow you to access things when processing a request, like the database, the session, etc. 21 | * 22 | * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each 23 | * wrap this and provides the required context. 24 | * 25 | * @see https://trpc.io/docs/server/context 26 | */ 27 | export const createTRPCContext = async (opts: { headers: Headers }) => { 28 | return { 29 | db, 30 | ...opts, 31 | } 32 | } 33 | 34 | /** 35 | * 2. INITIALIZATION 36 | * 37 | * This is where the tRPC API is initialized, connecting the context and transformer. We also parse 38 | * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation 39 | * errors on the backend. 40 | */ 41 | const t = initTRPC.context().create({ 42 | transformer: superjson, 43 | errorFormatter({ shape, error }) { 44 | return { 45 | ...shape, 46 | data: { 47 | ...shape.data, 48 | zodError: 49 | error.cause instanceof ZodError ? error.cause.flatten() : null, 50 | }, 51 | } 52 | }, 53 | }) 54 | 55 | /** 56 | * Create a server-side caller. 57 | * 58 | * @see https://trpc.io/docs/server/server-side-calls 59 | */ 60 | export const createCallerFactory = t.createCallerFactory 61 | 62 | /** 63 | * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) 64 | * 65 | * These are the pieces you use to build your tRPC API. You should import these a lot in the 66 | * "/src/server/api/routers" directory. 67 | */ 68 | 69 | /** 70 | * This is how you create new routers and sub-routers in your tRPC API. 71 | * 72 | * @see https://trpc.io/docs/router 73 | */ 74 | export const createTRPCRouter = t.router 75 | 76 | /** 77 | * Middleware for timing procedure execution and adding an artificial delay in development. 78 | * 79 | * You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating 80 | * network latency that would occur in production but not in local development. 81 | */ 82 | const timingMiddleware = t.middleware(async ({ next, path }) => { 83 | const start = Date.now() 84 | 85 | if (t._config.isDev) { 86 | // artificial delay in dev 87 | const waitMs = Math.floor(Math.random() * 400) + 100 88 | await new Promise((resolve) => setTimeout(resolve, waitMs)) 89 | } 90 | 91 | const result = await next() 92 | 93 | const end = Date.now() 94 | console.log(`[TRPC] ${path} took ${end - start}ms to execute`) 95 | 96 | return result 97 | }) 98 | 99 | /** 100 | * Public (unauthenticated) procedure 101 | * 102 | * This is the base piece you use to build new queries and mutations on your tRPC API. It does not 103 | * guarantee that a user querying is authorized, but you can still access user session data if they 104 | * are logged in. 105 | */ 106 | export const publicProcedure = t.procedure.use(timingMiddleware) 107 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'tw-animate-css'; 3 | 4 | @custom-variant dark (&:is(.dark *)); 5 | 6 | @theme { 7 | --font-sans: 8 | var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif, 9 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 10 | } 11 | 12 | @theme inline { 13 | --radius-sm: calc(var(--radius) - 4px); 14 | --radius-md: calc(var(--radius) - 2px); 15 | --radius-lg: var(--radius); 16 | --radius-xl: calc(var(--radius) + 4px); 17 | --color-background: var(--background); 18 | --color-foreground: var(--foreground); 19 | --color-card: var(--card); 20 | --color-card-foreground: var(--card-foreground); 21 | --color-popover: var(--popover); 22 | --color-popover-foreground: var(--popover-foreground); 23 | --color-primary: var(--primary); 24 | --color-primary-foreground: var(--primary-foreground); 25 | --color-secondary: var(--secondary); 26 | --color-secondary-foreground: var(--secondary-foreground); 27 | --color-muted: var(--muted); 28 | --color-muted-foreground: var(--muted-foreground); 29 | --color-accent: var(--accent); 30 | --color-accent-foreground: var(--accent-foreground); 31 | --color-destructive: var(--destructive); 32 | --color-border: var(--border); 33 | --color-input: var(--input); 34 | --color-ring: var(--ring); 35 | --color-chart-1: var(--chart-1); 36 | --color-chart-2: var(--chart-2); 37 | --color-chart-3: var(--chart-3); 38 | --color-chart-4: var(--chart-4); 39 | --color-chart-5: var(--chart-5); 40 | --color-sidebar: var(--sidebar); 41 | --color-sidebar-foreground: var(--sidebar-foreground); 42 | --color-sidebar-primary: var(--sidebar-primary); 43 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); 44 | --color-sidebar-accent: var(--sidebar-accent); 45 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); 46 | --color-sidebar-border: var(--sidebar-border); 47 | --color-sidebar-ring: var(--sidebar-ring); 48 | } 49 | 50 | :root { 51 | --radius: 0.625rem; 52 | --card: oklch(1 0 0); 53 | --card-foreground: oklch(0.145 0 0); 54 | --popover: oklch(1 0 0); 55 | --popover-foreground: oklch(0.145 0 0); 56 | --primary: oklch(0.205 0 0); 57 | --primary-foreground: oklch(0.985 0 0); 58 | --secondary: oklch(0.97 0 0); 59 | --secondary-foreground: oklch(0.205 0 0); 60 | --muted: oklch(0.97 0 0); 61 | --muted-foreground: oklch(0.556 0 0); 62 | --accent: oklch(0.97 0 0); 63 | --accent-foreground: oklch(0.205 0 0); 64 | --destructive: oklch(0.577 0.245 27.325); 65 | --border: oklch(0.922 0 0); 66 | --input: oklch(0.922 0 0); 67 | --ring: oklch(0.708 0 0); 68 | --chart-1: oklch(0.646 0.222 41.116); 69 | --chart-2: oklch(0.6 0.118 184.704); 70 | --chart-3: oklch(0.398 0.07 227.392); 71 | --chart-4: oklch(0.828 0.189 84.429); 72 | --chart-5: oklch(0.769 0.188 70.08); 73 | --sidebar: oklch(0.985 0 0); 74 | --sidebar-foreground: oklch(0.145 0 0); 75 | --sidebar-primary: oklch(0.205 0 0); 76 | --sidebar-primary-foreground: oklch(0.985 0 0); 77 | --sidebar-accent: oklch(0.97 0 0); 78 | --sidebar-accent-foreground: oklch(0.205 0 0); 79 | --sidebar-border: oklch(0.922 0 0); 80 | --sidebar-ring: oklch(0.708 0 0); 81 | --background: oklch(1 0 0); 82 | --foreground: oklch(0.145 0 0); 83 | } 84 | 85 | .dark { 86 | --background: oklch(0.145 0 0); 87 | --foreground: oklch(0.985 0 0); 88 | --card: oklch(0.205 0 0); 89 | --card-foreground: oklch(0.985 0 0); 90 | --popover: oklch(0.205 0 0); 91 | --popover-foreground: oklch(0.985 0 0); 92 | --primary: oklch(0.922 0 0); 93 | --primary-foreground: oklch(0.205 0 0); 94 | --secondary: oklch(0.269 0 0); 95 | --secondary-foreground: oklch(0.985 0 0); 96 | --muted: oklch(0.269 0 0); 97 | --muted-foreground: oklch(0.708 0 0); 98 | --accent: oklch(0.269 0 0); 99 | --accent-foreground: oklch(0.985 0 0); 100 | --destructive: oklch(0.704 0.191 22.216); 101 | --border: oklch(1 0 0 / 10%); 102 | --input: oklch(1 0 0 / 15%); 103 | --ring: oklch(0.556 0 0); 104 | --chart-1: oklch(0.488 0.243 264.376); 105 | --chart-2: oklch(0.696 0.17 162.48); 106 | --chart-3: oklch(0.769 0.188 70.08); 107 | --chart-4: oklch(0.627 0.265 303.9); 108 | --chart-5: oklch(0.645 0.246 16.439); 109 | --sidebar: oklch(0.205 0 0); 110 | --sidebar-foreground: oklch(0.985 0 0); 111 | --sidebar-primary: oklch(0.488 0.243 264.376); 112 | --sidebar-primary-foreground: oklch(0.985 0 0); 113 | --sidebar-accent: oklch(0.269 0 0); 114 | --sidebar-accent-foreground: oklch(0.985 0 0); 115 | --sidebar-border: oklch(1 0 0 / 10%); 116 | --sidebar-ring: oklch(0.556 0 0); 117 | } 118 | 119 | @layer base { 120 | * { 121 | @apply border-border outline-ring/50; 122 | } 123 | body { 124 | @apply bg-background text-foreground; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/components/auth/login-form.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { zodResolver } from '@hookform/resolvers/zod' 4 | import { Eye, EyeOff } from 'lucide-react' 5 | import { useRouter } from 'next/navigation' 6 | import { useTranslations } from 'next-intl' 7 | import { useState } from 'react' 8 | import { useForm } from 'react-hook-form' 9 | import { z } from 'zod' 10 | import { Button } from '~/components/ui/button' 11 | import { Card, CardContent } from '~/components/ui/card' 12 | import { Input } from '~/components/ui/input' 13 | import { Label } from '~/components/ui/label' 14 | import { authClient } from '~/lib/auth-client' 15 | 16 | const loginSchema = z.object({ 17 | email: z.string().email('emailInvalid'), 18 | password: z.string().min(1, 'passwordRequired'), 19 | }) 20 | 21 | type LoginFormData = z.infer 22 | 23 | export function LoginForm() { 24 | const router = useRouter() 25 | const t = useTranslations('Auth') 26 | const [showPassword, setShowPassword] = useState(false) 27 | const [isLoading, setIsLoading] = useState(false) 28 | 29 | const { 30 | register, 31 | handleSubmit, 32 | setError, 33 | formState: { errors }, 34 | } = useForm({ 35 | resolver: zodResolver(loginSchema), 36 | }) 37 | 38 | const onSubmit = async (data: LoginFormData) => { 39 | setIsLoading(true) 40 | 41 | try { 42 | const { data: signInData, error } = await authClient.signIn.email({ 43 | email: data.email, 44 | password: data.password, 45 | }) 46 | 47 | if (error) { 48 | setError('root', { 49 | message: t('loginError'), 50 | }) 51 | return 52 | } 53 | 54 | if (signInData) { 55 | router.push('/') 56 | router.refresh() 57 | } 58 | } catch (_err) { 59 | setError('root', { 60 | message: t('networkError'), 61 | }) 62 | } finally { 63 | setIsLoading(false) 64 | } 65 | } 66 | 67 | return ( 68 | 69 | 70 |
71 |
72 |
73 | 79 | 87 | {errors.email && ( 88 |

89 | {t(errors.email.message!)} 90 |

91 | )} 92 |
93 | 94 |
95 | 101 |
102 | 110 | 118 |
119 | {errors.password && ( 120 |

121 | {t(errors.password.message!)} 122 |

123 | )} 124 |
125 |
126 | 127 | {errors.root && ( 128 |

129 | {errors.root.message} 130 |

131 | )} 132 | 133 | 140 |
141 |
142 |
143 | ) 144 | } 145 | -------------------------------------------------------------------------------- /src/components/auth/register-form.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { zodResolver } from '@hookform/resolvers/zod' 4 | import { Eye, EyeOff } from 'lucide-react' 5 | import { useRouter } from 'next/navigation' 6 | import { useTranslations } from 'next-intl' 7 | import { useState } from 'react' 8 | import { useForm } from 'react-hook-form' 9 | import { z } from 'zod' 10 | import { Button } from '~/components/ui/button' 11 | import { Card, CardContent } from '~/components/ui/card' 12 | import { Input } from '~/components/ui/input' 13 | import { Label } from '~/components/ui/label' 14 | import { authClient } from '~/lib/auth-client' 15 | import { cn } from '~/lib/utils' 16 | 17 | const registerSchema = z.object({ 18 | name: z.string().min(2, 'usernameRequired').max(50, 'usernameRequired'), 19 | email: z.string().email('emailInvalid'), 20 | password: z 21 | .string() 22 | .min(8, 'passwordRequirements.length') 23 | .regex(/[A-Z]/, 'passwordRequirements.uppercase') 24 | .regex(/[a-z]/, 'passwordRequirements.lowercase') 25 | .regex(/[0-9]/, 'passwordRequirements.number'), 26 | }) 27 | 28 | type RegisterFormData = z.infer 29 | 30 | export function RegisterForm({ 31 | className, 32 | ...props 33 | }: React.ComponentProps<'div'>) { 34 | const router = useRouter() 35 | const t = useTranslations('Auth') 36 | const [showPassword, setShowPassword] = useState(false) 37 | const [isLoading, setIsLoading] = useState(false) 38 | 39 | const { 40 | register, 41 | handleSubmit, 42 | setError, 43 | formState: { errors }, 44 | } = useForm({ 45 | resolver: zodResolver(registerSchema), 46 | }) 47 | 48 | const onSubmit = async (data: RegisterFormData) => { 49 | setIsLoading(true) 50 | 51 | try { 52 | const { data: signUpData, error } = await authClient.signUp.email({ 53 | name: data.name, 54 | email: data.email, 55 | password: data.password, 56 | }) 57 | 58 | if (error) { 59 | setError('root', { 60 | message: error.message || t('registerError'), 61 | }) 62 | return 63 | } 64 | 65 | if (signUpData) { 66 | // 注册成功后自动登录 67 | const { error: signInError } = await authClient.signIn.email({ 68 | email: data.email, 69 | password: data.password, 70 | }) 71 | 72 | if (!signInError) { 73 | router.push('/') 74 | router.refresh() 75 | } else { 76 | setError('root', { 77 | message: t('autoLoginFailed'), 78 | }) 79 | } 80 | } 81 | } catch (_err) { 82 | setError('root', { 83 | message: t('networkError'), 84 | }) 85 | } finally { 86 | setIsLoading(false) 87 | } 88 | } 89 | 90 | return ( 91 |
95 | 96 | 97 |
98 |
99 |
100 | 106 | 114 | {errors.name && ( 115 |

116 | {t(errors.name.message!)} 117 |

118 | )} 119 |
120 | 121 |
122 | 128 | 136 | {errors.email && ( 137 |

138 | {t(errors.email.message!)} 139 |

140 | )} 141 |
142 | 143 |
144 | 150 |
151 | 159 | 167 |
168 | {errors.password && ( 169 |

170 | {t(errors.password.message!)} 171 |

172 | )} 173 |
174 | 175 | {errors.root && ( 176 |

177 | {errors.root.message} 178 |

179 | )} 180 | 181 | 188 |
189 |
190 |
191 |
192 |
193 | ) 194 | } 195 | -------------------------------------------------------------------------------- /src/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as SelectPrimitive from "@radix-ui/react-select" 4 | import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" 5 | import * as React from "react" 6 | 7 | import { cn } from "~/lib/utils" 8 | 9 | function Select({ 10 | ...props 11 | }: React.ComponentProps) { 12 | return 13 | } 14 | 15 | function SelectGroup({ 16 | ...props 17 | }: React.ComponentProps) { 18 | return 19 | } 20 | 21 | function SelectValue({ 22 | ...props 23 | }: React.ComponentProps) { 24 | return 25 | } 26 | 27 | function SelectTrigger({ 28 | className, 29 | size = "default", 30 | children, 31 | ...props 32 | }: React.ComponentProps & { 33 | size?: "sm" | "default" 34 | }) { 35 | return ( 36 | 45 | {children} 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 | function SelectContent({ 54 | className, 55 | children, 56 | position = "popper", 57 | ...props 58 | }: React.ComponentProps) { 59 | return ( 60 | 61 | 72 | 73 | 80 | {children} 81 | 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | function SelectLabel({ 89 | className, 90 | ...props 91 | }: React.ComponentProps) { 92 | return ( 93 | 98 | ) 99 | } 100 | 101 | function SelectItem({ 102 | className, 103 | children, 104 | ...props 105 | }: React.ComponentProps) { 106 | return ( 107 | 115 | 116 | 117 | 118 | 119 | 120 | {children} 121 | 122 | ) 123 | } 124 | 125 | function SelectSeparator({ 126 | className, 127 | ...props 128 | }: React.ComponentProps) { 129 | return ( 130 | 135 | ) 136 | } 137 | 138 | function SelectScrollUpButton({ 139 | className, 140 | ...props 141 | }: React.ComponentProps) { 142 | return ( 143 | 151 | 152 | 153 | ) 154 | } 155 | 156 | function SelectScrollDownButton({ 157 | className, 158 | ...props 159 | }: React.ComponentProps) { 160 | return ( 161 | 169 | 170 | 171 | ) 172 | } 173 | 174 | export { 175 | Select, 176 | SelectContent, 177 | SelectGroup, 178 | SelectItem, 179 | SelectLabel, 180 | SelectScrollDownButton, 181 | SelectScrollUpButton, 182 | SelectSeparator, 183 | SelectTrigger, 184 | SelectValue, 185 | } 186 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "nextjs-starter-template", 6 | "dependencies": { 7 | "@hookform/resolvers": "latest", 8 | "@prisma/client": "latest", 9 | "@radix-ui/react-label": "latest", 10 | "@radix-ui/react-select": "latest", 11 | "@radix-ui/react-slot": "latest", 12 | "@t3-oss/env-nextjs": "latest", 13 | "@tanstack/react-query": "latest", 14 | "@trpc/client": "latest", 15 | "@trpc/react-query": "latest", 16 | "@trpc/server": "latest", 17 | "better-auth": "latest", 18 | "class-variance-authority": "latest", 19 | "clsx": "latest", 20 | "lefthook": "latest", 21 | "lucide-react": "latest", 22 | "next": "latest", 23 | "next-intl": "latest", 24 | "next-themes": "latest", 25 | "nextjs-toploader": "latest", 26 | "react": "latest", 27 | "react-dom": "latest", 28 | "react-hook-form": "latest", 29 | "react-toastify": "latest", 30 | "server-only": "latest", 31 | "superjson": "latest", 32 | "tailwind-merge": "latest", 33 | "zod": "latest", 34 | "zustand": "latest", 35 | }, 36 | "devDependencies": { 37 | "@biomejs/biome": "latest", 38 | "@tailwindcss/postcss": "latest", 39 | "@types/node": "latest", 40 | "@types/react": "latest", 41 | "@types/react-dom": "latest", 42 | "postcss": "latest", 43 | "prisma": "latest", 44 | "tailwindcss": "latest", 45 | "tw-animate-css": "latest", 46 | "typescript": "latest", 47 | }, 48 | }, 49 | }, 50 | "packages": { 51 | "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 52 | 53 | "@better-auth/utils": ["@better-auth/utils@0.2.6", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA=="], 54 | 55 | "@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="], 56 | 57 | "@biomejs/biome": ["@biomejs/biome@2.2.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.2", "@biomejs/cli-darwin-x64": "2.2.2", "@biomejs/cli-linux-arm64": "2.2.2", "@biomejs/cli-linux-arm64-musl": "2.2.2", "@biomejs/cli-linux-x64": "2.2.2", "@biomejs/cli-linux-x64-musl": "2.2.2", "@biomejs/cli-win32-arm64": "2.2.2", "@biomejs/cli-win32-x64": "2.2.2" }, "bin": { "biome": "bin/biome" } }, "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w=="], 58 | 59 | "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ=="], 60 | 61 | "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag=="], 62 | 63 | "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw=="], 64 | 65 | "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw=="], 66 | 67 | "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ=="], 68 | 69 | "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig=="], 70 | 71 | "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ=="], 72 | 73 | "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg=="], 74 | 75 | "@emnapi/runtime": ["@emnapi/runtime@1.4.5", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.4.5.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], 76 | 77 | "@floating-ui/core": ["@floating-ui/core@1.7.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA=="], 78 | 79 | "@floating-ui/dom": ["@floating-ui/dom@1.7.0", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg=="], 80 | 81 | "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], 82 | 83 | "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], 84 | 85 | "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.4", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.1", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA=="], 86 | 87 | "@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.7", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ=="], 88 | 89 | "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.2", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/icu-skeleton-parser": "1.8.14", "tslib": "^2.8.0" } }, "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA=="], 90 | 91 | "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.14", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "tslib": "^2.8.0" } }, "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ=="], 92 | 93 | "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="], 94 | 95 | "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], 96 | 97 | "@hookform/resolvers": ["@hookform/resolvers@5.2.1", "https://registry.npmmirror.com/@hookform/resolvers/-/resolvers-5.2.1.tgz", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-u0+6X58gkjMcxur1wRWokA7XsiiBJ6aK17aPZxhkoYiK5J+HcTx0Vhu9ovXe6H+dVpO6cjrn2FkJTryXEMlryQ=="], 98 | 99 | "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="], 100 | 101 | "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="], 102 | 103 | "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="], 104 | 105 | "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="], 106 | 107 | "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="], 108 | 109 | "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="], 110 | 111 | "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="], 112 | 113 | "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="], 114 | 115 | "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="], 116 | 117 | "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="], 118 | 119 | "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="], 120 | 121 | "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="], 122 | 123 | "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="], 124 | 125 | "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "https://registry.npmmirror.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="], 126 | 127 | "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="], 128 | 129 | "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="], 130 | 131 | "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="], 132 | 133 | "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="], 134 | 135 | "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="], 136 | 137 | "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="], 138 | 139 | "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="], 140 | 141 | "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="], 142 | 143 | "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 144 | 145 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], 146 | 147 | "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 148 | 149 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 150 | 151 | "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 152 | 153 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 154 | 155 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 156 | 157 | "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], 158 | 159 | "@next/env": ["@next/env@15.5.2", "", {}, "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg=="], 160 | 161 | "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ=="], 162 | 163 | "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ=="], 164 | 165 | "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA=="], 166 | 167 | "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g=="], 168 | 169 | "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.2", "", { "os": "linux", "cpu": "x64" }, "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q=="], 170 | 171 | "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g=="], 172 | 173 | "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg=="], 174 | 175 | "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.2", "", { "os": "win32", "cpu": "x64" }, "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q=="], 176 | 177 | "@noble/ciphers": ["@noble/ciphers@0.6.0", "", {}, "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ=="], 178 | 179 | "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], 180 | 181 | "@peculiar/asn1-android": ["@peculiar/asn1-android@2.3.16", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw=="], 182 | 183 | "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA=="], 184 | 185 | "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg=="], 186 | 187 | "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.3.15", "", { "dependencies": { "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w=="], 188 | 189 | "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg=="], 190 | 191 | "@prisma/client": ["@prisma/client@6.14.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-8E/Nk3eL5g7RQIg/LUj1ICyDmhD053STjxrPxUtCRybs2s/2sOEcx9NpITuAOPn07HEpWBfhAVe1T/HYWXUPOw=="], 192 | 193 | "@prisma/config": ["@prisma/config@6.14.0", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.16.12", "empathic": "2.0.0" } }, "sha512-IwC7o5KNNGhmblLs23swnfBjADkacBb7wvyDXUWLwuvUQciKJZqyecU0jw0d7JRkswrj+XTL8fdr0y2/VerKQQ=="], 194 | 195 | "@prisma/debug": ["@prisma/debug@6.14.0", "", {}, "sha512-j4Lf+y+5QIJgQD4sJWSbkOD7geKx9CakaLp/TyTy/UDu9Wo0awvWCBH/BAxTHUaCpIl9USA5VS/KJhDqKJSwug=="], 196 | 197 | "@prisma/engines": ["@prisma/engines@6.14.0", "", { "dependencies": { "@prisma/debug": "6.14.0", "@prisma/engines-version": "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49", "@prisma/fetch-engine": "6.14.0", "@prisma/get-platform": "6.14.0" } }, "sha512-LhJjqsALFEcoAtF07nSaOkVguaxw/ZsgfROIYZ8bAZDobe7y8Wy+PkYQaPOK1iLSsFgV2MhCO/eNrI1gdSOj6w=="], 198 | 199 | "@prisma/engines-version": ["@prisma/engines-version@6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49", "", {}, "sha512-EgN9ODJpiX45yvwcngoStp3uQPJ3l+AEVoQ6dMMO2QvmwIlnxfApzKmJQExzdo7/hqQANrz5txHJdGYHzOnGHA=="], 200 | 201 | "@prisma/fetch-engine": ["@prisma/fetch-engine@6.14.0", "", { "dependencies": { "@prisma/debug": "6.14.0", "@prisma/engines-version": "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49", "@prisma/get-platform": "6.14.0" } }, "sha512-MPzYPOKMENYOaY3AcAbaKrfvXVlvTc6iHmTXsp9RiwCX+bPyfDMqMFVUSVXPYrXnrvEzhGHfyiFy0PRLHPysNg=="], 202 | 203 | "@prisma/get-platform": ["@prisma/get-platform@6.14.0", "", { "dependencies": { "@prisma/debug": "6.14.0" } }, "sha512-7VjuxKNwjnBhKfqPpMeWiHEa2sVjYzmHdl1slW6STuUCe9QnOY0OY1ljGSvz6wpG4U8DfbDqkG1yofd/1GINww=="], 204 | 205 | "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], 206 | 207 | "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], 208 | 209 | "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], 210 | 211 | "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], 212 | 213 | "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], 214 | 215 | "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], 216 | 217 | "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], 218 | 219 | "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], 220 | 221 | "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], 222 | 223 | "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], 224 | 225 | "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], 226 | 227 | "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], 228 | 229 | "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], 230 | 231 | "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], 232 | 233 | "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], 234 | 235 | "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], 236 | 237 | "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], 238 | 239 | "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], 240 | 241 | "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], 242 | 243 | "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], 244 | 245 | "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], 246 | 247 | "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], 248 | 249 | "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], 250 | 251 | "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], 252 | 253 | "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], 254 | 255 | "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], 256 | 257 | "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], 258 | 259 | "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="], 260 | 261 | "@simplewebauthn/browser": ["@simplewebauthn/browser@13.1.2", "", {}, "sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw=="], 262 | 263 | "@simplewebauthn/server": ["@simplewebauthn/server@13.1.2", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8" } }, "sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g=="], 264 | 265 | "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], 266 | 267 | "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], 268 | 269 | "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], 270 | 271 | "@t3-oss/env-core": ["@t3-oss/env-core@0.13.8", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw=="], 272 | 273 | "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.8", "", { "dependencies": { "@t3-oss/env-core": "0.13.8" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-QmTLnsdQJ8BiQad2W2nvV6oUpH4oMZMqnFEjhVpzU0h3sI9hn8zb8crjWJ1Amq453mGZs6A4v4ihIeBFDOrLeQ=="], 274 | 275 | "@tailwindcss/node": ["@tailwindcss/node@4.1.12", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.12" } }, "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ=="], 276 | 277 | "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.12", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.12", "@tailwindcss/oxide-darwin-arm64": "4.1.12", "@tailwindcss/oxide-darwin-x64": "4.1.12", "@tailwindcss/oxide-freebsd-x64": "4.1.12", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", "@tailwindcss/oxide-linux-x64-musl": "4.1.12", "@tailwindcss/oxide-wasm32-wasi": "4.1.12", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" } }, "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw=="], 278 | 279 | "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.12", "", { "os": "android", "cpu": "arm64" }, "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ=="], 280 | 281 | "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw=="], 282 | 283 | "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg=="], 284 | 285 | "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww=="], 286 | 287 | "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12", "", { "os": "linux", "cpu": "arm" }, "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ=="], 288 | 289 | "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g=="], 290 | 291 | "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA=="], 292 | 293 | "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.12", "", { "os": "linux", "cpu": "x64" }, "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q=="], 294 | 295 | "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.12", "", { "os": "linux", "cpu": "x64" }, "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A=="], 296 | 297 | "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.12", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@emnapi/wasi-threads": "^1.0.4", "@napi-rs/wasm-runtime": "^0.2.12", "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg=="], 298 | 299 | "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg=="], 300 | 301 | "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.12", "", { "os": "win32", "cpu": "x64" }, "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA=="], 302 | 303 | "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.12", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.12", "@tailwindcss/oxide": "4.1.12", "postcss": "^8.4.41", "tailwindcss": "4.1.12" } }, "sha512-5PpLYhCAwf9SJEeIsSmCDLgyVfdBhdBpzX1OJ87anT9IVR0Z9pjM0FNixCAUAHGnMBGB8K99SwAheXrT0Kh6QQ=="], 304 | 305 | "@tanstack/query-core": ["@tanstack/query-core@5.85.5", "", {}, "sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w=="], 306 | 307 | "@tanstack/react-query": ["@tanstack/react-query@5.85.5", "", { "dependencies": { "@tanstack/query-core": "5.85.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-/X4EFNcnPiSs8wM2v+b6DqS5mmGeuJQvxBglmDxl6ZQb5V26ouD2SJYAcC3VjbNwqhY2zjxVD15rDA5nGbMn3A=="], 308 | 309 | "@trpc/client": ["@trpc/client@11.5.0", "", { "peerDependencies": { "@trpc/server": "11.5.0", "typescript": ">=5.7.2" } }, "sha512-32oH+KOAdo73jJKjU9tyG+vCjID6A28NgXwUNr691O5HjpF5yyTX51Zzyee8YtGzU89Nw/drCHdfA4gD7BN2eg=="], 310 | 311 | "@trpc/react-query": ["@trpc/react-query@11.5.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.5.0", "@trpc/server": "11.5.0", "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-BPrMbYi5/oW11SSRUmD3dONCAG/JJjxfniSyFbsY5VSS6qHXDF1RcfLAkiF+Ofo7MPchqmHc2iNqSl5Eumh8iA=="], 312 | 313 | "@trpc/server": ["@trpc/server@11.5.0", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-0IBtkmUCeO2ycn4K45/cqsujnlCQrSvkCo7lFDpg3kGMIPiLyLRciID5IiS7prEjRjeITa+od2aaHTIwONApVw=="], 314 | 315 | "@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], 316 | 317 | "@types/react": ["@types/react@19.1.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ=="], 318 | 319 | "@types/react-dom": ["@types/react-dom@19.1.8", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ=="], 320 | 321 | "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="], 322 | 323 | "asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="], 324 | 325 | "better-auth": ["better-auth@1.3.7", "", { "dependencies": { "@better-auth/utils": "0.2.6", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.8.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "^1.0.13", "defu": "^6.1.4", "jose": "^5.10.0", "kysely": "^0.28.5", "nanostores": "^0.11.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-/1fEyx2SGgJQM5ujozDCh9eJksnVkNU/J7Fk/tG5Y390l8nKbrPvqiFlCjlMM+scR+UABJbQzA6An7HT50LHyQ=="], 326 | 327 | "better-call": ["better-call@1.0.15", "", { "dependencies": { "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-u4ZNRB1yBx5j3CltTEbY2ZoFPVcgsuvciAqTEmPvnZpZ483vlZf4LGJ5aVau1yMlrvlyHxOCica3OqXBLhmsUw=="], 328 | 329 | "c12": ["c12@3.1.0", "https://registry.npmmirror.com/c12/-/c12-3.1.0.tgz", { "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" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], 330 | 331 | "caniuse-lite": ["caniuse-lite@1.0.30001718", "", {}, "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw=="], 332 | 333 | "chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 334 | 335 | "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], 336 | 337 | "citty": ["citty@0.1.6", "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], 338 | 339 | "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], 340 | 341 | "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], 342 | 343 | "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 344 | 345 | "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], 346 | 347 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 348 | 349 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 350 | 351 | "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], 352 | 353 | "confbox": ["confbox@0.2.2", "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], 354 | 355 | "consola": ["consola@3.4.2", "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], 356 | 357 | "copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], 358 | 359 | "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], 360 | 361 | "decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="], 362 | 363 | "deepmerge-ts": ["deepmerge-ts@7.1.5", "https://registry.npmmirror.com/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="], 364 | 365 | "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], 366 | 367 | "destr": ["destr@2.0.5", "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], 368 | 369 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 370 | 371 | "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], 372 | 373 | "dotenv": ["dotenv@16.6.1", "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], 374 | 375 | "effect": ["effect@3.16.12", "https://registry.npmmirror.com/effect/-/effect-3.16.12.tgz", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg=="], 376 | 377 | "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], 378 | 379 | "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], 380 | 381 | "exsolve": ["exsolve@1.0.7", "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], 382 | 383 | "fast-check": ["fast-check@3.23.2", "https://registry.npmmirror.com/fast-check/-/fast-check-3.23.2.tgz", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], 384 | 385 | "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], 386 | 387 | "giget": ["giget@2.0.0", "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz", { "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" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], 388 | 389 | "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 390 | 391 | "intl-messageformat": ["intl-messageformat@10.7.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.2", "tslib": "^2.8.0" } }, "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug=="], 392 | 393 | "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], 394 | 395 | "is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="], 396 | 397 | "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], 398 | 399 | "jose": ["jose@5.10.0", "https://registry.npmmirror.com/jose/-/jose-5.10.0.tgz", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], 400 | 401 | "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 402 | 403 | "kysely": ["kysely@0.28.5", "", {}, "sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA=="], 404 | 405 | "lefthook": ["lefthook@1.12.3", "", { "optionalDependencies": { "lefthook-darwin-arm64": "1.12.3", "lefthook-darwin-x64": "1.12.3", "lefthook-freebsd-arm64": "1.12.3", "lefthook-freebsd-x64": "1.12.3", "lefthook-linux-arm64": "1.12.3", "lefthook-linux-x64": "1.12.3", "lefthook-openbsd-arm64": "1.12.3", "lefthook-openbsd-x64": "1.12.3", "lefthook-windows-arm64": "1.12.3", "lefthook-windows-x64": "1.12.3" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-huMg+mGp6wHPjkaLdchuOvxVRMzvz6OVdhivatiH2Qn47O5Zm46jwzbVPYIanX6N/8ZTjGLBxv8tZ0KYmKt/Jg=="], 406 | 407 | "lefthook-darwin-arm64": ["lefthook-darwin-arm64@1.12.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-j1lwaosWRy3vhz8oQgCS1M6EUFN95aIYeNuqkczsBoAA6BDNAmVP1ctYEIYUK4bYaIgENbqbA9prYMAhyzh6Og=="], 408 | 409 | "lefthook-darwin-x64": ["lefthook-darwin-x64@1.12.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-x6aWFfLQX4m5zQ4X9zh5+hHOE5XTvNjz2zB9DI+xbIBLs2RRg0xJNT3OfgSrBU1QtEBneJ5dRQP5nl47td9GDQ=="], 410 | 411 | "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@1.12.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-41OmulLqVZ0EOHmmHouJrpL59SwDD7FLoso4RsQVIBPaf8fHacdLo07Ye28VWQ5XolZQvnWcr1YXKo4JhqQMyw=="], 412 | 413 | "lefthook-freebsd-x64": ["lefthook-freebsd-x64@1.12.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-741/JRCJIS++hgYEH2uefN4FsH872V7gy2zDhcfQofiZnWP7+qhl4Wmwi8IpjIu4X7hLOC4cT18LOVU5L8KV9Q=="], 414 | 415 | "lefthook-linux-arm64": ["lefthook-linux-arm64@1.12.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-BXIy1aDFZmFgmebJliNrEqZfX1lSOD4b/USvANv1UirFrNgTq5SRssd1CKfflT2PwKX6LsJTD4WabLLWZOxp9A=="], 416 | 417 | "lefthook-linux-x64": ["lefthook-linux-x64@1.12.3", "", { "os": "linux", "cpu": "x64" }, "sha512-FRdwdj5jsQAP2eVrtkVUqMqYNCbQ2Ix84izy29/BvLlu/hVypAGbDfUkgFnsmAd6ZsCBeYCEtPuqyg3E3SO0Rg=="], 418 | 419 | "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@1.12.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-tch5wXY4GOjKAYohH7OFoxNdVHuUSYt2Pulo2VTkMYEG8IrvJnRO5MkvgHtKDHzU5mfABQYv5+ccJykDx5hQWA=="], 420 | 421 | "lefthook-openbsd-x64": ["lefthook-openbsd-x64@1.12.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-IHbHg/rUFXrAN7LnjcQEtutCHBaD49CZge96Hpk0GZ2eEG5GTCNRnUyEf+Kf3+RTqHFgwtADdpeDa/ZaGZTM4g=="], 422 | 423 | "lefthook-windows-arm64": ["lefthook-windows-arm64@1.12.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-wghcE5TSpb+mbtemUV6uAo9hEK09kxRzhf2nPdeDX+fw42cL2TGZsbaCnDyzaY144C+L2/wEWrLIHJMnZYkuqA=="], 424 | 425 | "lefthook-windows-x64": ["lefthook-windows-x64@1.12.3", "", { "os": "win32", "cpu": "x64" }, "sha512-7Co/L8e2x2hGC1L33jDJ4ZlTkO3PJm25GOGpLfN1kqwhGB/uzMLeTI/PBczjlIN8isUv26ouNd9rVR7Bibrwyg=="], 426 | 427 | "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], 428 | 429 | "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], 430 | 431 | "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], 432 | 433 | "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], 434 | 435 | "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], 436 | 437 | "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], 438 | 439 | "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], 440 | 441 | "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], 442 | 443 | "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], 444 | 445 | "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], 446 | 447 | "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], 448 | 449 | "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], 450 | 451 | "lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="], 452 | 453 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 454 | 455 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 456 | 457 | "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], 458 | 459 | "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], 460 | 461 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 462 | 463 | "nanostores": ["nanostores@0.11.4", "", {}, "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ=="], 464 | 465 | "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], 466 | 467 | "next": ["next@15.5.2", "", { "dependencies": { "@next/env": "15.5.2", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.2", "@next/swc-darwin-x64": "15.5.2", "@next/swc-linux-arm64-gnu": "15.5.2", "@next/swc-linux-arm64-musl": "15.5.2", "@next/swc-linux-x64-gnu": "15.5.2", "@next/swc-linux-x64-musl": "15.5.2", "@next/swc-win32-arm64-msvc": "15.5.2", "@next/swc-win32-x64-msvc": "15.5.2", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q=="], 468 | 469 | "next-intl": ["next-intl@4.3.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^4.3.5" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-tT3SltfpPOCAQ9kVNr+8t6FUtVf8G0WFlJcVc8zj4WCMfuF8XFk4gZCN/MtjgDgkUISw5aKamOClJB4EsV95WQ=="], 470 | 471 | "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], 472 | 473 | "nextjs-toploader": ["nextjs-toploader@3.8.16", "", { "dependencies": { "nprogress": "^0.2.0", "prop-types": "^15.8.1" }, "peerDependencies": { "next": ">= 6.0.0", "react": ">= 16.0.0", "react-dom": ">= 16.0.0" } }, "sha512-xiejFF9OQD8ovvHfrFhnEmRytvZtwIOY/mMtI9punOAACXpYpgC0y1afJ4DSIEmUi4Syy9A5BsFpUORTJ9z8Ng=="], 474 | 475 | "node-fetch-native": ["node-fetch-native@1.6.7", "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], 476 | 477 | "nprogress": ["nprogress@0.2.0", "", {}, "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="], 478 | 479 | "nypm": ["nypm@0.6.1", "https://registry.npmmirror.com/nypm/-/nypm-0.6.1.tgz", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.2.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w=="], 480 | 481 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 482 | 483 | "ohash": ["ohash@2.0.11", "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], 484 | 485 | "pathe": ["pathe@2.0.3", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 486 | 487 | "perfect-debounce": ["perfect-debounce@1.0.0", "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], 488 | 489 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 490 | 491 | "pkg-types": ["pkg-types@2.2.0", "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.2.0.tgz", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ=="], 492 | 493 | "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 494 | 495 | "prisma": ["prisma@6.14.0", "", { "dependencies": { "@prisma/config": "6.14.0", "@prisma/engines": "6.14.0" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-QEuCwxu+Uq9BffFw7in8In+WfbSUN0ewnaSUKloLkbJd42w6EyFckux4M0f7VwwHlM3A8ssaz4OyniCXlsn0WA=="], 496 | 497 | "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], 498 | 499 | "pure-rand": ["pure-rand@6.1.0", "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], 500 | 501 | "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], 502 | 503 | "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], 504 | 505 | "rc9": ["rc9@2.1.2", "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], 506 | 507 | "react": ["react@19.1.1", "https://registry.npmmirror.com/react/-/react-19.1.1.tgz", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], 508 | 509 | "react-dom": ["react-dom@19.1.1", "https://registry.npmmirror.com/react-dom/-/react-dom-19.1.1.tgz", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], 510 | 511 | "react-hook-form": ["react-hook-form@7.62.0", "https://registry.npmmirror.com/react-hook-form/-/react-hook-form-7.62.0.tgz", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA=="], 512 | 513 | "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], 514 | 515 | "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], 516 | 517 | "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], 518 | 519 | "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], 520 | 521 | "react-toastify": ["react-toastify@11.0.5", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA=="], 522 | 523 | "readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 524 | 525 | "rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="], 526 | 527 | "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], 528 | 529 | "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 530 | 531 | "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], 532 | 533 | "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], 534 | 535 | "sharp": ["sharp@0.34.3", "https://registry.npmmirror.com/sharp/-/sharp-0.34.3.tgz", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], 536 | 537 | "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], 538 | 539 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 540 | 541 | "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], 542 | 543 | "superjson": ["superjson@2.2.2", "", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="], 544 | 545 | "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], 546 | 547 | "tailwindcss": ["tailwindcss@4.1.12", "", {}, "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA=="], 548 | 549 | "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="], 550 | 551 | "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], 552 | 553 | "tinyexec": ["tinyexec@1.0.1", "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.1.tgz", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], 554 | 555 | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 556 | 557 | "tw-animate-css": ["tw-animate-css@1.3.7", "", {}, "sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A=="], 558 | 559 | "typescript": ["typescript@5.9.2", "https://registry.npmmirror.com/typescript/-/typescript-5.9.2.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], 560 | 561 | "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], 562 | 563 | "undici-types": ["undici-types@7.10.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.10.0.tgz", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], 564 | 565 | "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], 566 | 567 | "use-intl": ["use-intl@4.3.5", "", { "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "@schummar/icu-type-parser": "1.21.5", "intl-messageformat": "^10.5.14" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-qyL1TZNesVbzj/75ZbYsi+xzNSiFqp5rIVsiAN0JT8rPMSjX0/3KQz76aJIrngI1/wIQdVYFVdImWh5yAv+dWA=="], 568 | 569 | "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], 570 | 571 | "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 572 | 573 | "zod": ["zod@4.1.3", "", {}, "sha512-1neef4bMce1hNTrxvHVKxWjKfGDn0oAli3Wy1Uwb7TRO1+wEwoZUZNP1NXIEESybOBiFnBOhI6a4m6tCLE8dog=="], 574 | 575 | "zustand": ["zustand@5.0.8", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="], 576 | 577 | "@formatjs/ecma402-abstract/@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg=="], 578 | 579 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" }, "bundled": true }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="], 580 | 581 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.5", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.4.5.tgz", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], 582 | 583 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="], 584 | 585 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], 586 | 587 | "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], 588 | 589 | "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 590 | 591 | "c12/jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], 592 | 593 | "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], 594 | } 595 | } 596 | --------------------------------------------------------------------------------