diff --git a/.env.sample b/.env.sample index 48025e2..bae3c25 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,6 @@ -AUTH_SECRET="openssl rand -hex 32" +BETTER_AUTH_SECRET="openssl rand -hex 32" +BETTER_AUTH_URL=http://localhost:3000 + DATABASE_URL="postgresql://username:password@localhost:5432/db_name" NEXT_PUBLIC_SENTRY_DSN="https://@.ingest.de.sentry.io/" diff --git a/README.md b/README.md index 2866c6f..9638f8f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - 🔗 [Eslint](https://eslint.org/) - 🔗 [ShadCN/ui](https://ui.shadcn.com/) - 🔗 [Sentry](https://bit.ly/sentry-docs-dg) -- 🔗 [Kinde Auth](https://kinde.com/dgray-nextjsstack/) +- 🔗 [Better Auth](https://www.better-auth.com) - 🔗 [Neon Postgres](https://fyi.neon.tech/davegray) - 🔗 [Prisma ORM](https://www.prisma.io/) - 🔗 [react-hook-form](https://react-hook-form.com/) diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx new file mode 100644 index 0000000..ee63d36 --- /dev/null +++ b/app/(auth)/layout.tsx @@ -0,0 +1,5 @@ +import { PropsWithChildren } from 'react'; + +export default function AuthLayout({ children }: Readonly) { + return
{children}
; +} diff --git a/app/login/page.tsx b/app/(auth)/login/page.tsx similarity index 100% rename from app/login/page.tsx rename to app/(auth)/login/page.tsx diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx new file mode 100644 index 0000000..33696b6 --- /dev/null +++ b/app/(auth)/sign-in/page.tsx @@ -0,0 +1,20 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { SignInForm } from '@/features/auth'; + +export default function SignInPage() { + return ( +
+
+ + + Sign In + Enter your email below to login to your account + + + + + +
+
+ ); +} diff --git a/app/(auth)/sign-up/page.tsx b/app/(auth)/sign-up/page.tsx new file mode 100644 index 0000000..ac9ede8 --- /dev/null +++ b/app/(auth)/sign-up/page.tsx @@ -0,0 +1,20 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { SignUpForm } from '@/features/auth'; + +export default function SignUpPage() { + return ( +
+
+ + + Sign Up + Enter your email below to create a new account + + + + + +
+
+ ); +} diff --git a/app/(shop)/home/page.tsx b/app/(shop)/home/page.tsx index 44199e6..e08f4b6 100644 --- a/app/(shop)/home/page.tsx +++ b/app/(shop)/home/page.tsx @@ -1,7 +1,43 @@ +import Link from 'next/link'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { getUser } from '@/lib/auth-server'; + export const metadata = { title: 'Home', }; -export default function HomePage() { - return

HomePage

; +export default async function HomePage() { + const user = await getUser(); + + if (!user) { + return ( +
+

Please log in to view your profile.

+

If you don't have an account, please sign up.

+

+ Login | Sign Up +

+
+ ); + } + + return ( + + + User Profile + + + +
+

+ Name: {user.name} +

+

+ Email: {user.email} +

+
+
+
+ ); } diff --git a/app/api/auth/[...all]/route.ts b/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..6f2e48f --- /dev/null +++ b/app/api/auth/[...all]/route.ts @@ -0,0 +1,5 @@ +import { toNextJsHandler } from 'better-auth/next-js'; + +import { auth } from '@/lib/auth'; + +export const { POST, GET } = toNextJsHandler(auth); diff --git a/app/layout.tsx b/app/layout.tsx index 7662340..5fa8bd6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,10 +17,7 @@ export const metadata: Metadata = baseMetadata; export default function RootLayout({ children }: Readonly) { return ( - + diff --git a/middleware.ts b/middleware.ts index abba1b1..2e909e8 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,26 +1,16 @@ -import { withAuth } from '@kinde-oss/kinde-auth-nextjs/middleware'; -import { NextRequest } from 'next/server'; +import { getSessionCookie } from 'better-auth/cookies'; +import { NextRequest, NextResponse } from 'next/server'; -export default withAuth( - async function middleware(request: NextRequest) { - // console.log(request); - }, - { isReturnToCurrentPage: true }, -); +export async function middleware(request: NextRequest) { + const sessionCookie = getSessionCookie(request); + + if (!sessionCookie) { + return NextResponse.redirect(new URL('/sign-in', request.url)); + } + + return NextResponse.next(); +} export const config = { - matcher: [ - /* - * Match all request paths except for the ones starting with: - * - api (API routes) - * - _next/static (static files) - * - _next/image (image optimization files) - * - images - * - auth - * - login - * - favicon.ico, sitemap.xml, robots.txt (metadata files) - * - homepage (represented with after beginning with a slash) - */ - '/((?!api|_next/static|_next/image|images|auth|login|favicon.ico|sitemap.xml|robots.txt|$).*)', - ], + matcher: ['/customers/:path*', '/tickets/:path*'], }; diff --git a/package.json b/package.json index 95c524d..713a957 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,13 @@ "@radix-ui/react-switch": "^1.2.2", "@sentry/nextjs": "^8.42.0", "@t3-oss/env-nextjs": "^0.11.1", + "better-auth": "^1.2.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.503.0", "next": "^15.3.0", "next-themes": "^0.4.1", + "next-safe-action": "^7.10.4", "react": "^19.1.0", "react-dom": "^19.1.0", "react-hook-form": "^7.54.1", @@ -44,16 +46,16 @@ "zod": "^3.24.1" }, "devDependencies": { - "@next/eslint-plugin-next": "^15.3.0", "@eslint/eslintrc": "^3.3.1", + "@next/eslint-plugin-next": "^15.3.0", "@prisma/client": "^6.6.0", "@tailwindcss/postcss": "^4.1.4", "@tailwindcss/typography": "^0.5.15", "@types/node": "^22.10.1", "@types/react": "^19.1.0", "@types/react-dom": "^19.1.0", - "@typescript-eslint/parser": "^8.31.0", "@typescript-eslint/eslint-plugin": "^8.31.0", + "@typescript-eslint/parser": "^8.31.0", "dotenv": "^16.5.0", "eslint": "^9.25.1", "eslint-config-next": "^15.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d20b773..12a5668 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 3.9.1(react-hook-form@7.54.1(react@19.1.0)) '@kinde-oss/kinde-auth-nextjs': specifier: ^2.4.6 - version: 2.4.6(@babel/core@7.26.0)(next@15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.4.6(@babel/core@7.26.10)(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@kinde/management-api-js': specifier: ^0.11.0 version: 0.11.0 @@ -37,10 +37,13 @@ importers: version: 1.2.2(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@sentry/nextjs': specifier: ^8.42.0 - version: 8.42.0(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.96.1(esbuild@0.23.1)) + version: 8.42.0(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.96.1(esbuild@0.23.1)) '@t3-oss/env-nextjs': specifier: ^0.11.1 version: 0.11.1(typescript@5.7.2)(zod@3.24.1) + better-auth: + specifier: ^1.2.7 + version: 1.2.7 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -52,7 +55,10 @@ importers: version: 0.503.0(react@19.1.0) next: specifier: ^15.3.0 - version: 15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next-safe-action: + specifier: ^7.10.4 + version: 7.10.7(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@3.24.1) next-themes: specifier: ^0.4.1 version: 0.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -194,14 +200,22 @@ packages: resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + '@babel/compat-data@7.26.8': + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.10': + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} engines: {node: '>=6.9.0'} '@babel/generator@7.26.2': resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.27.0': + resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.25.9': resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} @@ -214,6 +228,10 @@ packages: resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.0': + resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.25.9': resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} engines: {node: '>=6.9.0'} @@ -289,8 +307,8 @@ packages: resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + '@babel/helpers@7.27.0': + resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} engines: {node: '>=6.9.0'} '@babel/parser@7.26.2': @@ -298,6 +316,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.27.0': + resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} engines: {node: '>=6.9.0'} @@ -671,14 +694,32 @@ packages: resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.0': + resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.9': resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.27.0': + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.26.0': resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} + '@babel/types@7.27.0': + resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + engines: {node: '>=6.9.0'} + + '@better-auth/utils@0.2.4': + resolution: {integrity: sha512-ayiX87Xd5sCHEplAdeMgwkA0FgnXsEZBgDn890XHHwSWNqqRZDYOq3uj2Ei2leTv1I2KbG5HHn60Ah1i2JWZjQ==} + + '@better-fetch/fetch@1.1.18': + resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} + '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} @@ -885,6 +926,9 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@hexagon/base64@1.1.28': + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + '@hookform/resolvers@3.9.1': resolution: {integrity: sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==} peerDependencies: @@ -1061,6 +1105,9 @@ packages: '@kinde/management-api-js@0.11.0': resolution: {integrity: sha512-KPGpwS8CdrU+zvGM8b2F4ieF5KaL0gPh/kJnG4fTlzNdQ4Zb8m4WDOISPUTRG2ZLKIlp0ev1X6HRzXijktDkJA==} + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + '@napi-rs/wasm-runtime@0.2.9': resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} @@ -1118,6 +1165,13 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@0.6.0': + resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1359,6 +1413,21 @@ packages: '@panva/hkdf@1.2.1': resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@peculiar/asn1-android@2.3.16': + resolution: {integrity: sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw==} + + '@peculiar/asn1-ecc@2.3.15': + resolution: {integrity: sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==} + + '@peculiar/asn1-rsa@2.3.15': + resolution: {integrity: sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==} + + '@peculiar/asn1-schema@2.3.15': + resolution: {integrity: sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==} + + '@peculiar/asn1-x509@2.3.15': + resolution: {integrity: sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==} + '@pkgr/core@0.2.4': resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -1817,6 +1886,13 @@ packages: peerDependencies: webpack: '>=4.40.0' + '@simplewebauthn/browser@13.1.0': + resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==} + + '@simplewebauthn/server@13.1.1': + resolution: {integrity: sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==} + engines: {node: '>=20.0.0'} + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -2276,6 +2352,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asn1js@3.0.6: + resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + engines: {node: '>=12.0.0'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -2317,6 +2397,12 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + better-auth@1.2.7: + resolution: {integrity: sha512-2hCB263GSrgetsMUZw8vv9O1e4S4AlYJW3P4e8bX9u3Q3idv4u9BzDFCblpTLuL4YjYovghMCN0vurAsctXOAQ==} + + better-call@1.0.9: + resolution: {integrity: sha512-Qfm0gjk0XQz0oI7qvTK1hbqTsBY4xV2hsHAxF8LZfUYl3RaECCIifXuVqtPpZJWvlCCMlQSvkvhhyuApGUba6g==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2518,6 +2604,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -3175,6 +3264,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kysely@0.27.6: + resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} + engines: {node: '>=14.0.0'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -3373,6 +3466,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanostores@0.11.4: + resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} + engines: {node: ^18.0.0 || >=20.0.0} + napi-postinstall@0.1.5: resolution: {integrity: sha512-HI5bHONOUYqV+FJvueOSgjRxHTLB25a3xIv59ugAxFe7xRNbW96hyYbMbsKzl+QvFV9mN/SrtHwiU+vYhMwA7Q==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -3384,6 +3481,27 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-safe-action@7.10.7: + resolution: {integrity: sha512-CbWitme0wOsbNc7r7iQafvg1w641PFOMmElHde5ss1BQwK/fUKV2D9xRpXciWFo5obqR12lpyS3FTFFBh95FqQ==} + engines: {node: '>=18.17'} + peerDependencies: + '@sinclair/typebox': '>= 0.33.3' + next: '>= 14.0.0 || >= 14.0.1-canary.0' + react: '>= 18.2.0' + react-dom: '>= 18.2.0' + valibot: '>= 0.36.0' + yup: '>= 1.0.0' + zod: '>= 3.0.0' + peerDependenciesMeta: + '@sinclair/typebox': + optional: true + valibot: + optional: true + yup: + optional: true + zod: + optional: true + next-themes@0.4.1: resolution: {integrity: sha512-xD3cn42n0f1DFCAOlxJlrGPog+WdhWHObgJ+LTM7J5Bff/uBuq4vn/okSSao7puz7yBLcrELLOQ7F1/9hwycyQ==} peerDependencies: @@ -3672,6 +3790,13 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3801,6 +3926,9 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true + rou3@0.5.1: + resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3842,6 +3970,9 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -4095,6 +4226,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -4247,7 +4383,7 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 '@auth/core@0.37.3': @@ -4276,18 +4412,20 @@ snapshots: '@babel/compat-data@7.26.2': {} - '@babel/core@7.26.0': + '@babel/compat-data@7.26.8': {} + + '@babel/core@7.26.10': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.2 - '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/generator': 7.27.0 + '@babel/helper-compilation-targets': 7.27.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helpers': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -4304,6 +4442,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 + '@babel/generator@7.27.0': + dependencies: + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + '@babel/helper-annotate-as-pure@7.25.9': dependencies: '@babel/types': 7.26.0 @@ -4323,29 +4469,37 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + '@babel/helper-compilation-targets@7.27.0': + dependencies: + '@babel/compat-data': 7.26.8 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 '@babel/traverse': 7.25.9 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.0)': + '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 regexpu-core: 6.2.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': + '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 debug: 4.4.0 @@ -4368,12 +4522,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/traverse': 7.27.0 transitivePeerDependencies: - supports-color @@ -4383,18 +4537,18 @@ snapshots: '@babel/helper-plugin-utils@7.25.9': {} - '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-wrap-function': 7.25.9 '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': + '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 '@babel/traverse': 7.25.9 @@ -4429,471 +4583,475 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helpers@7.26.0': + '@babel/helpers@7.27.0': dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.0 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 '@babel/parser@7.26.2': dependencies: '@babel/types': 7.26.0 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': + '@babel/parser@7.27.0': + dependencies: + '@babel/types': 7.27.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.10) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 - '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.10) '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-module-imports': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.10) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) '@babel/traverse': 7.25.9 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/template': 7.25.9 - '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-exponentiation-operator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-exponentiation-operator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-builder-binary-assignment-operator-visitor': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-simple-access': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10) - '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 regenerator-transform: 0.15.2 - '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': + '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) '@babel/helper-plugin-utils': 7.25.9 - '@babel/preset-env@7.26.0(@babel/core@7.26.0)': + '@babel/preset-env@7.26.0(@babel/core@7.26.10)': dependencies: '@babel/compat-data': 7.26.2 - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) - '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) - '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-exponentiation-operator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) - babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) - babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10) + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.10) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-exponentiation-operator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.10) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.10) + babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.10) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.10) + babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.10) core-js-compat: 3.39.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.25.9 '@babel/types': 7.26.0 esutils: 2.0.3 @@ -4908,6 +5066,12 @@ snapshots: '@babel/parser': 7.26.2 '@babel/types': 7.26.0 + '@babel/template@7.27.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 + '@babel/traverse@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -4920,11 +5084,35 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.27.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.26.0': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@babel/types@7.27.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@better-auth/utils@0.2.4': + dependencies: + typescript: 5.8.3 + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.18': {} + '@emnapi/core@1.4.3': dependencies: '@emnapi/wasi-threads': 1.0.2 @@ -5074,6 +5262,8 @@ snapshots: '@floating-ui/utils@0.2.9': {} + '@hexagon/base64@1.1.28': {} + '@hookform/resolvers@3.9.1(react-hook-form@7.54.1(react@19.1.0))': dependencies: react-hook-form: 7.54.1(react@19.1.0) @@ -5197,14 +5387,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@kinde-oss/kinde-auth-nextjs@2.4.6(@babel/core@7.26.0)(next@15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@kinde-oss/kinde-auth-nextjs@2.4.6(@babel/core@7.26.10)(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.10) '@kinde-oss/kinde-typescript-sdk': 2.9.1 '@kinde/jwt-decoder': 0.2.0 cookie: 1.0.1 crypto-js: 4.2.0 - next: 15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) uncrypto: 0.1.3 @@ -5225,6 +5415,8 @@ snapshots: aws-jwt-verify: 4.0.1 dotenv: 16.5.0 + '@levischuck/tiny-cbor@0.2.11': {} + '@napi-rs/wasm-runtime@0.2.9': dependencies: '@emnapi/core': 1.4.3 @@ -5262,6 +5454,10 @@ snapshots: '@next/swc-win32-x64-msvc@15.3.1': optional: true + '@noble/ciphers@0.6.0': {} + + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5570,6 +5766,39 @@ snapshots: '@panva/hkdf@1.2.1': {} + '@peculiar/asn1-android@2.3.16': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.3.15': + dependencies: + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + '@pkgr/core@0.2.4': {} '@prisma/client@6.6.0(prisma@6.6.0(typescript@5.7.2))(typescript@5.7.2)': @@ -5913,7 +6142,7 @@ snapshots: '@sentry/bundler-plugin-core@2.22.6': dependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 '@sentry/babel-plugin-component-annotate': 2.22.6 '@sentry/cli': 2.38.2 dotenv: 16.5.0 @@ -5967,7 +6196,7 @@ snapshots: '@sentry/core@8.42.0': {} - '@sentry/nextjs@8.42.0(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.96.1(esbuild@0.23.1))': + '@sentry/nextjs@8.42.0(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.96.1(esbuild@0.23.1))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) @@ -5981,7 +6210,7 @@ snapshots: '@sentry/vercel-edge': 8.42.0 '@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(esbuild@0.23.1)) chalk: 3.0.0 - next: 15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 @@ -6065,6 +6294,18 @@ snapshots: - encoding - supports-color + '@simplewebauthn/browser@13.1.0': {} + + '@simplewebauthn/server@13.1.1': + dependencies: + '@hexagon/base64': 1.1.28 + '@levischuck/tiny-cbor': 0.2.11 + '@peculiar/asn1-android': 2.3.16 + '@peculiar/asn1-ecc': 2.3.15 + '@peculiar/asn1-rsa': 2.3.15 + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.15': @@ -6563,6 +6804,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asn1js@3.0.6: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + ast-types-flow@0.0.8: {} async-function@1.0.0: {} @@ -6577,32 +6824,54 @@ snapshots: axobject-query@4.1.0: {} - babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): + babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.10): dependencies: '@babel/compat-data': 7.26.2 - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.10) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.10): dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.10) core-js-compat: 3.39.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): + babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.10): dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.10) transitivePeerDependencies: - supports-color balanced-match@1.0.2: {} + better-auth@1.2.7: + dependencies: + '@better-auth/utils': 0.2.4 + '@better-fetch/fetch': 1.1.18 + '@noble/ciphers': 0.6.0 + '@noble/hashes': 1.8.0 + '@simplewebauthn/browser': 13.1.0 + '@simplewebauthn/server': 13.1.1 + better-call: 1.0.9 + defu: 6.1.4 + jose: 5.9.6 + kysely: 0.27.6 + nanostores: 0.11.4 + zod: 3.24.1 + + better-call@1.0.9: + dependencies: + '@better-fetch/fetch': 1.1.18 + rou3: 0.5.1 + set-cookie-parser: 2.7.1 + uncrypto: 0.1.3 + binary-extensions@2.3.0: {} brace-expansion@1.1.11: @@ -6799,6 +7068,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.4: {} + detect-libc@2.0.3: {} detect-node-es@1.1.0: {} @@ -7605,6 +7876,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kysely@0.27.6: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -7776,18 +8049,28 @@ snapshots: nanoid@3.3.7: {} + nanostores@0.11.4: {} + napi-postinstall@0.1.5: {} natural-compare@1.4.0: {} neo-async@2.6.2: {} + next-safe-action@7.10.7(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@3.24.1): + dependencies: + next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + zod: 3.24.1 + next-themes@0.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - next@15.3.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.3.1 '@swc/counter': 0.1.3 @@ -7797,7 +8080,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.1 '@next/swc-darwin-x64': 15.3.1 @@ -8006,6 +8289,12 @@ snapshots: punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + queue-microtask@1.2.3: {} randombytes@2.1.0: @@ -8142,6 +8431,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + rou3@0.5.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -8190,6 +8481,8 @@ snapshots: dependencies: randombytes: 2.1.0 + set-cookie-parser@2.7.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -8383,12 +8676,12 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(@babel/core@7.26.0)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.26.0 + '@babel/core': 7.26.10 supports-color@7.2.0: dependencies: @@ -8503,6 +8796,8 @@ snapshots: typescript@5.7.2: {} + typescript@5.8.3: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 diff --git a/prisma.config.ts b/prisma.config.ts index 679225d..8d8c033 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -1,4 +1,5 @@ import path from 'node:path'; + import { config } from 'dotenv'; import type { PrismaConfig } from 'prisma'; diff --git a/prisma/migrations/20250424161810_add_authentication/migration.sql b/prisma/migrations/20250424161810_add_authentication/migration.sql new file mode 100644 index 0000000..dafa04b --- /dev/null +++ b/prisma/migrations/20250424161810_add_authentication/migration.sql @@ -0,0 +1,69 @@ +-- CreateTable +CREATE TABLE "user" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "emailVerified" BOOLEAN NOT NULL, + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "user_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "session" ( + "id" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "token" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + "ipAddress" TEXT, + "userAgent" TEXT, + "userId" TEXT NOT NULL, + + CONSTRAINT "session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account" ( + "id" TEXT NOT NULL, + "accountId" TEXT NOT NULL, + "providerId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "accessToken" TEXT, + "refreshToken" TEXT, + "idToken" TEXT, + "accessTokenExpiresAt" TIMESTAMP(3), + "refreshTokenExpiresAt" TIMESTAMP(3), + "scope" TEXT, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "verification" ( + "id" TEXT NOT NULL, + "identifier" TEXT NOT NULL, + "value" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3), + "updatedAt" TIMESTAMP(3), + + CONSTRAINT "verification_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); + +-- AddForeignKey +ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index fbffa92..044d57c 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ad5dcf5..0a2620e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -2,27 +2,27 @@ datasource db { provider = "postgresql" url = env("DATABASE_URL") } + generator client { - provider = "prisma-client-js" - previewFeatures = ["prismaSchemaFolder"] - output = "../src/generated/prisma" + provider = "prisma-client-js" + output = "../src/generated/prisma" } model Customer { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) firstName String lastName String - email String @unique + email String @unique phone String address1 String address2 String? city String - state String @db.VarChar(3) - zip String @db.VarChar(10) + state String @db.VarChar(3) + zip String @db.VarChar(10) notes String? - active Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + active Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt tickets Ticket[] } @@ -36,4 +36,64 @@ model Ticket { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customer Customer @relation(fields: [customerId], references: [id]) +} + +model User { + id String @id + name String + email String + emailVerified Boolean + image String? + createdAt DateTime + updatedAt DateTime + sessions Session[] + accounts Account[] + + @@unique([email]) + @@map("user") +} + +model Session { + id String @id + expiresAt DateTime + token String + createdAt DateTime + updatedAt DateTime + ipAddress String? + userAgent String? + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([token]) + @@map("session") +} + +model Account { + id String @id + accountId String + providerId String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + accessToken String? + refreshToken String? + idToken String? + accessTokenExpiresAt DateTime? + refreshTokenExpiresAt DateTime? + scope String? + password String? + createdAt DateTime + updatedAt DateTime + + @@map("account") +} + +model Verification { + id String @id + identifier String + value String + expiresAt DateTime + createdAt DateTime? + updatedAt DateTime? + + @@map("verification") } \ No newline at end of file diff --git a/prisma/seed.ts b/prisma/seed.ts index e3ce403..2a02ec2 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,6 +1,6 @@ +import prisma from '@/lib/prisma'; import { generateCustomers } from '@/prisma/data/customers'; import { generateTickets } from '@/prisma/data/tickets'; -import prisma from '@/prisma/singleton'; const seed = async () => { const customers = generateCustomers(); diff --git a/src/components/alert-server-response.tsx b/src/components/alert-server-response.tsx new file mode 100644 index 0000000..8735047 --- /dev/null +++ b/src/components/alert-server-response.tsx @@ -0,0 +1,77 @@ +import { CircleCheck, TriangleAlert } from 'lucide-react'; +import { Fragment, ReactNode } from 'react'; + +const MessageBox = ({ type, content }: { type: 'success' | 'error'; content: ReactNode }) => { + if (type === 'error') { + return ( +
+ +

{content}

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

{content}

+
+ ); +}; + +type Props = { + result: { + data?: { + message?: string; + error?: boolean; + }; + serverError?: string; + validationErrors?: Record; + }; +}; + +export function AlertServerResponse({ result }: Props) { + const { data, serverError, validationErrors } = result; + + if (data?.error) { + return ( + + ); + } + + return ( + + {data?.message && ( + + )} + + {serverError && ( + + )} + + {validationErrors && ( + ( +

{`${key}: ${validationErrors[key as keyof typeof validationErrors]}`}

+ ))} + /> + )} +
+ ); +} diff --git a/src/components/header.tsx b/src/components/header.tsx index ff6bab7..3380c60 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,4 +1,3 @@ -import { LogoutLink } from '@kinde-oss/kinde-auth-nextjs/components'; import { File, HomeIcon, LogOut, UsersRound } from 'lucide-react'; import Link from 'next/link'; @@ -38,15 +37,12 @@ export const Header = () => { diff --git a/src/features/auth/components/sign-in-form.tsx b/src/features/auth/components/sign-in-form.tsx new file mode 100644 index 0000000..e66412b --- /dev/null +++ b/src/features/auth/components/sign-in-form.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { LoaderCircle } from 'lucide-react'; +import Link from 'next/link'; +import { useAction } from 'next-safe-action/hooks'; +import { SubmitHandler, useForm } from 'react-hook-form'; + +import { AlertServerResponse } from '@/components/alert-server-response'; +import { InputWithLabel } from '@/components/rhf/input-with-label'; +import { Button } from '@/components/ui/button'; +import { Form } from '@/components/ui/form'; +import { SignInFormFields, SignInFormSchema, signInAction } from '@/features/auth'; + +export function SignInForm() { + const defaultValues: SignInFormFields = { + email: '', + password: '', + rememberMe: false, + }; + + const form = useForm({ + mode: 'onBlur', + resolver: zodResolver(SignInFormSchema), + defaultValues, + }); + + const { result, isPending, execute: signIn } = useAction(signInAction); + const hasResult = Object.keys(result).length > 0; + + const onSubmit: SubmitHandler = async (data) => { + signIn(data); + }; + + return ( +
+ + + required + name='email' + title='Email' + placeholder='m@example.com' + /> + + required + type='password' + name='password' + title='Password' + placeholder='Enter your password' + /> + + +
+ Don't have an account ? + + Sign up + +
+ + {hasResult ? : null} + + + ); +} diff --git a/src/features/auth/components/sign-up-form.tsx b/src/features/auth/components/sign-up-form.tsx new file mode 100644 index 0000000..eb2c9a9 --- /dev/null +++ b/src/features/auth/components/sign-up-form.tsx @@ -0,0 +1,89 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { LoaderCircle } from 'lucide-react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useTransition } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { toast } from 'sonner'; + +import { InputWithLabel } from '@/components/rhf/input-with-label'; +import { Button } from '@/components/ui/button'; +import { Form } from '@/components/ui/form'; +import { SignUpFormFields, SignUpFormSchema } from '@/features/auth'; +import { signUp } from '@/lib/auth-client'; + +export function SignUpForm() { + const router = useRouter(); + const [isPending, startTransition] = useTransition(); + + const defaultValues: SignUpFormFields = { + email: '', + password: '', + name: '', + }; + + const form = useForm({ + mode: 'onBlur', + resolver: zodResolver(SignUpFormSchema), + defaultValues, + }); + + const onSubmit: SubmitHandler = async (data) => { + startTransition(async () => { + await signUp.email(data, { + onSuccess: () => { + router.push('/home'); + }, + onError: (response) => { + console.error('SignUp Error', response); + toast.error(response.error.message || 'Error signing up, please try again.'); + }, + }); + }); + }; + + return ( +
+ + + required + name='name' + title='Name' + placeholder='John Doe' + /> + + required + name='email' + type='email' + title='Email' + placeholder='m@example.com' + /> + + required + type='password' + name='password' + title='Password' + placeholder='Enter your password' + /> + + +
+ Already have an account ? + + Sign in + +
+ + + ); +} diff --git a/src/features/auth/index.ts b/src/features/auth/index.ts new file mode 100644 index 0000000..ba130c0 --- /dev/null +++ b/src/features/auth/index.ts @@ -0,0 +1,6 @@ +export * from './types/auth.schema'; + +export * from './services/auth.actions'; + +export * from './components/sign-in-form'; +export * from './components/sign-up-form'; diff --git a/src/features/auth/services/auth.actions.ts b/src/features/auth/services/auth.actions.ts new file mode 100644 index 0000000..15aab76 --- /dev/null +++ b/src/features/auth/services/auth.actions.ts @@ -0,0 +1,20 @@ +'use server'; + +import { redirect } from 'next/navigation'; + +import { SignInFormSchema } from '@/features/auth'; +import { signIn } from '@/lib/auth-client'; +import { actionClient } from '@/lib/safe-action'; + +export const signInAction = actionClient + .metadata({ actionName: 'signInAction' }) + .schema(SignInFormSchema) + .action(async ({ parsedInput }) => { + const { error } = await signIn.email(parsedInput); + + if (error) { + return { error: true, message: 'Incorrect credentials' }; + } + + redirect('/home'); + }); diff --git a/src/features/auth/types/auth.schema.ts b/src/features/auth/types/auth.schema.ts new file mode 100644 index 0000000..57961e8 --- /dev/null +++ b/src/features/auth/types/auth.schema.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; + +const BaseAuthFormSchema = z.object({ + email: z.string().email().max(50), + password: z.string().min(8).max(32), +}); + +export const SignUpFormSchema = BaseAuthFormSchema.extend({ + name: z.string().min(1).max(50), + images: z.string().optional(), +}); +export type SignUpFormFields = z.infer; + +export const SignInFormSchema = BaseAuthFormSchema.extend({ + rememberMe: z.boolean().optional(), +}); +export type SignInFormFields = z.infer; diff --git a/src/features/customer/services/customer.query.ts b/src/features/customer/services/customer.query.ts index d2e131d..0b85294 100644 --- a/src/features/customer/services/customer.query.ts +++ b/src/features/customer/services/customer.query.ts @@ -1,5 +1,5 @@ import { fetcherWithToastError } from '@/lib/fetcher'; -import prisma from '@/prisma/singleton'; +import prisma from '@/lib/prisma'; export const getCustomer = async (id: number) => { return fetcherWithToastError(() => diff --git a/src/features/ticket/components/ticket-form.tsx b/src/features/ticket/components/ticket-form.tsx index 0bf993e..855907f 100644 --- a/src/features/ticket/components/ticket-form.tsx +++ b/src/features/ticket/components/ticket-form.tsx @@ -10,13 +10,13 @@ import { TextAreaWithLabel } from '@/components/rhf/textarea-with-label'; import { Button } from '@/components/ui/button'; import { Form } from '@/components/ui/form'; import { CustomerInfos } from '@/features/customer'; -import { Customer, Ticket } from '@/generated/prisma'; import { DEFAULT_TECH_ID, DEFAULT_TECH_TICKET, TicketFields, TicketSchema, -} from '@/src/features/ticket/types/ticket.schema'; +} from '@/features/ticket/types/ticket.schema'; +import { Customer, Ticket } from '@/generated/prisma'; type Props = { ticket?: Ticket; diff --git a/src/features/ticket/services/ticket.query.ts b/src/features/ticket/services/ticket.query.ts index 95a89ae..227e541 100644 --- a/src/features/ticket/services/ticket.query.ts +++ b/src/features/ticket/services/ticket.query.ts @@ -1,5 +1,5 @@ import { fetcherWithToastError } from '@/lib/fetcher'; -import prisma from '@/prisma/singleton'; +import prisma from '@/lib/prisma'; export const getTicket = (id: number) => { return fetcherWithToastError(() => prisma.ticket.findUnique({ where: { id } })); diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts new file mode 100644 index 0000000..bbe709d --- /dev/null +++ b/src/lib/auth-client.ts @@ -0,0 +1,7 @@ +import { createAuthClient } from 'better-auth/react'; + +export const authClient = createAuthClient({ + baseURL: process.env.BETTER_AUTH_URL, +}); + +export const { useSession, signIn, signUp, signOut } = authClient; diff --git a/src/lib/auth-server.ts b/src/lib/auth-server.ts new file mode 100644 index 0000000..66919ca --- /dev/null +++ b/src/lib/auth-server.ts @@ -0,0 +1,16 @@ +import { headers } from 'next/headers'; + +import { auth } from '@/lib/auth'; + +export const getSession = async () => { + const session = await auth.api.getSession({ + headers: await headers(), + }); + + return session; +}; + +export const getUser = async () => { + const session = await getSession(); + return session?.user; +}; diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..1e114ca --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,13 @@ +import { betterAuth } from 'better-auth'; +import { prismaAdapter } from 'better-auth/adapters/prisma'; + +import prisma from '@/lib/prisma'; + +export const auth = betterAuth({ + database: prismaAdapter(prisma, { + provider: 'postgresql', + }), + emailAndPassword: { + enabled: true, + }, +}); diff --git a/src/lib/env.ts b/src/lib/env.ts index ca4cf8c..0ef8c8e 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -3,8 +3,9 @@ import { z } from 'zod'; export const env = createEnv({ server: { - AUTH_SECRET: z.string().min(1), DATABASE_URL: z.string().url(), + BETTER_AUTH_URL: z.string().min(1), + BETTER_AUTH_SECRET: z.string().min(1), SENTRY_AUTH_TOKEN: z.string().min(1), @@ -22,17 +23,17 @@ export const env = createEnv({ KINDE_MANAGEMENT_CLIENT_SECRET: z.string().min(1), }, client: { - NEXT_PUBLIC_SENTRY_DSN: z.string().url(), NEXT_PUBLIC_KINDE_EMAIL: z.string(), NEXT_PUBLIC_KINDE_GMAIL: z.string(), NEXT_PUBLIC_KINDE_GITHUB: z.string(), + NEXT_PUBLIC_SENTRY_DSN: z.string().url(), }, runtimeEnv: { - AUTH_SECRET: process.env.AUTH_SECRET, DATABASE_URL: process.env.DATABASE_URL, + BETTER_AUTH_URL: process.env.BETTER_AUTH_URL, + BETTER_AUTH_SECRET: process.env.AUTH_SECRET, SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN, - NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, KINDE_CLIENT_ID: process.env.KINDE_CLIENT_ID, KINDE_CLIENT_SECRET: process.env.KINDE_CLIENT_SECRET, @@ -47,6 +48,7 @@ export const env = createEnv({ KINDE_MANAGEMENT_CLIENT_ID: process.env.KINDE_MANAGEMENT_CLIENT_ID, KINDE_MANAGEMENT_CLIENT_SECRET: process.env.KINDE_MANAGEMENT_CLIENT_SECRET, + NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, NEXT_PUBLIC_KINDE_EMAIL: process.env.NEXT_PUBLIC_KINDE_EMAIL, NEXT_PUBLIC_KINDE_GMAIL: process.env.NEXT_PUBLIC_KINDE_GMAIL, NEXT_PUBLIC_KINDE_GITHUB: process.env.NEXT_PUBLIC_KINDE_GITHUB, diff --git a/prisma/singleton.ts b/src/lib/prisma.ts similarity index 100% rename from prisma/singleton.ts rename to src/lib/prisma.ts diff --git a/src/lib/safe-action.ts b/src/lib/safe-action.ts new file mode 100644 index 0000000..3428a18 --- /dev/null +++ b/src/lib/safe-action.ts @@ -0,0 +1,31 @@ +import * as Sentry from '@sentry/nextjs'; +import { createSafeActionClient } from 'next-safe-action'; +import { z } from 'zod'; + +export const actionClient = createSafeActionClient({ + defineMetadataSchema() { + return z.object({ + actionName: z.string(), + }); + }, + handleServerError(error, utils) { + const { clientInput, metadata } = utils; + + Sentry.captureException(error, (scope) => { + scope.clear(); + scope.setContext('serverError', { message: error.message }); + scope.setContext('metadata', { actionName: metadata.actionName }); + scope.setContext('clientInput', { clientInput }); + return scope; + }); + + // We don't want to leak any sensitive data + if (error.constructor.name === 'DatabaseError') { + return 'Database Error: Your data did not save. Support will be notified.'; + } + + console.error('Action error:', error.message); + return error.message; + }, + defaultValidationErrorsShape: 'flattened', +}); diff --git a/src/styles/globals.css b/src/styles/globals.css index ba1f1cf..139c8d7 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -19,6 +19,7 @@ --accent: hsl(220 14.3% 95.9%); --accent-foreground: hsl(220.9 39.3% 11%); --destructive: hsl(0 84.2% 60.2%); + --positive: hsl(149, 76%, 38%); --destructive-foreground: hsl(210 20% 98%); --border: hsl(220 13% 91%); --input: hsl(220 13% 91%); @@ -47,6 +48,7 @@ --accent: hsl(215 27.9% 16.9%); --accent-foreground: hsl(210 20% 98%); --destructive: hsl(0 62.8% 45%); + --positive: hsl(149, 77%, 30%); --destructive-foreground: hsl(210 20% 98%); --border: hsl(215 27.9% 16.9%); --input: hsl(215 27.9% 16.9%); @@ -74,6 +76,8 @@ --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); + --color-positive: var(--positive); + --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground);