From 6060492169570f2c5b870a547c8abb22854715c3 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Thu, 1 Jan 2026 20:45:10 +1000 Subject: [PATCH] feat: add Better Auth with email/password authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add User, Session, Account, Verification models to Prisma schema - Create auth configuration with Prisma adapter - Add Resend email integration for verification emails - Create sign-in, sign-up, and verify-email pages - Create user dashboard with profile display - Add middleware for /dashboard route protection - Add better-auth and resend dependencies 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- apps/web/package.json | 2 + apps/web/src/app/(auth)/layout.tsx | 9 + apps/web/src/app/(auth)/sign-in/page.tsx | 100 ++++ apps/web/src/app/(auth)/sign-up/page.tsx | 118 ++++ apps/web/src/app/(auth)/verify-email/page.tsx | 39 ++ apps/web/src/app/api/auth/[...all]/route.ts | 4 + apps/web/src/app/dashboard/page.tsx | 52 ++ .../web/src/components/auth/SignOutButton.tsx | 33 ++ apps/web/src/env.ts | 7 + apps/web/src/lib/auth-client.ts | 7 + apps/web/src/lib/auth.ts | 25 + apps/web/src/lib/email.ts | 40 ++ apps/web/src/middleware.ts | 18 + packages/db/prisma/schema.prisma | 73 +++ pnpm-lock.yaml | 502 +++++++++++++++++- 15 files changed, 1018 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/app/(auth)/layout.tsx create mode 100644 apps/web/src/app/(auth)/sign-in/page.tsx create mode 100644 apps/web/src/app/(auth)/sign-up/page.tsx create mode 100644 apps/web/src/app/(auth)/verify-email/page.tsx create mode 100644 apps/web/src/app/api/auth/[...all]/route.ts create mode 100644 apps/web/src/app/dashboard/page.tsx create mode 100644 apps/web/src/components/auth/SignOutButton.tsx create mode 100644 apps/web/src/lib/auth-client.ts create mode 100644 apps/web/src/lib/auth.ts create mode 100644 apps/web/src/lib/email.ts create mode 100644 apps/web/src/middleware.ts diff --git a/apps/web/package.json b/apps/web/package.json index c5fa8be..a43c059 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -28,6 +28,7 @@ "@vercel/blob": "^2.0.0", "@vercel/kv": "^3.0.0", "ai": "6.0.3", + "better-auth": "^1.4.10", "bm25": "^0.1.1", "d3": "^7.9.0", "next": "^16.0.8", @@ -41,6 +42,7 @@ "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", + "resend": "^6.6.0", "zod": "^4.0.0" }, "devDependencies": { diff --git a/apps/web/src/app/(auth)/layout.tsx b/apps/web/src/app/(auth)/layout.tsx new file mode 100644 index 0000000..063f0ad --- /dev/null +++ b/apps/web/src/app/(auth)/layout.tsx @@ -0,0 +1,9 @@ +import type { ReactNode } from 'react'; + +export default function AuthLayout({ children }: { children: ReactNode }) { + return ( +
+
{children}
+
+ ); +} diff --git a/apps/web/src/app/(auth)/sign-in/page.tsx b/apps/web/src/app/(auth)/sign-in/page.tsx new file mode 100644 index 0000000..f510a23 --- /dev/null +++ b/apps/web/src/app/(auth)/sign-in/page.tsx @@ -0,0 +1,100 @@ +'use client'; + +import { signIn } from '@/lib/auth-client'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; + +export default function SignInPage() { + const router = useRouter(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(''); + setLoading(true); + + try { + const result = await signIn.email({ + email, + password, + }); + + if (result.error) { + setError(result.error.message || 'Failed to sign in'); + return; + } + + router.push('/dashboard'); + } catch { + setError('An unexpected error occurred'); + } finally { + setLoading(false); + } + } + + return ( +
+
+

Sign In

+

Welcome back to TPMJS

+
+ +
+ {error && ( +
+ {error} +
+ )} + +
+ + setEmail(e.target.value)} + required + className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-foreground focus:border-transparent" + placeholder="you@example.com" + /> +
+ +
+ + setPassword(e.target.value)} + required + className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-foreground focus:border-transparent" + placeholder="Enter your password" + /> +
+ + +
+ +

+ Don't have an account?{' '} + + Sign up + +

+
+ ); +} diff --git a/apps/web/src/app/(auth)/sign-up/page.tsx b/apps/web/src/app/(auth)/sign-up/page.tsx new file mode 100644 index 0000000..e6b0ee6 --- /dev/null +++ b/apps/web/src/app/(auth)/sign-up/page.tsx @@ -0,0 +1,118 @@ +'use client'; + +import { signUp } from '@/lib/auth-client'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; + +export default function SignUpPage() { + const router = useRouter(); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(''); + setLoading(true); + + try { + const result = await signUp.email({ + name, + email, + password, + }); + + if (result.error) { + setError(result.error.message || 'Failed to create account'); + return; + } + + router.push('/verify-email'); + } catch { + setError('An unexpected error occurred'); + } finally { + setLoading(false); + } + } + + return ( +
+
+

Create Account

+

Join TPMJS to access your dashboard

+
+ +
+ {error && ( +
+ {error} +
+ )} + +
+ + setName(e.target.value)} + required + className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-foreground focus:border-transparent" + placeholder="Your name" + /> +
+ +
+ + setEmail(e.target.value)} + required + className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-foreground focus:border-transparent" + placeholder="you@example.com" + /> +
+ +
+ + setPassword(e.target.value)} + required + minLength={8} + className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-foreground focus:border-transparent" + placeholder="At least 8 characters" + /> +
+ + +
+ +

