From 46816cf6e90fa6ba5caceda58cfca0c3b77e5894 Mon Sep 17 00:00:00 2001 From: Jack Herrington Date: Tue, 23 Sep 2025 07:10:57 -0700 Subject: [PATCH] Revert "refactor: migrate to `@convex-dev/better-auth` version `0.8` (#485)" This reverts commit 995d1abfaabf94a8eac783fcc4de32c84003d6f5. --- .prettierignore | 4 - agents/tasks/tanstack-com-task-list.md | 44 +- convex/_generated/api.d.ts | 3486 ++---------------------- convex/auth.config.ts | 8 +- convex/auth.ts | 116 +- convex/http.ts | 5 +- convex/schema.ts | 2 + convex/stats.ts | 3 +- package.json | 9 +- pnpm-lock.yaml | 137 +- src/components/Navbar.tsx | 106 +- src/router.tsx | 5 +- src/routes/__root.tsx | 43 +- src/routes/_libraries/account.tsx | 14 +- src/routes/api/auth/$.ts | 2 +- src/server/auth.server.ts | 41 + src/utils/gh-sponsor-meta.json | 2 +- 17 files changed, 428 insertions(+), 3599 deletions(-) create mode 100644 src/server/auth.server.ts diff --git a/.prettierignore b/.prettierignore index 16706c2d6..84d40a821 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,7 +6,3 @@ routeTree.gen.ts convex/_generated convex/README.md src/blog/tanstack-db-0.1-the-embedded-client-database-for-tanstack-query.md -.content-collections -dist/ -.tanstack/ -.output/ \ No newline at end of file diff --git a/agents/tasks/tanstack-com-task-list.md b/agents/tasks/tanstack-com-task-list.md index 3a9509a8c..4709bff31 100644 --- a/agents/tasks/tanstack-com-task-list.md +++ b/agents/tasks/tanstack-com-task-list.md @@ -8,27 +8,22 @@ - Links: PRs, issues, routes, components ### How to use this file - - Update status/notes as tasks progress. Keep routes/components and data sources referenced so any agent can continue seamlessly. - Prefer reusing existing components and content models referenced below. --- ## 1. Metrics & Market Leadership Signals - **Goal:** Visible proof of dominance and growth. ### Audit snapshot - - Homepage metrics: `OpenSourceStats` counters present on homepage (`src/routes/_libraries/index.tsx` uses `OpenSourceStats`). Partial. - "Trusted By": Component exists as text marquee (`src/components/TrustedByMarquee.tsx`). Not on homepage yet; currently used on some library pages (e.g. `src/routes/_libraries/table.$version.index.tsx`). Partial. - NPM stats: Extensive interactive page exists at `src/routes/stats/npm/index.tsx` with charts and comparisons. Done (separate page). - Backend metrics: `convex/stats.ts` + `@erquhart/convex-oss-stats` provides GitHub/NPM org metrics; `OpenSourceStats.tsx` consumes `api.stats.getGithubOwner`, `api.stats.getNpmOrg`. Done for aggregate; per-library not yet surfaced. ### Tasks - - [ ] Implement “Trusted By” on homepage - - Status: Backlog - Notes: - Reuse `TrustedByMarquee` but upgrade to support logos + links + tooltip proof. @@ -40,10 +35,9 @@ - Renders without CLS, loops smoothly, accessible (ARIA, alt text). Logos swap dark/light. - All entries have a proof link; no unverified brands. - Links: `src/components/TrustedByMarquee.tsx`, `src/routes/_libraries/index.tsx`. - - Owner: + - Owner: - [ ] Add Real-Time Metrics Counters (per-library + org rollup) - - Status: Partial (org rollup live via `OpenSourceStats`) - Notes: - Extend counters to per-library pages using existing Convex endpoints or add repo-level endpoints via `convex-oss-stats` if needed. @@ -61,9 +55,9 @@ - Notes: - Route: `src/routes/state-of-tanstack.tsx`. - Include growth charts (npm downloads: reuse `NpmStatsChart.tsx` or embed portions of `stats/npm`), GitHub stars, contributors, dependents (available via Convex aggregation already powering `OpenSourceStats`). - - Community stats: Discord members (needs server function), newsletter subscribers (manual or vendor API), X/Twitter followers (manual or API), repository contributors (Convex or GitHub GraphQL on server). - - Ecosystem counts: partners (derive from `src/utils/partners.tsx`), plugins/tools (manual list or content collection). - - CTA to GitHub org. + - Community stats: Discord members (needs server function), newsletter subscribers (manual or vendor API), X/Twitter followers (manual or API), repository contributors (Convex or GitHub GraphQL on server). + - Ecosystem counts: partners (derive from `src/utils/partners.tsx`), plugins/tools (manual list or content collection). + - CTA to GitHub org. - Acceptance: - Page loads instantly with cached metrics; charts are responsive and accessible. - Sources and last-updated timestamps shown. @@ -71,7 +65,6 @@ - Owner: ### Tech/context - - Data: `@erquhart/convex-oss-stats` via `convex/stats.ts` (org-level GitHub star/contributor/dependent counts, npm downloads). Consider adding per-repo endpoints if needed. - Secrets: Configure any tokens via Netlify/Convex env; never expose client-side. - Accessibility: Ensure counters/animations are readable and respect `prefers-reduced-motion`. @@ -79,19 +72,15 @@ --- ## 2. Founder & Team Story - **Goal:** Frame the team as visionary and credible. ### Audit snapshot - - Ethos page exists: `src/routes/_libraries/ethos.tsx` (narrative and positioning). - Maintainers directory page exists: `src/routes/_libraries/maintainers.tsx` with `MaintainerCard` variants and filters; bios sourced from `src/libraries/maintainers`. - No dedicated "About" route; no speaking engagements index; no curated endorsements/tweets. ### Tasks - - [ ] Redesign/Create “About” page - - Status: Backlog - Notes: - Route: `src/routes/about.tsx`. @@ -101,7 +90,6 @@ - Links: `src/components/MaintainerCard.tsx`, `src/routes/_libraries/maintainers.tsx`. - [ ] Speaking Engagements section - - Status: Backlog - Notes: - Add to About or standalone `src/routes/speaking.tsx`. @@ -118,25 +106,22 @@ - Acceptance: Renders endorsements with attribution and embedded tweets with proper theming. ### Tech/context - - Reuse `MaintainerCard` and existing images in `src/images/`. - Avoid fetching social embeds at build if rate-limited; hydrate on client or cache server-side. --- -## 4. Commercial Hooks + +## 4. Commercial Hooks **Goal:** Show monetizable pathways. ### Audit snapshot - - Enterprise/Support: `src/routes/_libraries/paid-support.tsx` exists with HubSpot script and CTAs. Partial substitute for "Enterprise" page. - No dedicated Partner Program page. ### Tasks - - [ ] “Enterprise” page - - Status: Partial - Notes: - Option 1: Rename and expand `paid-support` into `enterprise` (route alias + updated copy) while keeping legacy route. @@ -152,20 +137,18 @@ - Link to Partners page. - Acceptance: Published page with clear application CTA. + + --- ## 5. Future Vision Page - **Goal:** Show long-term upside. ### Audit snapshot - - No public roadmap found; ethos narrative exists but not a vision statement page. ### Tasks - - [ ] Public Roadmap page - - Status: Backlog - Notes: - Route: `src/routes/roadmap.tsx`. @@ -184,17 +167,13 @@ --- ## 6. Media & Momentum - **Goal:** Make hype and credibility easy to digest. ### Audit snapshot - - No dedicated media kit, in-the-news, or social proof feeds found. ### Tasks - - [ ] Press/Media Kit page - - Status: Backlog - Notes: - Route: `src/routes/media-kit.tsx`. @@ -203,7 +182,6 @@ - Acceptance: Page provides direct downloads and usage rules. - [ ] In the News page - - Status: Backlog - Notes: - Route: `src/routes/news.tsx`. @@ -221,7 +199,6 @@ --- ### Shared implementation notes - - Routing: New pages should be added under `src/routes/*` using TanStack Start conventions; update nav/footers as needed. - Data placement: Prefer `src/data/*.ts` (typed) or `content/*.(json|yaml)` for editorial lists. Avoid hardcoding in components unless small. - Theming: Provide dark/light logo variants; `public/` is ideal for static assets. @@ -231,12 +208,10 @@ - Analytics: Add outbound link tracking if available (future). ### Potential blockers - - External API limits (GitHub GraphQL, Discord member count, X/Twitter API). Prefer server-side fetch with caching or public embed widgets. - Legal/branding approvals for “Trusted By” logos—require proof links. ### Quick links to relevant code - - Homepage: `src/routes/_libraries/index.tsx` - Metrics: `src/components/OpenSourceStats.tsx`, `convex/stats.ts`, `src/components/NpmStatsChart.tsx`, `src/routes/stats/npm/index.tsx` - Trusted By: `src/components/TrustedByMarquee.tsx` @@ -244,9 +219,8 @@ - SEO helper: `src/utils/seo` ### Ownership & tracking - - For each task above, fill in: - Owner: - Issue/PR links: - Status: - - Next step: + - Next step: \ No newline at end of file diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 17a88f5ff..1c6f029a4 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -257,3263 +257,6 @@ export declare const components: { }; }; betterAuth: { - adapter: { - create: FunctionReference< - "mutation", - "internal", - { - input: - | { - data: { - createdAt: number; - displayUsername?: null | string; - email: string; - emailVerified: boolean; - image?: null | string; - isAnonymous?: null | boolean; - name: string; - phoneNumber?: null | string; - phoneNumberVerified?: null | boolean; - twoFactorEnabled?: null | boolean; - updatedAt: number; - userId?: null | string; - username?: null | string; - }; - model: "user"; - } - | { - data: { - createdAt: number; - expiresAt: number; - ipAddress?: null | string; - token: string; - updatedAt: number; - userAgent?: null | string; - userId: string; - }; - model: "session"; - } - | { - data: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - accountId: string; - createdAt: number; - idToken?: null | string; - password?: null | string; - providerId: string; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scope?: null | string; - updatedAt: number; - userId: string; - }; - model: "account"; - } - | { - data: { - createdAt: number; - expiresAt: number; - identifier: string; - updatedAt: number; - value: string; - }; - model: "verification"; - } - | { - data: { backupCodes: string; secret: string; userId: string }; - model: "twoFactor"; - } - | { - data: { - aaguid?: null | string; - backedUp: boolean; - counter: number; - createdAt?: null | number; - credentialID: string; - deviceType: string; - name?: null | string; - publicKey: string; - transports?: null | string; - userId: string; - }; - model: "passkey"; - } - | { - data: { - clientId?: null | string; - clientSecret?: null | string; - createdAt?: null | number; - disabled?: null | boolean; - icon?: null | string; - metadata?: null | string; - name?: null | string; - redirectURLs?: null | string; - type?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - model: "oauthApplication"; - } - | { - data: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - clientId?: null | string; - createdAt?: null | number; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - model: "oauthAccessToken"; - } - | { - data: { - clientId?: null | string; - consentGiven?: null | boolean; - createdAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - model: "oauthConsent"; - } - | { - data: { - createdAt: number; - name: string; - organizationId: string; - updatedAt?: null | number; - }; - model: "team"; - } - | { - data: { - createdAt?: null | number; - teamId: string; - userId: string; - }; - model: "teamMember"; - } - | { - data: { - createdAt: number; - logo?: null | string; - metadata?: null | string; - name: string; - slug?: null | string; - }; - model: "organization"; - } - | { - data: { - createdAt: number; - organizationId: string; - role: string; - userId: string; - }; - model: "member"; - } - | { - data: { - email: string; - expiresAt: number; - inviterId: string; - organizationId: string; - role?: null | string; - status: string; - teamId?: null | string; - }; - model: "invitation"; - } - | { - data: { - domain: string; - issuer: string; - oidcConfig?: null | string; - organizationId?: null | string; - providerId: string; - samlConfig?: null | string; - userId?: null | string; - }; - model: "ssoProvider"; - } - | { - data: { - createdAt: number; - privateKey: string; - publicKey: string; - }; - model: "jwks"; - } - | { - data: { - cancelAtPeriodEnd?: null | boolean; - periodEnd?: null | number; - periodStart?: null | number; - plan: string; - referenceId: string; - seats?: null | number; - status?: null | string; - stripeCustomerId?: null | string; - stripeSubscriptionId?: null | string; - trialEnd?: null | number; - trialStart?: null | number; - }; - model: "subscription"; - } - | { - data: { - address: string; - chainId: number; - createdAt: number; - isPrimary?: null | boolean; - userId: string; - }; - model: "walletAddress"; - } - | { - data: { - count?: null | number; - key?: null | string; - lastRequest?: null | number; - }; - model: "rateLimit"; - } - | { - data: { count: number; key: string; lastRequest: number }; - model: "ratelimit"; - }; - onCreateHandle?: string; - select?: Array; - }, - any - >; - deleteMany: FunctionReference< - "mutation", - "internal", - { - input: - | { - model: "user"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "email" - | "emailVerified" - | "image" - | "createdAt" - | "updatedAt" - | "twoFactorEnabled" - | "isAnonymous" - | "username" - | "displayUsername" - | "phoneNumber" - | "phoneNumberVerified" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "session"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "expiresAt" - | "token" - | "createdAt" - | "updatedAt" - | "ipAddress" - | "userAgent" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "account"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accountId" - | "providerId" - | "userId" - | "accessToken" - | "refreshToken" - | "idToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "scope" - | "password" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "verification"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "identifier" - | "value" - | "expiresAt" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "twoFactor"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "secret" | "backupCodes" | "userId" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "passkey"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "publicKey" - | "userId" - | "credentialID" - | "counter" - | "deviceType" - | "backedUp" - | "transports" - | "createdAt" - | "aaguid" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthApplication"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "icon" - | "metadata" - | "clientId" - | "clientSecret" - | "redirectURLs" - | "type" - | "disabled" - | "userId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthAccessToken"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accessToken" - | "refreshToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthConsent"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "consentGiven" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "team"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "organizationId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "teamMember"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "teamId" | "userId" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "organization"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "slug" - | "logo" - | "createdAt" - | "metadata" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "member"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "userId" - | "role" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "invitation"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "email" - | "role" - | "teamId" - | "status" - | "expiresAt" - | "inviterId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ssoProvider"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "issuer" - | "oidcConfig" - | "samlConfig" - | "userId" - | "providerId" - | "organizationId" - | "domain" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "jwks"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "publicKey" | "privateKey" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "subscription"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "plan" - | "referenceId" - | "stripeCustomerId" - | "stripeSubscriptionId" - | "status" - | "periodStart" - | "periodEnd" - | "trialStart" - | "trialEnd" - | "cancelAtPeriodEnd" - | "seats" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "walletAddress"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "userId" - | "address" - | "chainId" - | "isPrimary" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "rateLimit"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ratelimit"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }; - onDeleteHandle?: string; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - }, - any - >; - deleteOne: FunctionReference< - "mutation", - "internal", - { - input: - | { - model: "user"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "email" - | "emailVerified" - | "image" - | "createdAt" - | "updatedAt" - | "twoFactorEnabled" - | "isAnonymous" - | "username" - | "displayUsername" - | "phoneNumber" - | "phoneNumberVerified" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "session"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "expiresAt" - | "token" - | "createdAt" - | "updatedAt" - | "ipAddress" - | "userAgent" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "account"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accountId" - | "providerId" - | "userId" - | "accessToken" - | "refreshToken" - | "idToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "scope" - | "password" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "verification"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "identifier" - | "value" - | "expiresAt" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "twoFactor"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "secret" | "backupCodes" | "userId" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "passkey"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "publicKey" - | "userId" - | "credentialID" - | "counter" - | "deviceType" - | "backedUp" - | "transports" - | "createdAt" - | "aaguid" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthApplication"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "icon" - | "metadata" - | "clientId" - | "clientSecret" - | "redirectURLs" - | "type" - | "disabled" - | "userId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthAccessToken"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accessToken" - | "refreshToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthConsent"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "consentGiven" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "team"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "organizationId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "teamMember"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "teamId" | "userId" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "organization"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "slug" - | "logo" - | "createdAt" - | "metadata" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "member"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "userId" - | "role" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "invitation"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "email" - | "role" - | "teamId" - | "status" - | "expiresAt" - | "inviterId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ssoProvider"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "issuer" - | "oidcConfig" - | "samlConfig" - | "userId" - | "providerId" - | "organizationId" - | "domain" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "jwks"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "publicKey" | "privateKey" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "subscription"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "plan" - | "referenceId" - | "stripeCustomerId" - | "stripeSubscriptionId" - | "status" - | "periodStart" - | "periodEnd" - | "trialStart" - | "trialEnd" - | "cancelAtPeriodEnd" - | "seats" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "walletAddress"; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "userId" - | "address" - | "chainId" - | "isPrimary" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "rateLimit"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ratelimit"; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }; - onDeleteHandle?: string; - }, - any - >; - findMany: FunctionReference< - "query", - "internal", - { - limit?: number; - model: - | "user" - | "session" - | "account" - | "verification" - | "twoFactor" - | "passkey" - | "oauthApplication" - | "oauthAccessToken" - | "oauthConsent" - | "team" - | "teamMember" - | "organization" - | "member" - | "invitation" - | "ssoProvider" - | "jwks" - | "subscription" - | "walletAddress" - | "rateLimit" - | "ratelimit"; - offset?: number; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - sortBy?: { direction: "asc" | "desc"; field: string }; - where?: Array<{ - connector?: "AND" | "OR"; - field: string; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }, - any - >; - findOne: FunctionReference< - "query", - "internal", - { - model: - | "user" - | "session" - | "account" - | "verification" - | "twoFactor" - | "passkey" - | "oauthApplication" - | "oauthAccessToken" - | "oauthConsent" - | "team" - | "teamMember" - | "organization" - | "member" - | "invitation" - | "ssoProvider" - | "jwks" - | "subscription" - | "walletAddress" - | "rateLimit" - | "ratelimit"; - select?: Array; - where?: Array<{ - connector?: "AND" | "OR"; - field: string; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }, - any - >; - updateMany: FunctionReference< - "mutation", - "internal", - { - input: - | { - model: "user"; - update: { - createdAt?: number; - displayUsername?: null | string; - email?: string; - emailVerified?: boolean; - image?: null | string; - isAnonymous?: null | boolean; - name?: string; - phoneNumber?: null | string; - phoneNumberVerified?: null | boolean; - twoFactorEnabled?: null | boolean; - updatedAt?: number; - userId?: null | string; - username?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "email" - | "emailVerified" - | "image" - | "createdAt" - | "updatedAt" - | "twoFactorEnabled" - | "isAnonymous" - | "username" - | "displayUsername" - | "phoneNumber" - | "phoneNumberVerified" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "session"; - update: { - createdAt?: number; - expiresAt?: number; - ipAddress?: null | string; - token?: string; - updatedAt?: number; - userAgent?: null | string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "expiresAt" - | "token" - | "createdAt" - | "updatedAt" - | "ipAddress" - | "userAgent" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "account"; - update: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - accountId?: string; - createdAt?: number; - idToken?: null | string; - password?: null | string; - providerId?: string; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scope?: null | string; - updatedAt?: number; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accountId" - | "providerId" - | "userId" - | "accessToken" - | "refreshToken" - | "idToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "scope" - | "password" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "verification"; - update: { - createdAt?: number; - expiresAt?: number; - identifier?: string; - updatedAt?: number; - value?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "identifier" - | "value" - | "expiresAt" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "twoFactor"; - update: { - backupCodes?: string; - secret?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "secret" | "backupCodes" | "userId" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "passkey"; - update: { - aaguid?: null | string; - backedUp?: boolean; - counter?: number; - createdAt?: null | number; - credentialID?: string; - deviceType?: string; - name?: null | string; - publicKey?: string; - transports?: null | string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "publicKey" - | "userId" - | "credentialID" - | "counter" - | "deviceType" - | "backedUp" - | "transports" - | "createdAt" - | "aaguid" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthApplication"; - update: { - clientId?: null | string; - clientSecret?: null | string; - createdAt?: null | number; - disabled?: null | boolean; - icon?: null | string; - metadata?: null | string; - name?: null | string; - redirectURLs?: null | string; - type?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "icon" - | "metadata" - | "clientId" - | "clientSecret" - | "redirectURLs" - | "type" - | "disabled" - | "userId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthAccessToken"; - update: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - clientId?: null | string; - createdAt?: null | number; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accessToken" - | "refreshToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthConsent"; - update: { - clientId?: null | string; - consentGiven?: null | boolean; - createdAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "consentGiven" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "team"; - update: { - createdAt?: number; - name?: string; - organizationId?: string; - updatedAt?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "organizationId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "teamMember"; - update: { - createdAt?: null | number; - teamId?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "teamId" | "userId" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "organization"; - update: { - createdAt?: number; - logo?: null | string; - metadata?: null | string; - name?: string; - slug?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "slug" - | "logo" - | "createdAt" - | "metadata" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "member"; - update: { - createdAt?: number; - organizationId?: string; - role?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "userId" - | "role" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "invitation"; - update: { - email?: string; - expiresAt?: number; - inviterId?: string; - organizationId?: string; - role?: null | string; - status?: string; - teamId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "email" - | "role" - | "teamId" - | "status" - | "expiresAt" - | "inviterId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ssoProvider"; - update: { - domain?: string; - issuer?: string; - oidcConfig?: null | string; - organizationId?: null | string; - providerId?: string; - samlConfig?: null | string; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "issuer" - | "oidcConfig" - | "samlConfig" - | "userId" - | "providerId" - | "organizationId" - | "domain" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "jwks"; - update: { - createdAt?: number; - privateKey?: string; - publicKey?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "publicKey" | "privateKey" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "subscription"; - update: { - cancelAtPeriodEnd?: null | boolean; - periodEnd?: null | number; - periodStart?: null | number; - plan?: string; - referenceId?: string; - seats?: null | number; - status?: null | string; - stripeCustomerId?: null | string; - stripeSubscriptionId?: null | string; - trialEnd?: null | number; - trialStart?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "plan" - | "referenceId" - | "stripeCustomerId" - | "stripeSubscriptionId" - | "status" - | "periodStart" - | "periodEnd" - | "trialStart" - | "trialEnd" - | "cancelAtPeriodEnd" - | "seats" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "walletAddress"; - update: { - address?: string; - chainId?: number; - createdAt?: number; - isPrimary?: null | boolean; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "userId" - | "address" - | "chainId" - | "isPrimary" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "rateLimit"; - update: { - count?: null | number; - key?: null | string; - lastRequest?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ratelimit"; - update: { count?: number; key?: string; lastRequest?: number }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }; - onUpdateHandle?: string; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - }, - any - >; - updateOne: FunctionReference< - "mutation", - "internal", - { - input: - | { - model: "user"; - update: { - createdAt?: number; - displayUsername?: null | string; - email?: string; - emailVerified?: boolean; - image?: null | string; - isAnonymous?: null | boolean; - name?: string; - phoneNumber?: null | string; - phoneNumberVerified?: null | boolean; - twoFactorEnabled?: null | boolean; - updatedAt?: number; - userId?: null | string; - username?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "email" - | "emailVerified" - | "image" - | "createdAt" - | "updatedAt" - | "twoFactorEnabled" - | "isAnonymous" - | "username" - | "displayUsername" - | "phoneNumber" - | "phoneNumberVerified" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "session"; - update: { - createdAt?: number; - expiresAt?: number; - ipAddress?: null | string; - token?: string; - updatedAt?: number; - userAgent?: null | string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "expiresAt" - | "token" - | "createdAt" - | "updatedAt" - | "ipAddress" - | "userAgent" - | "userId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "account"; - update: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - accountId?: string; - createdAt?: number; - idToken?: null | string; - password?: null | string; - providerId?: string; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scope?: null | string; - updatedAt?: number; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accountId" - | "providerId" - | "userId" - | "accessToken" - | "refreshToken" - | "idToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "scope" - | "password" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "verification"; - update: { - createdAt?: number; - expiresAt?: number; - identifier?: string; - updatedAt?: number; - value?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "identifier" - | "value" - | "expiresAt" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "twoFactor"; - update: { - backupCodes?: string; - secret?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "secret" | "backupCodes" | "userId" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "passkey"; - update: { - aaguid?: null | string; - backedUp?: boolean; - counter?: number; - createdAt?: null | number; - credentialID?: string; - deviceType?: string; - name?: null | string; - publicKey?: string; - transports?: null | string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "publicKey" - | "userId" - | "credentialID" - | "counter" - | "deviceType" - | "backedUp" - | "transports" - | "createdAt" - | "aaguid" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthApplication"; - update: { - clientId?: null | string; - clientSecret?: null | string; - createdAt?: null | number; - disabled?: null | boolean; - icon?: null | string; - metadata?: null | string; - name?: null | string; - redirectURLs?: null | string; - type?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "icon" - | "metadata" - | "clientId" - | "clientSecret" - | "redirectURLs" - | "type" - | "disabled" - | "userId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthAccessToken"; - update: { - accessToken?: null | string; - accessTokenExpiresAt?: null | number; - clientId?: null | string; - createdAt?: null | number; - refreshToken?: null | string; - refreshTokenExpiresAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "accessToken" - | "refreshToken" - | "accessTokenExpiresAt" - | "refreshTokenExpiresAt" - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "oauthConsent"; - update: { - clientId?: null | string; - consentGiven?: null | boolean; - createdAt?: null | number; - scopes?: null | string; - updatedAt?: null | number; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "clientId" - | "userId" - | "scopes" - | "createdAt" - | "updatedAt" - | "consentGiven" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "team"; - update: { - createdAt?: number; - name?: string; - organizationId?: string; - updatedAt?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "organizationId" - | "createdAt" - | "updatedAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "teamMember"; - update: { - createdAt?: null | number; - teamId?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "teamId" | "userId" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "organization"; - update: { - createdAt?: number; - logo?: null | string; - metadata?: null | string; - name?: string; - slug?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "name" - | "slug" - | "logo" - | "createdAt" - | "metadata" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "member"; - update: { - createdAt?: number; - organizationId?: string; - role?: string; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "userId" - | "role" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "invitation"; - update: { - email?: string; - expiresAt?: number; - inviterId?: string; - organizationId?: string; - role?: null | string; - status?: string; - teamId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "organizationId" - | "email" - | "role" - | "teamId" - | "status" - | "expiresAt" - | "inviterId" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ssoProvider"; - update: { - domain?: string; - issuer?: string; - oidcConfig?: null | string; - organizationId?: null | string; - providerId?: string; - samlConfig?: null | string; - userId?: null | string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "issuer" - | "oidcConfig" - | "samlConfig" - | "userId" - | "providerId" - | "organizationId" - | "domain" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "jwks"; - update: { - createdAt?: number; - privateKey?: string; - publicKey?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "publicKey" | "privateKey" | "createdAt" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "subscription"; - update: { - cancelAtPeriodEnd?: null | boolean; - periodEnd?: null | number; - periodStart?: null | number; - plan?: string; - referenceId?: string; - seats?: null | number; - status?: null | string; - stripeCustomerId?: null | string; - stripeSubscriptionId?: null | string; - trialEnd?: null | number; - trialStart?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "plan" - | "referenceId" - | "stripeCustomerId" - | "stripeSubscriptionId" - | "status" - | "periodStart" - | "periodEnd" - | "trialStart" - | "trialEnd" - | "cancelAtPeriodEnd" - | "seats" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "walletAddress"; - update: { - address?: string; - chainId?: number; - createdAt?: number; - isPrimary?: null | boolean; - userId?: string; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: - | "userId" - | "address" - | "chainId" - | "isPrimary" - | "createdAt" - | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "rateLimit"; - update: { - count?: null | number; - key?: null | string; - lastRequest?: null | number; - }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - } - | { - model: "ratelimit"; - update: { count?: number; key?: string; lastRequest?: number }; - where?: Array<{ - connector?: "AND" | "OR"; - field: "key" | "count" | "lastRequest" | "id"; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; - }; - onUpdateHandle?: string; - }, - any - >; - }; adapterTest: { count: FunctionReference<"query", "internal", any, any>; create: FunctionReference<"mutation", "internal", any, any>; @@ -3521,6 +264,7 @@ export declare const components: { deleteMany: FunctionReference<"mutation", "internal", any, any>; findMany: FunctionReference<"query", "internal", any, any>; findOne: FunctionReference<"query", "internal", any, any>; + isAuthenticated: FunctionReference<"query", "internal", {}, any>; update: FunctionReference<"mutation", "internal", any, any>; updateMany: FunctionReference<"mutation", "internal", any, any>; }; @@ -3532,6 +276,9 @@ export declare const components: { input: | { data: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; createdAt: number; displayUsername?: null | string; email: string; @@ -3541,6 +288,9 @@ export declare const components: { name: string; phoneNumber?: null | string; phoneNumberVerified?: null | boolean; + role?: null | string; + stripeCustomerId?: null | string; + teamId?: null | string; twoFactorEnabled?: null | boolean; updatedAt: number; userId?: null | string; @@ -3550,8 +300,11 @@ export declare const components: { } | { data: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; createdAt: number; expiresAt: number; + impersonatedBy?: null | string; ipAddress?: null | string; token: string; updatedAt: number; @@ -3606,6 +359,31 @@ export declare const components: { }; model: "passkey"; } + | { + data: { + createdAt: number; + enabled?: null | boolean; + expiresAt?: null | number; + key: string; + lastRefillAt?: null | number; + lastRequest?: null | number; + metadata?: null | string; + name?: null | string; + permissions?: null | string; + prefix?: null | string; + rateLimitEnabled?: null | boolean; + rateLimitMax?: null | number; + rateLimitTimeWindow?: null | number; + refillAmount?: null | number; + refillInterval?: null | number; + remaining?: null | number; + requestCount?: null | number; + start?: null | string; + updatedAt: number; + userId: string; + }; + model: "apikey"; + } | { data: { clientId?: null | string; @@ -3748,10 +526,6 @@ export declare const components: { lastRequest?: null | number; }; model: "rateLimit"; - } - | { - data: { count: number; key: string; lastRequest: number }; - model: "ratelimit"; }; }, any @@ -3934,6 +708,9 @@ export declare const components: { sortBy?: { direction: "asc" | "desc"; field: string }; unique?: boolean; update: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; createdAt?: number; displayUsername?: null | string; email?: string; @@ -3943,6 +720,9 @@ export declare const components: { name?: string; phoneNumber?: null | string; phoneNumberVerified?: null | boolean; + role?: null | string; + stripeCustomerId?: null | string; + teamId?: null | string; twoFactorEnabled?: null | boolean; updatedAt?: number; userId?: null | string; @@ -3987,8 +767,11 @@ export declare const components: { sortBy?: { direction: "asc" | "desc"; field: string }; unique?: boolean; update: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; createdAt?: number; expiresAt?: number; + impersonatedBy?: null | string; ipAddress?: null | string; token?: string; updatedAt?: number; @@ -4208,6 +991,66 @@ export declare const components: { | null; }>; } + | { + limit?: number; + model: "apikey"; + offset?: number; + paginationOpts: { + cursor: string | null; + endCursor?: string | null; + id?: number; + maximumBytesRead?: number; + maximumRowsRead?: number; + numItems: number; + }; + select?: Array; + sortBy?: { direction: "asc" | "desc"; field: string }; + unique?: boolean; + update: { + createdAt?: number; + enabled?: null | boolean; + expiresAt?: null | number; + key?: string; + lastRefillAt?: null | number; + lastRequest?: null | number; + metadata?: null | string; + name?: null | string; + permissions?: null | string; + prefix?: null | string; + rateLimitEnabled?: null | boolean; + rateLimitMax?: null | number; + rateLimitTimeWindow?: null | number; + refillAmount?: null | number; + refillInterval?: null | number; + remaining?: null | number; + requestCount?: null | number; + start?: null | string; + updatedAt?: number; + userId?: string; + }; + where?: Array<{ + connector?: "AND" | "OR"; + field: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } | { limit?: number; model: "oauthApplication"; @@ -4805,45 +1648,6 @@ export declare const components: { | Array | null; }>; - } - | { - limit?: number; - model: "ratelimit"; - offset?: number; - paginationOpts: { - cursor: string | null; - endCursor?: string | null; - id?: number; - maximumBytesRead?: number; - maximumRowsRead?: number; - numItems: number; - }; - select?: Array; - sortBy?: { direction: "asc" | "desc"; field: string }; - unique?: boolean; - update: { count?: number; key?: string; lastRequest?: number }; - where?: Array<{ - connector?: "AND" | "OR"; - field: string; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; }; }, any @@ -4856,6 +1660,9 @@ export declare const components: { | { model: "user"; update: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; createdAt?: number; displayUsername?: null | string; email?: string; @@ -4865,6 +1672,9 @@ export declare const components: { name?: string; phoneNumber?: null | string; phoneNumberVerified?: null | boolean; + role?: null | string; + stripeCustomerId?: null | string; + teamId?: null | string; twoFactorEnabled?: null | boolean; updatedAt?: number; userId?: null | string; @@ -4896,8 +1706,11 @@ export declare const components: { | { model: "session"; update: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; createdAt?: number; expiresAt?: number; + impersonatedBy?: null | string; ipAddress?: null | string; token?: string; updatedAt?: number; @@ -5065,6 +1878,53 @@ export declare const components: { | null; }>; } + | { + model: "apikey"; + update: { + createdAt?: number; + enabled?: null | boolean; + expiresAt?: null | number; + key?: string; + lastRefillAt?: null | number; + lastRequest?: null | number; + metadata?: null | string; + name?: null | string; + permissions?: null | string; + prefix?: null | string; + rateLimitEnabled?: null | boolean; + rateLimitMax?: null | number; + rateLimitTimeWindow?: null | number; + refillAmount?: null | number; + refillInterval?: null | number; + remaining?: null | number; + requestCount?: null | number; + start?: null | string; + updatedAt?: number; + userId?: string; + }; + where?: Array<{ + connector?: "AND" | "OR"; + field: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } | { model: "oauthApplication"; update: { @@ -5493,32 +2353,6 @@ export declare const components: { | Array | null; }>; - } - | { - model: "ratelimit"; - update: { count?: number; key?: string; lastRequest?: number }; - where?: Array<{ - connector?: "AND" | "OR"; - field: string; - operator?: - | "lt" - | "lte" - | "gt" - | "gte" - | "eq" - | "in" - | "ne" - | "contains" - | "starts_with" - | "ends_with"; - value: - | string - | number - | boolean - | Array - | Array - | null; - }>; }; }, any diff --git a/convex/auth.config.ts b/convex/auth.config.ts index ca3c34cc0..f4eb56461 100644 --- a/convex/auth.config.ts +++ b/convex/auth.config.ts @@ -1,10 +1,8 @@ -const authConfig = { +export default { providers: [ { domain: process.env.CONVEX_SITE_URL, - applicationID: 'convex', + applicationID: "convex", }, ], -} - -export default authConfig +}; diff --git a/convex/auth.ts b/convex/auth.ts index 7d54a350a..d2ca06d14 100644 --- a/convex/auth.ts +++ b/convex/auth.ts @@ -1,84 +1,64 @@ -import { components, internal } from './_generated/api' -import { query, QueryCtx } from './_generated/server' -import type { Id, DataModel } from './_generated/dataModel' -import schema from './schema' -import type { Infer } from 'convex/values' -import { betterAuth } from 'better-auth' import { + BetterAuth, type AuthFunctions, - type GenericCtx, - createClient, + type PublicAuthFunctions, } from '@convex-dev/better-auth' -import { convex } from '@convex-dev/better-auth/plugins' +import { api, components, internal } from './_generated/api' +import { query, QueryCtx } from './_generated/server' +import type { Id, DataModel } from './_generated/dataModel' +import schema from './schema' + +import { Infer } from 'convex/values' // Typesafe way to pass Convex functions defined in this file const authFunctions: AuthFunctions = internal.auth +const publicAuthFunctions: PublicAuthFunctions = api.auth // Initialize the component -export const authComponent = createClient(components.betterAuth, { +export const betterAuthComponent = new BetterAuth(components.betterAuth, { authFunctions, - triggers: { - user: { - onCreate: async (ctx, authUser) => { - const userId = await ctx.db.insert('users', { - createdAt: authUser.createdAt, - email: authUser.email, - updatedAt: authUser.updatedAt, - displayUsername: authUser.displayUsername ?? '', - image: authUser.image ?? '', - name: authUser.name, - capabilities: [], - }) - await authComponent.setUserId(ctx, authUser._id, userId) - }, - onUpdate: async (ctx, _, authUser) => { - await ctx.db.patch(authUser.userId as Id<'users'>, { - email: authUser.email, - updatedAt: authUser.updatedAt, - displayUsername: authUser.displayUsername ?? '', - image: authUser.image ?? '', - name: authUser.name, - }) - }, - onDelete: async (ctx, authUser) => { - await ctx.db.delete(authUser.userId as Id<'users'>) - }, - }, - }, + publicAuthFunctions, }) -export const { onCreate, onUpdate, onDelete } = authComponent.triggersApi() - -export const createAuth = ( - ctx: GenericCtx, - { optionsOnly } = { optionsOnly: false } -) => - betterAuth({ - // All auth requests will be proxied through your TanStack Start server - baseURL: process.env.URL, - database: authComponent.adapter(ctx), +// These are required named exports +export const { + createUser, + updateUser, + deleteUser, + createSession, + isAuthenticated, +} = betterAuthComponent.createAuthFunctions({ + // Must create a user and return the user id + onCreateUser: async (ctx, user) => { + return ctx.db.insert('users', { + createdAt: user.createdAt, + email: user.email, + updatedAt: user.updatedAt, + displayUsername: user.displayUsername ?? '', + image: user.image ?? '', + name: user.name, + capabilities: [], + }) + }, - logger: { - disabled: optionsOnly, - }, + // Delete the user when they are deleted from Better Auth + onDeleteUser: async (ctx, userId) => { + await ctx.db.delete(userId as Id<'users'>) + }, - // Simple non-verified email/password to get started - socialProviders: { - github: { - clientId: process.env.GITHUB_OAUTH_CLIENT_ID as string, - clientSecret: process.env.GITHUB_OAUTH_CLIENT_SECRET as string, - }, - google: { - clientId: process.env.GOOGLE_OAUTH_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET as string, - }, - }, - plugins: [ - // The Convex plugin is required - convex(), - ], - }) + onUpdateUser: async (ctx, user) => { + await ctx.db.patch(user.userId as Id<'users'>, { + email: user.email, + updatedAt: user.updatedAt, + displayUsername: user.displayUsername ?? '', + image: user.image ?? '', + name: user.name, + }) + }, +}) +// Example function for getting the current user +// Feel free to edit, omit, etc. export const getCurrentUser = query({ args: {}, handler: async (ctx) => { @@ -106,5 +86,5 @@ export type TanStackUser = Awaited> & Infer function getBetterAuthUser(ctx: QueryCtx) { - return authComponent.safeGetAuthUser(ctx) + return betterAuthComponent.getAuthUser(ctx) } diff --git a/convex/http.ts b/convex/http.ts index 96b5ef0cb..b0cb04465 100644 --- a/convex/http.ts +++ b/convex/http.ts @@ -1,10 +1,11 @@ import { ossStats } from './stats' import { httpRouter } from 'convex/server' -import { authComponent, createAuth } from './auth' +import { betterAuthComponent } from './auth' +import { createAuth } from '../src/server/auth.server' const http = httpRouter() ossStats.registerRoutes(http) -authComponent.registerRoutes(http, createAuth) +betterAuthComponent.registerRoutes(http, createAuth) export default http diff --git a/convex/schema.ts b/convex/schema.ts index a8ed9b8b7..0320d1459 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -1,4 +1,5 @@ import { defineSchema, defineTable } from 'convex/server' +import { authTables } from '@convex-dev/auth/server' import { v } from 'convex/values' import { z } from 'zod' @@ -19,6 +20,7 @@ export function validateCapability( const validCapabilities = VALID_CAPABILITIES const schema = defineSchema({ + ...authTables, users: defineTable({ displayUsername: v.optional(v.string()), createdAt: v.number(), diff --git a/convex/stats.ts b/convex/stats.ts index 1f88ebb37..40aa60376 100644 --- a/convex/stats.ts +++ b/convex/stats.ts @@ -5,8 +5,7 @@ import { v } from 'convex/values' export const ossStats = new OssStats(components.ossStats, { githubOwners: ['tanstack'], - npmOrgs: ['tanstack'], - npmPackages: ['react-query', 'react-table'], + npmOrgs: ['tanstack', 'tannerlinsley'], }) export const getStats = query({ diff --git a/package.json b/package.json index a28627f4c..56699203c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "dependencies": { "@auth/core": "0.37.0", "@clerk/tanstack-react-start": "^0.21.5", - "@convex-dev/better-auth": "^0.8.6", + "@convex-dev/auth": "^0.0.88", + "@convex-dev/better-auth": "^0.7.15", "@convex-dev/react-query": "0.0.0-alpha.11", "@erquhart/convex-oss-stats": "^0.8.1", "@floating-ui/react": "^0.27.8", @@ -30,8 +31,8 @@ "@octokit/rest": "^20.0.2", "@orama/react-components": "^0.1.23", "@radix-ui/react-dropdown-menu": "^2.1.12", - "@radix-ui/react-select": "^2.2.2", "@radix-ui/react-toast": "^1.2.2", + "@radix-ui/react-select": "^2.2.2", "@remix-run/node": "^2.8.1", "@sentry/react": "^8.35.0", "@sentry/vite-plugin": "^2.22.6", @@ -53,9 +54,9 @@ "@vitejs/plugin-react": "^4.3.3", "algoliasearch": "^5.23.4", "axios": "^1.6.7", - "better-auth": "^1.3.8", + "better-auth": "^1.3.7", "cmdk": "^1.1.1", - "convex": "^1.27.0", + "convex": "^1.25.4", "convex-oss-stats": "link:../../../erquhart/convex-oss-stats", "d3": "^7.9.0", "date-fns": "^2.30.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa7b1d06c..67293fc31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,15 +14,18 @@ importers: '@clerk/tanstack-react-start': specifier: ^0.21.5 version: 0.21.5(@tanstack/react-router@1.131.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/react-start@1.131.27(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@vitejs/plugin-react@4.3.4(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@convex-dev/auth': + specifier: ^0.0.88 + version: 0.0.88(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) '@convex-dev/better-auth': - specifier: ^0.8.6 - version: 0.8.6(better-auth@1.3.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3) + specifier: ^0.7.15 + version: 0.7.15(better-auth@1.3.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@4.0.17))(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3) '@convex-dev/react-query': specifier: 0.0.0-alpha.11 - version: 0.0.0-alpha.11(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) + version: 0.0.0-alpha.11(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) '@erquhart/convex-oss-stats': specifier: ^0.8.1 - version: 0.8.1(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) + version: 0.8.1(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) '@floating-ui/react': specifier: ^0.27.8 version: 0.27.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -117,14 +120,14 @@ importers: specifier: ^1.6.7 version: 1.7.8 better-auth: - specifier: ^1.3.8 - version: 1.3.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.3.7 + version: 1.3.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@4.0.17) cmdk: specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) convex: - specifier: ^1.27.0 - version: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + specifier: ^1.25.4 + version: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) convex-oss-stats: specifier: link:../../../erquhart/convex-oss-stats version: link:../../../erquhart/convex-oss-stats @@ -1267,11 +1270,22 @@ packages: react: optional: true - '@convex-dev/better-auth@0.8.6': - resolution: {integrity: sha512-iP2erc4YKz0HzrWSdaSRv2ck3gsdb2yBwUdSOsM0ebvNMcTsoWEBuu07cq6lUGMyJ97qxOnZDtN7CemTXKpSpA==} + '@convex-dev/auth@0.0.88': + resolution: {integrity: sha512-D1uMuBEQH+1h6T42yxtppivUYXEAqID5/j4aqzgXrVDnqpQJMoeSU9qM3qJaSV3psA1VClwTRj6JMrDPHLFXqQ==} + hasBin: true + peerDependencies: + '@auth/core': ^0.37.0 + convex: ^1.17.0 + react: ^18.2.0 || ^19.0.0-0 + peerDependenciesMeta: + react: + optional: true + + '@convex-dev/better-auth@0.7.15': + resolution: {integrity: sha512-C+5xJ2d2AugMGOmGgprer5Bbgv6Wi39B//CP1o9j6zYNEbGlOtRtV5Jbpp6IwOy2ZaK0ngyUw3MLDjMq/OmK1w==} peerDependencies: - better-auth: 1.3.8 - convex: ^1.26.2 + better-auth: 1.3.4 + convex: '>=1.25.0 <1.35.0' react: ^18.3.1 || ^19.0.0 react-dom: ^18.3.1 || ^19.0.0 @@ -4551,19 +4565,20 @@ packages: before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} - better-auth@1.3.8: - resolution: {integrity: sha512-uRFzHbWkhr8eWNy+BJwyMnrZPOvQjwrcLND3nc6jusRteYA9cjeRGElgCPTWTIyWUfzaQ708Lb5Mdq9Gv41Qpw==} + better-auth@1.3.7: + resolution: {integrity: sha512-/1fEyx2SGgJQM5ujozDCh9eJksnVkNU/J7Fk/tG5Y390l8nKbrPvqiFlCjlMM+scR+UABJbQzA6An7HT50LHyQ==} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + zod: ^3.25.0 || ^4.0.0 peerDependenciesMeta: react: optional: true react-dom: optional: true - better-call@1.0.16: - resolution: {integrity: sha512-42dgJ1rOtc0anOoxjXPOWuel/Z/4aeO7EJ2SiXNwvlkySSgjXhNjAjTMWa8DL1nt6EXS3jl3VKC3mPsU/lUgVA==} + better-call@1.0.13: + resolution: {integrity: sha512-auqdP9lnNOli9tKpZIiv0nEIwmmyaD/RotM3Mucql+Ef88etoZi/t7Ph5LjlmZt/hiSahhNTt6YVnx6++rziXA==} binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -4860,8 +4875,8 @@ packages: zod: optional: true - convex@1.27.0: - resolution: {integrity: sha512-IHkqZX3GtY4nKFPTAR4mvWHHhDiQX9PM7EjpEv0pJWoMoq0On6oOL3iZ7Xz4Ls96dF7WJd4AjfitJsg2hUnLSQ==} + convex@1.25.4: + resolution: {integrity: sha512-LiGZZTmbe5iHWwDOYfSA00w+uDM8kgLC0ohFJW0VgQlKcs8famHCE6yuplk4wwXyj9Lhb1+yMRfrAD2ZEquqHg==} engines: {node: '>=18.0.0', npm: '>=7.0.0'} hasBin: true peerDependencies: @@ -7316,6 +7331,11 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -8687,9 +8707,6 @@ packages: zod@4.0.17: resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==} - zod@4.1.9: - resolution: {integrity: sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ==} - zustand@4.5.2: resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} engines: {node: '>=12.7.0'} @@ -9968,10 +9985,10 @@ snapshots: '@content-collections/integrations': 0.2.1(@content-collections/core@0.8.2(typescript@5.6.3)) vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.4)(yaml@2.8.1) - '@convex-dev/auth@0.0.83(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': + '@convex-dev/auth@0.0.83(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': dependencies: '@auth/core': 0.37.0 - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cookie: 1.0.2 is-network-error: 1.1.0 jose: 5.10.0 @@ -9984,13 +10001,29 @@ snapshots: optionalDependencies: react: 19.0.0 - '@convex-dev/better-auth@0.8.6(better-auth@1.3.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)': + '@convex-dev/auth@0.0.88(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': dependencies: - '@better-fetch/fetch': 1.1.18 - better-auth: 1.3.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@auth/core': 0.37.0 + '@oslojs/crypto': 1.0.1 + '@oslojs/encoding': 1.1.0 + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + cookie: 1.0.2 + is-network-error: 1.1.0 + jose: 5.10.0 + jwt-decode: 4.0.0 + lucia: 3.2.2 + oauth4webapi: 3.7.0 + path-to-regexp: 6.3.0 + server-only: 0.0.1 + optionalDependencies: + react: 19.0.0 + + '@convex-dev/better-auth@0.7.15(better-auth@1.3.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@4.0.17))(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)': + dependencies: + better-auth: 1.3.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@4.0.17) common-tags: 1.8.2 - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.104(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@3.25.76) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.104(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@3.25.76) is-network-error: 1.1.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -10001,18 +10034,18 @@ snapshots: - hono - typescript - '@convex-dev/crons@0.1.9(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': + '@convex-dev/crons@0.1.9(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': dependencies: - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cron-parser: 4.9.0 - '@convex-dev/react-query@0.0.0-alpha.11(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17)': + '@convex-dev/react-query@0.0.0-alpha.11(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17)': dependencies: '@auth/core': 0.37.0 - '@convex-dev/auth': 0.0.83(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) + '@convex-dev/auth': 0.0.83(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) '@tanstack/react-query': 5.84.2(react@19.0.0) - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.104(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.104(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) transitivePeerDependencies: - '@simplewebauthn/browser' - '@simplewebauthn/server' @@ -10050,14 +10083,14 @@ snapshots: tslib: 2.8.1 optional: true - '@erquhart/convex-oss-stats@0.8.1(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17)': + '@erquhart/convex-oss-stats@0.8.1(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17)': dependencies: - '@convex-dev/crons': 0.1.9(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) + '@convex-dev/crons': 0.1.9(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) '@octokit/graphql': 8.2.2 '@octokit/graphql-schema': 15.26.0 cheerio: 1.0.0 - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.99(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.99(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17) date-fns: 4.1.0 framer-motion: 11.18.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nano: 10.1.4 @@ -13399,7 +13432,7 @@ snapshots: before-after-hook@3.0.2: {} - better-auth@1.3.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + better-auth@1.3.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@4.0.17): dependencies: '@better-auth/utils': 0.2.6 '@better-fetch/fetch': 1.1.18 @@ -13407,17 +13440,17 @@ snapshots: '@noble/hashes': 1.8.0 '@simplewebauthn/browser': 13.1.2 '@simplewebauthn/server': 13.1.2 - better-call: 1.0.16 + better-call: 1.0.13 defu: 6.1.4 jose: 5.10.0 kysely: 0.28.5 nanostores: 0.11.4 - zod: 4.1.9 + zod: 4.0.17 optionalDependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - better-call@1.0.16: + better-call@1.0.13: dependencies: '@better-fetch/fetch': 1.1.18 rou3: 0.5.1 @@ -13707,35 +13740,35 @@ snapshots: convert-source-map@2.0.0: {} - convex-helpers@0.1.104(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@3.25.76): + convex-helpers@0.1.104(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@3.25.76): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: react: 19.0.0 typescript: 5.6.3 zod: 3.25.76 - convex-helpers@0.1.104(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17): + convex-helpers@0.1.104(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: react: 19.0.0 typescript: 5.6.3 zod: 4.0.17 - convex-helpers@0.1.99(convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17): + convex-helpers@0.1.99(convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.6.3)(zod@4.0.17): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: react: 19.0.0 typescript: 5.6.3 zod: 4.0.17 - convex@1.27.0(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): + convex@1.25.4(@clerk/clerk-react@5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): dependencies: esbuild: 0.25.4 jwt-decode: 4.0.0 - prettier: 3.6.2 + prettier: 3.5.3 optionalDependencies: '@clerk/clerk-react': 5.43.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 @@ -16467,6 +16500,8 @@ snapshots: prettier@2.8.8: {} + prettier@3.5.3: {} + prettier@3.6.2: {} pretty-bytes@6.1.1: {} @@ -17879,8 +17914,6 @@ snapshots: zod@4.0.17: {} - zod@4.1.9: {} - zustand@4.5.2(@types/react@18.3.12)(react@19.0.0): dependencies: use-sync-external-store: 1.2.0(react@19.0.0) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index c9654ac64..c3ee4dc0f 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -16,25 +16,31 @@ import { } from 'react-icons/fa' import { ThemeToggle } from './ThemeToggle' import { SearchButton } from './SearchButton' +import { Authenticated, Unauthenticated, useQuery } from 'convex/react' +import { AuthLoading } from 'convex/react' import { api } from 'convex/_generated/api' -import { MdLibraryBooks, MdLineAxis, MdPerson, MdSupport } from 'react-icons/md' +import { + MdLibraryBooks, + MdLineAxis, + MdMenu, + MdPerson, + MdSupport, +} from 'react-icons/md' import { CgClose, CgMenuLeft, CgMusicSpeaker } from 'react-icons/cg' import { BiSolidCheckShield } from 'react-icons/bi' import { PiHammerFill } from 'react-icons/pi' import { libraries } from '~/libraries' import { sortBy } from '~/utils/utils' -import { useSuspenseQuery } from '@tanstack/react-query' -import { convexQuery } from '@convex-dev/react-query' export function Navbar({ children }: { children: React.ReactNode }) { - const user = useSuspenseQuery(convexQuery(api.auth.getCurrentUser, {})) + const user = useQuery(api.auth.getCurrentUser) const matches = useMatches() const Title = [...matches].reverse().find((m) => m.staticData.Title)?.staticData.Title ?? null - const canAdmin = user.data?.capabilities.includes('admin') + const canAdmin = user?.capabilities.includes('admin') const containerRef = React.useRef(null) @@ -60,8 +66,6 @@ export function Navbar({ children }: { children: React.ReactNode }) { setShowMenu((prev) => !prev) } - const isAuthenticated = Boolean(user.data) - const loginButton = ( <> {(() => { @@ -76,37 +80,36 @@ export function Navbar({ children }: { children: React.ReactNode }) { ) - if (!isAuthenticated) { - return loginEl - } + return ( + <> + {loginEl} + {loginEl} + + ) })()} - {isAuthenticated && ( - <> + +
+ + + My Account + +
+ {canAdmin ? (
- + - My Account + Admin
- <> - {canAdmin ? ( -
- - - Admin - -
- ) : null} - - - )} + ) : null} +
) @@ -343,27 +346,28 @@ export function Navbar({ children }: { children: React.ReactNode }) {
- {isAuthenticated && - user.data?.capabilities.some((capability) => - ['builder', 'admin'].includes(capability) - ) ? ( - -
-
- + + {user?.capabilities.some((capability) => + ['builder', 'admin'].includes(capability) + ) ? ( + +
+
+ +
+
Builder
-
Builder
-
- - ) : null} + + ) : null} + {[ { label: 'Maintainers', diff --git a/src/router.tsx b/src/router.tsx index 9f8fe0158..6ff8be9bc 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -4,7 +4,7 @@ import { } from '@tanstack/react-router' import { routerWithQueryClient } from '@tanstack/react-router-with-query' import { convexQuery, ConvexQueryClient } from '@convex-dev/react-query' -import { ConvexProvider, ConvexReactClient } from 'convex/react' +import { ConvexProvider } from 'convex/react' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' @@ -16,8 +16,7 @@ import React from 'react' export function createRouter() { const CONVEX_URL = env.VITE_CONVEX_URL! - const convexClient = new ConvexReactClient(CONVEX_URL, { expectAuth: true }) - const convexQueryClient = new ConvexQueryClient(convexClient) + const convexQueryClient = new ConvexQueryClient(CONVEX_URL) const queryClient: QueryClient = new QueryClient({ defaultOptions: { diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index c6d1c4d3d..34bd745a1 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -24,7 +24,7 @@ import { SearchProvider } from '~/contexts/SearchContext' import { SearchModal } from '~/components/SearchModal' import { ToastProvider } from '~/components/ToastProvider' import { ThemeProvider } from '~/components/ThemeProvider' -import { convexQuery, ConvexQueryClient } from '@convex-dev/react-query' +import { ConvexQueryClient } from '@convex-dev/react-query' import { ConvexReactClient } from 'convex/react' import { Navbar } from '~/components/Navbar' @@ -33,25 +33,6 @@ import { authClient } from '../utils/auth.client' import { LibrariesLayout } from './_libraries/route' import { TanStackUser } from 'convex/auth' -import { createServerFn } from '@tanstack/react-start' -import { getCookie, getWebRequest } from '@tanstack/react-start/server' -import { - fetchSession, - getCookieName, -} from '@convex-dev/better-auth/react-start' -import { api } from 'convex/_generated/api' - -// Get auth information for SSR using available cookies -const fetchAuth = createServerFn({ method: 'GET' }).handler(async () => { - const { createAuth } = await import('convex/auth') - const { session } = await fetchSession(getWebRequest()) - const sessionCookieName = getCookieName(createAuth) - const token = getCookie(sessionCookieName) - return { - userId: session?.user.id, - token, - } -}) export const Route = createRootRouteWithContext<{ queryClient: QueryClient @@ -156,18 +137,11 @@ export const Route = createRootRouteWithContext<{ }) } - const { userId, token } = await fetchAuth() - - // During SSR only (the only time serverHttpClient exists), - // set the auth token to make HTTP queries with. - if (token) { - ctx.context.convexQueryClient.serverHttpClient?.setAuth(token) - } - - return { - userId, - token, - } + // // During SSR only (the only time serverHttpClient exists), + // // set the auth token for Convex to make HTTP queries with. + // if (token) { + // ctx.context.convexQueryClient.serverHttpClient?.setAuth(token) + // } }, staleTime: Infinity, errorComponent: (props) => { @@ -193,11 +167,6 @@ export const Route = createRootRouteWithContext<{ ) }, - loader: async ({ context }) => { - await context.queryClient.ensureQueryData( - convexQuery(api.auth.getCurrentUser, {}) - ) - }, }) function DocumentWrapper({ children }: { children: React.ReactNode }) { diff --git a/src/routes/_libraries/account.tsx b/src/routes/_libraries/account.tsx index 382660175..68b87dab8 100644 --- a/src/routes/_libraries/account.tsx +++ b/src/routes/_libraries/account.tsx @@ -1,12 +1,10 @@ import { FaSignOutAlt } from 'react-icons/fa' -import { useMutation } from 'convex/react' +import { Authenticated, Unauthenticated, useMutation } from 'convex/react' import { Link, redirect } from '@tanstack/react-router' import { authClient } from '~/utils/auth.client' import { useCurrentUserQuery } from '~/hooks/useCurrentUser' import { api } from 'convex/_generated/api' import { useToast } from '~/components/ToastProvider' -import { convexQuery } from '@convex-dev/react-query' -import { useSuspenseQuery } from '@tanstack/react-query' export const Route = createFileRoute({ component: AccountPage, @@ -122,12 +120,12 @@ function UserSettings() { } function AccountPage() { - const user = useSuspenseQuery(convexQuery(api.auth.getCurrentUser, {})) - const isAuthenticated = Boolean(user.data) return (
- {isAuthenticated && } - {!isAuthenticated && ( + + + +

Sign In Required @@ -141,7 +139,7 @@ function AccountPage() {

- )} +
) } diff --git a/src/routes/api/auth/$.ts b/src/routes/api/auth/$.ts index 0d57b95ff..4f8fa0854 100644 --- a/src/routes/api/auth/$.ts +++ b/src/routes/api/auth/$.ts @@ -1,4 +1,4 @@ -import { reactStartHandler } from '@convex-dev/better-auth/react-start' +import { reactStartHandler } from "~/server/auth.server" export const ServerRoute = createServerFileRoute().methods({ GET: ({ request }) => { diff --git a/src/server/auth.server.ts b/src/server/auth.server.ts new file mode 100644 index 000000000..1d526e149 --- /dev/null +++ b/src/server/auth.server.ts @@ -0,0 +1,41 @@ +import { convexAdapter } from '@convex-dev/better-auth' +import { convex } from '@convex-dev/better-auth/plugins' +import { betterAuth } from 'better-auth' +import { reactStartHelpers } from '@convex-dev/better-auth/react-start' +import { GenericCtx } from '../../convex/_generated/server' +import { betterAuthComponent } from '../../convex/auth' + +// You'll want to replace this with an environment variable +const siteUrl = process.env.URL +const convexSiteUrl = + process.env.NODE_ENV === 'production' + ? process.env.VITE_CONVEX_SITE_URL! + : 'http://upbeat-greyhound-631.convex.site' + +export const createAuth = (ctx: GenericCtx) => + betterAuth({ + // All auth requests will be proxied through your TanStack Start server + baseURL: siteUrl, + database: convexAdapter(ctx, betterAuthComponent), + + // Simple non-verified email/password to get started + socialProviders: { + github: { + clientId: process.env.GITHUB_OAUTH_CLIENT_ID as string, + clientSecret: process.env.GITHUB_OAUTH_CLIENT_SECRET as string, + }, + google: { + clientId: process.env.GOOGLE_OAUTH_CLIENT_ID as string, + clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET as string, + }, + }, + plugins: [ + // The Convex plugin is required + convex(), + ], + }) + +export const { fetchSession, reactStartHandler, getCookieName } = + reactStartHelpers(createAuth, { + convexSiteUrl, + }) diff --git a/src/utils/gh-sponsor-meta.json b/src/utils/gh-sponsor-meta.json index 890078ae7..d291b0647 100644 --- a/src/utils/gh-sponsor-meta.json +++ b/src/utils/gh-sponsor-meta.json @@ -271,7 +271,7 @@ "imageUrl": "https://www.toyokumo.co.jp/logo-en.svg", "linkUrl": "https://www.toyokumo.co.jp/en?utm_source=tanstack" }, - { + { "login": "promptmonitor", "name": "Promptmonitor", "imageUrl": "https://bvatwanklwlvzlcxrcxn.supabase.co/storage/v1/object/public/assets/promptmonitor-icon-brandfill.png",