diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35b3e9a..a36f5a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,7 @@ jobs: run: pnpm build env: NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }} - name: Upload build artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9538516..86e7b11 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -55,6 +55,7 @@ jobs: run: pnpm build env: NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bef35d9..90f243c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,6 +79,7 @@ jobs: run: pnpm build env: NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }} release: name: Create GitHub Release diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd0f07..2103f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,94 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [0.3.0] - 2026-06-30 + +### Overview + +This release integrates **Clerk** as the authentication provider for `apps/web`, +wired directly into the Convex real-time backend via `ConvexProviderWithClerk`. +Authentication is enforced at both the UI layer (Next.js 16 proxy middleware) and +the backend layer (Convex mutation identity checks). + +--- + +### Added + +#### Clerk authentication — `apps/web` + +- **`@clerk/nextjs ^7.5.9`** added to `apps/web` dependencies +- **`app/layout.tsx`** — `ClerkProvider` wraps the entire application, providing + Clerk session context to all pages and components +- **`proxy.ts`** (Next.js 16 middleware filename) — `clerkMiddleware()` protects all + routes by default; public routes `/sign-in(.*)` and `/sign-up(.*)` are exempt +- **`app/(auth)/layout.tsx`** — centered layout for all auth pages +- **`app/(auth)/sign-in/[[...sign-in]]/page.tsx`** — Clerk hosted `` component + with catch-all routing for multi-step sign-in flows +- **`app/(auth)/sign-up/[[...sign-up]]/page.tsx`** — Clerk hosted `` component + with catch-all routing for multi-step sign-up flows +- **`app/page.tsx`** — `` / `` guards from Convex; + authenticated view shows `` and the Add user mutation button; + unauthenticated view shows `` + +#### Convex + Clerk session bridging + +- **`components/theme-provider.tsx`** — replaced bare `ConvexProvider` with + `ConvexProviderWithClerk` (from `convex/react-clerk`), passing `useAuth` from + `@clerk/nextjs` so Convex automatically includes the active Clerk JWT in all + function calls. Removed dead `ThemeHotkey`, `isTypingTarget`, `NextThemesProvider`, + and `useTheme` code that was leftover from the previous provider setup. + +#### Convex backend auth hardening — `packages/backend` + +- **`convex/auth.config.ts`** — Clerk JWT provider configuration; reads + `CLERK_JWT_ISSUER_DOMAIN` from the Convex Dashboard environment. Uses + `/// ` because this file runs in Node.js (Convex CLI), + not the V8 function isolate runtime. +- **`convex/users.ts`** — `add` mutation now calls `ctx.auth.getUserIdentity()` and + throws `"Not Authenticated"` for unauthenticated callers, preventing anonymous + writes to the users table. +- **`package.json`** — added `@types/node ^20.19.41` dev dependency + +--- + +### Fixed + +- **`packages/backend/tsconfig.json`** — removed deprecated `baseUrl` compiler option. + In TypeScript 5+, `paths` does not require `baseUrl` to be set; the option was + flagged as deprecated in TS 5.0 and will stop functioning in TS 7.0. + +--- + +### Changed + +- **`apps/widget/components/theme-provider.tsx`** — removed dead `ThemeHotkey`, + `isTypingTarget`, `NextThemesProvider`, and `useTheme` code, matching the cleanup + done in `apps/web`. + +#### CI/tooling + +- `ci.yml`, `release.yml`, `codeql.yml` — `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` added + to the build step environment, reading from the `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` + GitHub repository variable. This prevents `ClerkProvider` from throwing during + `next build` in CI environments where `.env.local` is absent. + +--- + +### Technical Decisions + +- **`ConvexProviderWithClerk` over manual token injection** — Convex's official Clerk + integration handles token refresh, expiry, and re-auth automatically. Manual token + passing would require re-implementing this logic. +- **Middleware-level route protection** — protecting routes at the `proxy.ts` layer + means unauthenticated users are redirected before any page code runs, not just + hidden by client-side conditionals. +- **`ctx.auth.getUserIdentity()` in mutations** — server-side auth checks are the last + line of defence; even if middleware is bypassed, mutations reject unauthenticated calls. +- **`proxy.ts` (not `middleware.ts`)** — Next.js 16 renamed the middleware file from + `middleware.ts` to `proxy.ts`. The code API is identical; only the filename changed. + +--- + ## [0.2.0] - 2026-06-29 ### Overview @@ -249,7 +337,8 @@ Initial release of **Echo** — an enterprise-grade full-stack monorepo platform --- -[Unreleased]: https://github.com/RISHII7/echo/compare/v0.2.0...HEAD +[Unreleased]: https://github.com/RISHII7/echo/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/RISHII7/echo/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/RISHII7/echo/compare/v0.1.1...v0.2.0 [0.1.1]: https://github.com/RISHII7/echo/compare/v0.1.0...v0.1.1 [0.1.0]: https://github.com/RISHII7/echo/releases/tag/v0.1.0 diff --git a/README.md b/README.md index 13d3642..00f7308 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ [![Turborepo](https://img.shields.io/badge/Turborepo-2-EF4444?logo=turborepo)](https://turbo.build) [![Tailwind CSS](https://img.shields.io/badge/Tailwind-4-38BDF8?logo=tailwindcss)](https://tailwindcss.com) [![Convex](https://img.shields.io/badge/Convex-backend-EE342F?logo=convex)](https://convex.dev) +[![Clerk](https://img.shields.io/badge/Clerk-auth-6C47FF?logo=clerk)](https://clerk.com) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [Report Bug](https://github.com/RISHII7/echo/issues/new?template=bug_report.yml) · [Request Feature](https://github.com/RISHII7/echo/issues/new?template=feature_request.yml) · [Documentation](https://github.com/RISHII7/echo/wiki) @@ -57,7 +58,8 @@ Echo is a production-ready, full-stack monorepo platform engineered for enterpri ## Features - **Monorepo Architecture** — Turborepo with remote caching, task pipelines, and workspace dependency graph -- **Real-Time Backend** — Convex reactive database with live queries and server mutations +- **Authentication** — Clerk with hosted sign-in/sign-up UI, session management, and middleware-level route protection +- **Real-Time Backend** — Convex reactive database with live queries and server mutations, bridged to Clerk sessions - **Type Safety** — End-to-end TypeScript with strict mode and generated API types across all packages - **Design System** — Shared UI component library built on shadcn/ui and Radix primitives - **Performance** — Next.js Turbopack, React Server Components, and Tailwind CSS v4 @@ -96,6 +98,7 @@ The monorepo uses a **workspace dependency graph** where apps consume packages, | UI Library | React 19 | | Styling | Tailwind CSS 4 | | Components | shadcn/ui + Radix UI | +| Auth | Clerk | | Backend | Convex (real-time DB) | | Icons | Lucide React | | Monorepo | Turborepo 2 | @@ -133,20 +136,30 @@ pnpm dev ### Environment Variables -Create `apps/web/.env.local` and `apps/widget/.env.local` with the following: +Create `apps/web/.env.local`: -| Variable | Description | Required | -| ------------------------ | --------------------------------- | -------- | -| `NEXT_PUBLIC_CONVEX_URL` | Convex deployment URL | Yes | -| `NEXT_PUBLIC_APP_URL` | Public URL of the web application | No | +| Variable | Description | Required | +| ----------------------------------- | ------------------------------------- | -------- | +| `NEXT_PUBLIC_CONVEX_URL` | Convex deployment URL | Yes | +| `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Clerk publishable key (`pk_test_...`) | Yes | +| `CLERK_SECRET_KEY` | Clerk secret key (`sk_test_...`) | Yes | -To get your `NEXT_PUBLIC_CONVEX_URL`, run `pnpm --filter backend dev` and copy the deployment URL from the Convex dashboard, or from `packages/backend/.env.local` after the first `convex dev` run. +Create `packages/backend/.env.local` (auto-generated by `convex dev`): + +| Variable | Description | Required | +| ------------------------- | ------------------------------------ | -------- | +| `CONVEX_DEPLOYMENT` | Convex deployment identifier | Yes | +| `CLERK_JWT_ISSUER_DOMAIN` | Clerk JWT issuer URL for Convex auth | Yes | ```bash # apps/web/.env.local NEXT_PUBLIC_CONVEX_URL=https://.convex.cloud +NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... +CLERK_SECRET_KEY=sk_test_... ``` +Get your Clerk keys from the [Clerk Dashboard](https://dashboard.clerk.com) → **API Keys**. + --- ## Development diff --git a/apps/web/app/(auth)/layout.tsx b/apps/web/app/(auth)/layout.tsx new file mode 100644 index 0000000..5e74a56 --- /dev/null +++ b/apps/web/app/(auth)/layout.tsx @@ -0,0 +1,9 @@ +const Layout = ({ children }: { children: React.ReactNode }) => { + return ( +
+ {children} +
+ ) +} + +export default Layout diff --git a/apps/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/apps/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx new file mode 100644 index 0000000..4602981 --- /dev/null +++ b/apps/web/app/(auth)/sign-in/[[...sign-in]]/page.tsx @@ -0,0 +1,7 @@ +import { SignIn } from "@clerk/nextjs" + +const Page = () => { + return +} + +export default Page diff --git a/apps/web/app/(auth)/sign-up/[[...sign-up]]/page.tsx b/apps/web/app/(auth)/sign-up/[[...sign-up]]/page.tsx new file mode 100644 index 0000000..2c0d33c --- /dev/null +++ b/apps/web/app/(auth)/sign-up/[[...sign-up]]/page.tsx @@ -0,0 +1,7 @@ +import { SignUp } from "@clerk/nextjs" + +const Page = () => { + return +} + +export default Page diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 9863fc1..f414c59 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,9 +1,12 @@ +import { ClerkProvider } from "@clerk/nextjs" import { Geist, Geist_Mono } from "next/font/google" -import "@workspace/ui/globals.css" -import { ThemeProvider } from "@/components/theme-provider" import { cn } from "@workspace/ui/lib/utils" +import { ThemeProvider } from "@/components/theme-provider" + +import "@workspace/ui/globals.css" + const geist = Geist({ subsets: ["latin"], variable: "--font-sans" }) const fontMono = Geist_Mono({ @@ -28,7 +31,9 @@ export default function RootLayout({ )} > - {children} + + {children} + ) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index dcb5e26..1d6106d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,19 +1,34 @@ "use client" -import { useMutation, useQuery } from "convex/react" +import { + useMutation, + useQuery, + Authenticated, + Unauthenticated, +} from "convex/react" import { api } from "@workspace/backend/_generated/api" import { Button } from "@workspace/ui/components/button" +import { SignInButton, UserButton } from "@clerk/nextjs" export default function Page() { const users = useQuery(api.users.getMany) const addUser = useMutation(api.users.add) return ( -
-

apps/web

- -
- {JSON.stringify(users, null, 2)} -
-
+ <> + +
+

apps/web

+ + +
+ {JSON.stringify(users, null, 2)} +
+
+
+ +

Must be signed In

+ Sign In! +
+ ) } diff --git a/apps/web/components/theme-provider.tsx b/apps/web/components/theme-provider.tsx index d37aeef..582977b 100644 --- a/apps/web/components/theme-provider.tsx +++ b/apps/web/components/theme-provider.tsx @@ -1,63 +1,22 @@ "use client" -import * as React from "react" -import { ConvexProvider, ConvexReactClient } from "convex/react" -import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes" +import { ConvexReactClient } from "convex/react" +import { ConvexProviderWithClerk } from "convex/react-clerk" -const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL || "") +import { useAuth } from "@clerk/nextjs" -function ThemeProvider({ - children, - ...props -}: React.ComponentProps) { - return {children} +if (!process.env.NEXT_PUBLIC_CONVEX_URL) { + throw new Error("Missing NEXT_PUBLIC_CONVEX_URL in your .env file") } -function isTypingTarget(target: EventTarget | null) { - if (!(target instanceof HTMLElement)) { - return false - } +const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL) +function ThemeProvider({ children }: { children: React.ReactNode }) { return ( - target.isContentEditable || - target.tagName === "INPUT" || - target.tagName === "TEXTAREA" || - target.tagName === "SELECT" + + {children} + ) } -function ThemeHotkey() { - const { resolvedTheme, setTheme } = useTheme() - - React.useEffect(() => { - function onKeyDown(event: KeyboardEvent) { - if (event.defaultPrevented || event.repeat) { - return - } - - if (event.metaKey || event.ctrlKey || event.altKey) { - return - } - - if (event.key.toLowerCase() !== "d") { - return - } - - if (isTypingTarget(event.target)) { - return - } - - setTheme(resolvedTheme === "dark" ? "light" : "dark") - } - - window.addEventListener("keydown", onKeyDown) - - return () => { - window.removeEventListener("keydown", onKeyDown) - } - }, [resolvedTheme, setTheme]) - - return null -} - export { ThemeProvider } diff --git a/apps/web/package.json b/apps/web/package.json index 3d96e5f..919502a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,6 +12,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@clerk/nextjs": "^7.5.9", "@workspace/backend": "workspace:*", "@workspace/math": "workspace:*", "@workspace/ui": "workspace:*", diff --git a/apps/web/proxy.ts b/apps/web/proxy.ts new file mode 100644 index 0000000..c27dc35 --- /dev/null +++ b/apps/web/proxy.ts @@ -0,0 +1,18 @@ +import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server" + +const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"]) + +export default clerkMiddleware(async (auth, req) => { + if (!isPublicRoute(req)) { + await auth.protect() + } +}) + +export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", + // Always run for API routes + "/(api|trpc)(.*)", + ], +} diff --git a/apps/widget/components/theme-provider.tsx b/apps/widget/components/theme-provider.tsx index 371ac85..f5ddd5c 100644 --- a/apps/widget/components/theme-provider.tsx +++ b/apps/widget/components/theme-provider.tsx @@ -1,63 +1,11 @@ "use client" -import * as React from "react" -import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes" import { ConvexReactClient, ConvexProvider } from "convex/react" const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL || "") -function ThemeProvider({ - children, - ...props -}: React.ComponentProps) { +function ThemeProvider({ children }: { children: React.ReactNode }) { return {children} } -function isTypingTarget(target: EventTarget | null) { - if (!(target instanceof HTMLElement)) { - return false - } - - return ( - target.isContentEditable || - target.tagName === "INPUT" || - target.tagName === "TEXTAREA" || - target.tagName === "SELECT" - ) -} - -function ThemeHotkey() { - const { resolvedTheme, setTheme } = useTheme() - - React.useEffect(() => { - function onKeyDown(event: KeyboardEvent) { - if (event.defaultPrevented || event.repeat) { - return - } - - if (event.metaKey || event.ctrlKey || event.altKey) { - return - } - - if (event.key.toLowerCase() !== "d") { - return - } - - if (isTypingTarget(event.target)) { - return - } - - setTheme(resolvedTheme === "dark" ? "light" : "dark") - } - - window.addEventListener("keydown", onKeyDown) - - return () => { - window.removeEventListener("keydown", onKeyDown) - } - }, [resolvedTheme, setTheme]) - - return null -} - export { ThemeProvider } diff --git a/packages/backend/convex/auth.config.ts b/packages/backend/convex/auth.config.ts new file mode 100644 index 0000000..fe8f7af --- /dev/null +++ b/packages/backend/convex/auth.config.ts @@ -0,0 +1,14 @@ +/// + +export default { + providers: [ + { + // Replace with your own Clerk Issuer URL from your "convex" JWT template + // or with `process.env.CLERK_JWT_ISSUER_DOMAIN` + // and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard + // See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances + domain: process.env.CLERK_JWT_ISSUER_DOMAIN, + applicationID: "convex", + }, + ], +} diff --git a/packages/backend/convex/users.ts b/packages/backend/convex/users.ts index 0111a28..c7b05e3 100644 --- a/packages/backend/convex/users.ts +++ b/packages/backend/convex/users.ts @@ -12,6 +12,10 @@ export const getMany = query({ export const add = mutation({ args: {}, handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity() + if (identity === null) { + throw new Error("Not Authenticated") + } const userId = await ctx.db.insert("users", { name: "RISHII", }) diff --git a/packages/backend/package.json b/packages/backend/package.json index 039f871..3a7a5c1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -9,6 +9,7 @@ "./convex": "./convex/*.ts" }, "devDependencies": { + "@types/node": "^20.19.41", "@workspace/typescript-config": "workspace:*", "typescript": "latest" }, diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 84b35d0..cc259d9 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "@workspace/typescript-config/base.json", "compilerOptions": { - "baseUrl": ".", "paths": { "@workspace/backend/*": ["./convex/*"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4f915b..9d05bae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: apps/web: dependencies: + '@clerk/nextjs': + specifier: ^7.5.9 + version: 7.5.9(next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@workspace/backend': specifier: workspace:* version: link:../../packages/backend @@ -40,7 +43,7 @@ importers: version: link:../../packages/ui convex: specifier: ^1.42.0 - version: 1.42.0(react@19.2.4) + version: 1.42.0(@clerk/react@6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) lucide-react: specifier: ^1.21.0 version: 1.21.0(react@19.2.4) @@ -95,7 +98,7 @@ importers: version: link:../../packages/ui convex: specifier: ^1.42.0 - version: 1.42.0(react@19.2.4) + version: 1.42.0(@clerk/react@6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) lucide-react: specifier: ^1.21.0 version: 1.21.0(react@19.2.4) @@ -141,8 +144,11 @@ importers: dependencies: convex: specifier: ^1.42.0 - version: 1.42.0(react@19.2.4) + version: 1.42.0(@clerk/react@6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) devDependencies: + '@types/node': + specifier: ^20.19.41 + version: 20.19.41 '@workspace/typescript-config': specifier: workspace:* version: link:../typescript-config @@ -436,6 +442,37 @@ packages: '@types/react': optional: true + '@clerk/backend@3.8.4': + resolution: {integrity: sha512-3G+kEu8kalqQokJ/n0IynhBh2i6TtiilRzuAg5UWMburkK658/elrG0Uqsapd5yKhmkNuvizHwOPWwKAMxP2EQ==} + engines: {node: '>=20.9.0'} + + '@clerk/nextjs@7.5.9': + resolution: {integrity: sha512-5QzCWT4DpEcTK7/GgkSanuclXaWE9KbvmbIgoxwN5eMSOPFgkcrcSvR106OuDoY27Yo9I+z5xWDUdb+DJBMtuA==} + engines: {node: '>=20.9.0'} + peerDependencies: + next: ^15.2.8 || ^15.3.8 || ^15.4.10 || ^15.5.9 || ^15.6.0-0 || ^16.0.10 || ^16.1.0-0 + react: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + react-dom: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + + '@clerk/react@6.11.1': + resolution: {integrity: sha512-iEWckisZa/V3siCFlvGTNg7Yj+kndRx7HKBZ/QMSd6uEU9kd2tZ8Ymvd4GFPCpClwcki4h7jC750FujN656Jqg==} + engines: {node: '>=20.9.0'} + peerDependencies: + react: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + react-dom: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + + '@clerk/shared@4.22.0': + resolution: {integrity: sha512-GZ56kzUB2UBb8MCF+Eo8WIey+W4RP1tAkyOL+geiHxrQxJlUcgD+xalY2YPJLH8WVFwtOnfIl8KPEo0M0e/DRg==} + engines: {node: '>=20.9.0'} + peerDependencies: + react: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + react-dom: ^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + '@dotenvx/dotenvx@1.75.1': resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==} hasBin: true @@ -1227,6 +1264,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1322,6 +1362,9 @@ packages: '@tailwindcss/postcss@4.3.0': resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==} + '@tanstack/query-core@5.101.2': + resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} + '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} @@ -1789,6 +1832,10 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2026,6 +2073,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.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} @@ -2145,6 +2195,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2442,6 +2495,10 @@ packages: jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-cookie@3.0.7: + resolution: {integrity: sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==} + engines: {node: '>=20'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3083,6 +3140,9 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3153,6 +3213,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -3688,6 +3751,43 @@ snapshots: optionalDependencies: '@types/react': 19.2.15 + '@clerk/backend@3.8.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@clerk/shared': 4.22.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + standardwebhooks: 1.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - react + - react-dom + + '@clerk/nextjs@7.5.9(next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@clerk/backend': 3.8.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@clerk/react': 6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@clerk/shared': 4.22.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + server-only: 0.0.1 + tslib: 2.8.1 + + '@clerk/react@6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@clerk/shared': 4.22.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tslib: 2.8.1 + + '@clerk/shared@4.22.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@tanstack/query-core': 5.101.2 + dequal: 2.0.3 + glob-to-regexp: 0.4.1 + js-cookie: 3.0.7 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + '@dotenvx/dotenvx@1.75.1': dependencies: '@dotenvx/primitives': 0.8.0 @@ -4262,6 +4362,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@stablelib/base64@1.0.1': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -4335,6 +4437,8 @@ snapshots: postcss: 8.5.15 tailwindcss: 4.3.0 + '@tanstack/query-core@5.101.2': {} + '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 @@ -4717,12 +4821,13 @@ snapshots: convert-source-map@2.0.0: {} - convex@1.42.0(react@19.2.4): + convex@1.42.0(@clerk/react@6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): dependencies: esbuild: 0.27.0 prettier: 3.8.3 ws: 8.21.0 optionalDependencies: + '@clerk/react': 6.11.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 transitivePeerDependencies: - bufferutil @@ -4813,6 +4918,8 @@ snapshots: depd@2.0.0: {} + dequal@2.0.3: {} + detect-libc@2.1.2: {} diff@8.0.4: {} @@ -5238,6 +5345,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-sha256@1.3.0: {} + fast-uri@3.1.2: {} fastq@1.20.1: @@ -5363,6 +5472,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + globals@14.0.0: {} globals@17.6.0: {} @@ -5614,6 +5725,8 @@ snapshots: jose@6.2.3: {} + js-cookie@3.0.7: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -6190,6 +6303,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-only@0.0.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6338,6 +6453,11 @@ snapshots: source-map@0.6.1: {} + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + statuses@2.0.2: {} stdin-discarder@0.2.2: {}