+ Already have an account?{' '} + + Sign in + +

+
+ ); +} diff --git a/apps/web/src/app/(auth)/verify-email/page.tsx b/apps/web/src/app/(auth)/verify-email/page.tsx new file mode 100644 index 0000000..828607a --- /dev/null +++ b/apps/web/src/app/(auth)/verify-email/page.tsx @@ -0,0 +1,39 @@ +import Link from 'next/link'; + +export default function VerifyEmailPage() { + return ( +
+
+ + + +
+ +
+

Check your email

+

+ We've sent you a verification link. Please check your email to verify your account. +

+
+ +
+

Didn't receive the email?

+

Check your spam folder or try signing up again.

+
+ + + Back to sign in + +
+ ); +} diff --git a/apps/web/src/app/api/auth/[...all]/route.ts b/apps/web/src/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..9900a3b --- /dev/null +++ b/apps/web/src/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from '@/lib/auth'; +import { toNextJsHandler } from 'better-auth/next-js'; + +export const { GET, POST } = toNextJsHandler(auth); diff --git a/apps/web/src/app/dashboard/page.tsx b/apps/web/src/app/dashboard/page.tsx new file mode 100644 index 0000000..ae1ea6a --- /dev/null +++ b/apps/web/src/app/dashboard/page.tsx @@ -0,0 +1,52 @@ +import { SignOutButton } from '@/components/auth/SignOutButton'; +import { auth } from '@/lib/auth'; +import { headers } from 'next/headers'; +import { redirect } from 'next/navigation'; + +export default async function DashboardPage() { + const session = await auth.api.getSession({ + headers: await headers(), + }); + + if (!session) { + redirect('/sign-in'); + } + + return ( +
+
+
+

Dashboard

+ +
+ +
+

Profile

+ +
+
+ +

{session.user.name}

+
+ +
+ +

{session.user.email}

+
+ +
+ +

+ {session.user.emailVerified ? ( + Verified + ) : ( + Not verified + )} +

+
+
+
+
+
+ ); +} diff --git a/apps/web/src/components/auth/SignOutButton.tsx b/apps/web/src/components/auth/SignOutButton.tsx new file mode 100644 index 0000000..cd0067d --- /dev/null +++ b/apps/web/src/components/auth/SignOutButton.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { signOut } from '@/lib/auth-client'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; + +export function SignOutButton() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + + async function handleSignOut() { + setLoading(true); + try { + await signOut(); + router.push('/sign-in'); + } catch (error) { + console.error('Sign out failed:', error); + } finally { + setLoading(false); + } + } + + return ( + + ); +} diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index ab751c8..017c9ea 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -9,4 +9,11 @@ export const env = createEnv({ .string() .url() .default('https://endearing-commitment-production.up.railway.app'), // Railway service for health checks + + // Better Auth + BETTER_AUTH_SECRET: z.string().min(32).optional(), // Required for session encryption + BETTER_AUTH_URL: z.string().url().optional(), // Base URL for auth callbacks + + // Resend (Email) + RESEND_API_KEY: z.string().startsWith('re_').optional(), // Resend API key for sending emails }); diff --git a/apps/web/src/lib/auth-client.ts b/apps/web/src/lib/auth-client.ts new file mode 100644 index 0000000..1784745 --- /dev/null +++ b/apps/web/src/lib/auth-client.ts @@ -0,0 +1,7 @@ +'use client'; + +import { createAuthClient } from 'better-auth/react'; + +export const authClient = createAuthClient(); + +export const { signIn, signUp, signOut, useSession } = authClient; diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts new file mode 100644 index 0000000..52023d5 --- /dev/null +++ b/apps/web/src/lib/auth.ts @@ -0,0 +1,25 @@ +import { prisma } from '@tpmjs/db'; +import { betterAuth } from 'better-auth'; +import { prismaAdapter } from 'better-auth/adapters/prisma'; +import { sendVerificationEmail } from './email'; + +export const auth = betterAuth({ + database: prismaAdapter(prisma, { + provider: 'postgresql', + }), + emailAndPassword: { + enabled: true, + requireEmailVerification: true, + }, + emailVerification: { + sendVerificationEmail: async ({ user, url }) => { + await sendVerificationEmail(user.email, url); + }, + }, + session: { + expiresIn: 60 * 60 * 24 * 7, // 7 days + updateAge: 60 * 60 * 24, // 1 day + }, +}); + +export type Session = typeof auth.$Infer.Session; diff --git a/apps/web/src/lib/email.ts b/apps/web/src/lib/email.ts new file mode 100644 index 0000000..bf7f424 --- /dev/null +++ b/apps/web/src/lib/email.ts @@ -0,0 +1,40 @@ +import { Resend } from 'resend'; + +const resend = new Resend(process.env.RESEND_API_KEY); + +export async function sendVerificationEmail(to: string, verificationUrl: string) { + const { error } = await resend.emails.send({ + from: 'TPMJS ', + to, + subject: 'Verify your email - TPMJS', + html: ` + + + + + + + +

Verify your email

+

+ Click the button below to verify your email address and complete your registration. +

+ + Verify Email + +

+ If you didn't create an account on TPMJS, you can safely ignore this email. +

+

+ This link will expire in 24 hours. +

+ + + `, + }); + + if (error) { + console.error('Failed to send verification email:', error); + throw new Error('Failed to send verification email'); + } +} diff --git a/apps/web/src/middleware.ts b/apps/web/src/middleware.ts new file mode 100644 index 0000000..c1ce3c5 --- /dev/null +++ b/apps/web/src/middleware.ts @@ -0,0 +1,18 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + // Protect /dashboard routes + if (request.nextUrl.pathname.startsWith('/dashboard')) { + const sessionToken = request.cookies.get('better-auth.session_token'); + if (!sessionToken) { + return NextResponse.redirect(new URL('/sign-in', request.url)); + } + } + + return NextResponse.next(); +} + +export const config = { + matcher: ['/dashboard/:path*'], +}; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index e9a9541..020e270 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -313,3 +313,76 @@ model StatsSnapshot { @@index([date]) @@map("stats_snapshots") } + +// ============================================================================ +// Better Auth Models +// ============================================================================ + +/// User table - stores authenticated users +model User { + id String @id @default(cuid()) + name String + email String @unique + emailVerified Boolean @default(false) @map("email_verified") + image String? + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + sessions Session[] + accounts Account[] + + @@map("users") +} + +/// Session table - stores user sessions +model Session { + id String @id @default(cuid()) + userId String @map("user_id") + token String @unique + expiresAt DateTime @map("expires_at") + ipAddress String? @map("ip_address") + userAgent String? @map("user_agent") + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@map("sessions") +} + +/// Account table - stores auth provider accounts (including email/password) +model Account { + id String @id @default(cuid()) + userId String @map("user_id") + accountId String @map("account_id") + providerId String @map("provider_id") + accessToken String? @map("access_token") @db.Text + refreshToken String? @map("refresh_token") @db.Text + idToken String? @map("id_token") @db.Text + expiresAt DateTime? @map("expires_at") + password String? + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@map("accounts") +} + +/// Verification table - stores email verification tokens +model Verification { + id String @id @default(cuid()) + identifier String + value String + expiresAt DateTime @map("expires_at") + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("verifications") +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f230a17..3dee4f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -265,6 +265,9 @@ importers: ai: specifier: 6.0.3 version: 6.0.3(zod@4.1.13) + better-auth: + specifier: ^1.4.10 + version: 1.4.10(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(mongodb@6.20.0)(next@16.0.8(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vitest@2.1.9(@types/node@22.19.1)(happy-dom@15.11.7)(jsdom@27.4.0)(msw@2.12.3(@types/node@22.19.1)(typescript@5.9.3))) bm25: specifier: ^0.1.1 version: 0.1.1 @@ -304,6 +307,9 @@ importers: remark-gfm: specifier: ^4.0.1 version: 4.0.1 + resend: + specifier: ^6.6.0 + version: 6.6.0 zod: specifier: ^4.0.0 version: 4.1.13 @@ -337,10 +343,10 @@ importers: version: 17.2.3 eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@2.6.1) + version: 9.39.1(jiti@1.21.7) eslint-config-next: specifier: ^16.0.4 - version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) postcss: specifier: ^8.5.1 version: 8.5.6 @@ -599,7 +605,7 @@ importers: version: 13.1.0 drizzle-orm: specifier: ^0.38.3 - version: 0.38.4(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(@types/better-sqlite3@7.6.13)(@types/react@19.2.7)(better-sqlite3@11.10.0)(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react@19.2.0) + version: 0.38.4(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(@types/better-sqlite3@7.6.13)(@types/react@19.2.7)(better-sqlite3@11.10.0)(kysely@0.28.9)(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react@19.2.0) ora: specifier: ^8.1.1 version: 8.2.0 @@ -3796,6 +3802,27 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@better-auth/core@1.4.10': + resolution: {integrity: sha512-AThrfb6CpG80wqkanfrbN2/fGOYzhGladHFf3JhaWt/3/Vtf4h084T6PJLrDE7M/vCCGYvDI1DkvP3P1OB2HAg==} + peerDependencies: + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + better-call: 1.1.7 + jose: ^6.1.0 + kysely: ^0.28.5 + nanostores: ^1.0.1 + + '@better-auth/telemetry@1.4.10': + resolution: {integrity: sha512-Dq4XJX6EKsUu0h3jpRagX739p/VMOTcnJYWRrLtDYkqtZFg+sFiFsSWVcfapZoWpRSUGYX9iKwl6nDHn6Ju2oQ==} + peerDependencies: + '@better-auth/core': 1.4.10 + + '@better-auth/utils@0.3.0': + resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==} + + '@better-fetch/fetch@1.1.21': + resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} + '@biomejs/biome@1.9.4': resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} engines: {node: '>=14.21.3'} @@ -5114,6 +5141,14 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@2.1.1': + resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + engines: {node: '>= 20.19.0'} + + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -5447,6 +5482,9 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -6402,6 +6440,73 @@ packages: bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + better-auth@1.4.10: + resolution: {integrity: sha512-0kqwEBJLe8eyFzbUspRG/htOriCf9uMLlnpe34dlIJGdmDfPuQISd4shShvUrvIVhPxsY1dSTXdXPLpqISYOYg==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: '>=0.31.4' + drizzle-orm: '>=0.41.0' + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + + better-call@1.1.7: + resolution: {integrity: sha512-6gaJe1bBIEgVebQu/7q9saahVzvBsGaByEnE8aDVncZEDiJO7sdNB28ot9I6iXSbR25egGmmZ6aIURXyQHRraQ==} + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -7292,6 +7397,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + esbuild-register@3.6.0: resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} peerDependencies: @@ -7530,6 +7638,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -8229,6 +8340,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -8370,6 +8484,10 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4 <7' + kysely@0.28.9: + resolution: {integrity: sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA==} + engines: {node: '>=20.0.0'} + langium@3.3.1: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} @@ -8891,6 +9009,10 @@ packages: engines: {node: ^18 || >=20} hasBin: true + nanostores@1.1.0: + resolution: {integrity: sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==} + engines: {node: ^20.0.0 || >=22.0.0} + napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -9423,6 +9545,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9604,6 +9729,15 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resend@6.6.0: + resolution: {integrity: sha512-d1WoOqSxj5x76JtQMrieNAG1kZkh4NU4f+Je1yq4++JsDpLddhEwnJlNfvkCzvUuZy9ZquWmMMAm2mENd2JvRw==} + engines: {node: '>=20'} + peerDependencies: + '@react-email/render': '*' + peerDependenciesMeta: + '@react-email/render': + optional: true + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -9647,6 +9781,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rou3@0.7.12: + resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} + roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} @@ -9733,6 +9870,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -10007,6 +10147,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svix@1.76.1: + resolution: {integrity: sha512-CRuDWBTgYfDnBLRaZdKp9VuoPcNUq9An14c/k+4YJ15Qc5Grvf66vp0jvTltd4t7OIRj+8lM1DAgvSgvf7hdLw==} + swr@2.3.7: resolution: {integrity: sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g==} peerDependencies: @@ -10387,6 +10530,9 @@ packages: url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -10402,6 +10548,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -11086,6 +11236,27 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@better-auth/core@1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.1.13))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0)': + dependencies: + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@standard-schema/spec': 1.1.0 + better-call: 1.1.7(zod@4.1.13) + jose: 6.1.3 + kysely: 0.28.9 + nanostores: 1.1.0 + zod: 4.1.13 + + '@better-auth/telemetry@1.4.10(@better-auth/core@1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.1.13))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0))': + dependencies: + '@better-auth/core': 1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.1.13))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + + '@better-auth/utils@0.3.0': {} + + '@better-fetch/fetch@1.1.21': {} + '@biomejs/biome@1.9.4': optionalDependencies: '@biomejs/cli-darwin-arm64': 1.9.4 @@ -11741,6 +11912,11 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@1.21.7))': + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -12089,6 +12265,10 @@ snapshots: '@next/swc-win32-x64-msvc@16.0.8': optional: true + '@noble/ciphers@2.1.1': {} + + '@noble/hashes@2.0.1': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -12348,6 +12528,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.1.0': {} '@storybook/addon-actions@8.6.14(storybook@8.6.14(prettier@2.8.8))': @@ -12899,6 +13081,23 @@ snapshots: dependencies: '@types/webidl-conversions': 7.0.3 + '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + eslint: 9.39.1(jiti@1.21.7) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -12916,6 +13115,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.3 + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.48.0 @@ -12946,6 +13157,18 @@ snapshots: dependencies: typescript: 5.9.3 + '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.1(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.48.0 @@ -12975,6 +13198,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -13435,6 +13669,39 @@ snapshots: dependencies: tweetnacl: 0.14.5 + better-auth@1.4.10(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(mongodb@6.20.0)(next@16.0.8(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vitest@2.1.9(@types/node@22.19.1)(happy-dom@15.11.7)(jsdom@27.4.0)(msw@2.12.3(@types/node@22.19.1)(typescript@5.9.3))): + dependencies: + '@better-auth/core': 1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.1.13))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0) + '@better-auth/telemetry': 1.4.10(@better-auth/core@1.4.10(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.7(zod@4.1.13))(jose@6.1.3)(kysely@0.28.9)(nanostores@1.1.0)) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@noble/ciphers': 2.1.1 + '@noble/hashes': 2.0.1 + better-call: 1.1.7(zod@4.1.13) + defu: 6.1.4 + jose: 6.1.3 + kysely: 0.28.9 + nanostores: 1.1.0 + zod: 4.1.13 + optionalDependencies: + '@prisma/client': 6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3) + mongodb: 6.20.0 + next: 16.0.8(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + pg: 8.16.3 + prisma: 6.19.0(typescript@5.9.3) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + vitest: 2.1.9(@types/node@22.19.1)(happy-dom@15.11.7)(jsdom@27.4.0)(msw@2.12.3(@types/node@22.19.1)(typescript@5.9.3)) + + better-call@1.1.7(zod@4.1.13): + dependencies: + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + rou3: 0.7.12 + set-cookie-parser: 2.7.2 + optionalDependencies: + zod: 4.1.13 + better-opn@3.0.2: dependencies: open: 8.4.2 @@ -14166,13 +14433,14 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.38.4(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(@types/better-sqlite3@7.6.13)(@types/react@19.2.7)(better-sqlite3@11.10.0)(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react@19.2.0): + drizzle-orm@0.38.4(@opentelemetry/api@1.9.0)(@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3))(@types/better-sqlite3@7.6.13)(@types/react@19.2.7)(better-sqlite3@11.10.0)(kysely@0.28.9)(pg@8.16.3)(prisma@6.19.0(typescript@5.9.3))(react@19.2.0): optionalDependencies: '@opentelemetry/api': 1.9.0 '@prisma/client': 6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3) '@types/better-sqlite3': 7.6.13 '@types/react': 19.2.7 better-sqlite3: 11.10.0 + kysely: 0.28.9 pg: 8.16.3 prisma: 6.19.0(typescript@5.9.3) react: 19.2.0 @@ -14341,6 +14609,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es6-promise@4.2.8: {} + esbuild-register@3.6.0(esbuild@0.19.12): dependencies: debug: 4.4.3 @@ -14498,12 +14768,32 @@ snapshots: escape-string-regexp@5.0.0: {} + eslint-config-next@16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.0.4 + eslint: 9.39.1(jiti@1.21.7) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.1(jiti@1.21.7)) + globals: 16.4.0 + typescript-eslint: 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + eslint-config-next@16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.0.4 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) @@ -14526,7 +14816,22 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.1(jiti@1.21.7) + get-tsconfig: 4.13.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -14551,13 +14856,23 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): + dependencies: + debug: 3.2.7 + optionalDependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -14590,7 +14905,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -14599,9 +14914,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -14617,6 +14932,52 @@ snapshots: - eslint-import-resolver-webpack - supports-color + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.1(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.0 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.1(jiti@1.21.7) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: aria-query: 5.3.2 @@ -14640,6 +15001,17 @@ snapshots: dependencies: eslint: 9.39.1(jiti@2.6.1) + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@1.21.7)): + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.1(jiti@1.21.7) + hermes-parser: 0.25.1 + zod: 4.1.13 + zod-validation-error: 4.0.2(zod@4.1.13) + transitivePeerDependencies: + - supports-color + eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: '@babel/core': 7.28.5 @@ -14651,6 +15023,28 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.39.1(jiti@1.21.7) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -14682,6 +15076,47 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint@9.39.1(jiti@1.21.7): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.7 + transitivePeerDependencies: + - supports-color + eslint@9.39.1(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -14831,6 +15266,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-sha256@1.3.0: {} + fast-uri@3.1.0: {} fast-xml-parser@4.5.3: @@ -15635,6 +16072,8 @@ snapshots: jiti@2.6.1: {} + jose@6.1.3: {} + joycon@3.1.1: {} js-tokens@4.0.0: {} @@ -15814,6 +16253,8 @@ snapshots: typescript: 5.9.3 zod: 4.1.13 + kysely@0.28.9: {} + langium@3.3.1: dependencies: chevrotain: 11.0.3 @@ -16536,6 +16977,8 @@ snapshots: nanoid@5.1.6: {} + nanostores@1.1.0: {} + napi-build-utils@2.0.0: {} napi-postinstall@0.3.4: {} @@ -17110,6 +17553,8 @@ snapshots: quansync@0.2.11: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} range-parser@1.2.1: {} @@ -17401,6 +17846,10 @@ snapshots: requires-port@1.0.0: {} + resend@6.6.0: + dependencies: + svix: 1.76.1 + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -17460,6 +17909,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 + rou3@0.7.12: {} + roughjs@4.6.6: dependencies: hachure-fill: 0.5.2 @@ -17585,6 +18036,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-cookie-parser@2.7.2: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -17945,6 +18398,15 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svix@1.76.1: + dependencies: + '@stablelib/base64': 1.0.1 + '@types/node': 22.19.1 + es6-promise: 4.2.8 + fast-sha256: 1.3.0 + url-parse: 1.5.10 + uuid: 10.0.0 + swr@2.3.7(react@19.2.0): dependencies: dequal: 2.0.3 @@ -18263,6 +18725,17 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typescript-eslint@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.1(jiti@1.21.7) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript-eslint@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) @@ -18393,6 +18866,11 @@ snapshots: url-join@4.0.1: {} + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + use-sync-external-store@1.6.0(react@19.2.0): dependencies: react: 19.2.0 @@ -18409,6 +18887,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@11.1.0: {} uuid@3.4.0: {}