From e84cc8f345b03824ccaf413863e4368175d2ba07 Mon Sep 17 00:00:00 2001 From: Austin Montoya <126262824+austin-liminal@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:41:11 -0700 Subject: [PATCH 01/10] refactor: migrate to convex + better-auth (#471) * refactor: migrate to convex + better-auth * chore: upgrade to tailwindcss v4 --------- Co-authored-by: Tanner Linsley --- convex/_generated/api.d.ts | 2100 +++++++++++++++++++++++++++ convex/_generated/dataModel.d.ts | 34 +- convex/auth.config.ts | 8 + convex/auth.ts | 63 + convex/convex.config.ts | 2 + convex/http.ts | 4 + convex/schema.ts | 12 + package.json | 9 +- pnpm-lock.yaml | 793 +++++----- src/libraries/auth-client.ts | 8 + src/libraries/auth.ts | 32 + src/libraries/server-auth-utils.ts | 8 + src/routeTree.gen.ts | 145 +- src/router.tsx | 6 +- src/routes/__root.tsx | 50 +- src/routes/_libraries/account.$.tsx | 95 +- src/routes/_libraries/dashboard.tsx | 60 + src/routes/_libraries/login.tsx | 86 +- src/routes/_libraries/route.tsx | 38 +- src/routes/api/auth/$.ts | 10 + src/routes/builder.tsx | 31 + src/server/auth.ts | 82 -- src/stores/userSettings.ts | 6 +- src/styles/app.css | 56 +- src/utils/env.ts | 13 +- 25 files changed, 3137 insertions(+), 614 deletions(-) create mode 100644 convex/auth.config.ts create mode 100644 convex/auth.ts create mode 100644 convex/schema.ts create mode 100644 src/libraries/auth-client.ts create mode 100644 src/libraries/auth.ts create mode 100644 src/libraries/server-auth-utils.ts create mode 100644 src/routes/_libraries/dashboard.tsx create mode 100644 src/routes/api/auth/$.ts create mode 100644 src/routes/builder.tsx delete mode 100644 src/server/auth.ts diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 672339fa..ebd6338c 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -8,6 +8,7 @@ * @module */ +import type * as auth from "../auth.js"; import type * as http from "../http.js"; import type * as stats from "../stats.js"; @@ -16,6 +17,7 @@ import type { FilterApi, FunctionReference, } from "convex/server"; + /** * A utility for referencing Convex functions in your app's API. * @@ -25,6 +27,7 @@ import type { * ``` */ declare const fullApi: ApiFromModules<{ + auth: typeof auth; http: typeof http; stats: typeof stats; }>; @@ -167,4 +170,2101 @@ export declare const components: { >; }; }; + betterAuth: { + adapterTest: { + count: FunctionReference<"query", "internal", any, any>; + create: FunctionReference<"mutation", "internal", any, any>; + delete: FunctionReference<"mutation", "internal", any, any>; + 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>; + }; + lib: { + create: FunctionReference< + "mutation", + "internal", + { + input: + | { + data: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; + createdAt: number; + displayUsername?: null | string; + email: string; + emailVerified: boolean; + image?: null | string; + isAnonymous?: null | boolean; + 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; + username?: null | string; + }; + model: "user"; + } + | { + data: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; + createdAt: number; + expiresAt: number; + impersonatedBy?: null | string; + 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?: null | number; + expiresAt: number; + identifier: string; + updatedAt?: null | 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: { + 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; + 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; + 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: { + createdAt: number; + name: string; + organizationId: string; + updatedAt?: null | number; + }; + model: "team"; + } + | { + data: { + createdAt?: null | number; + teamId: string; + userId: string; + }; + model: "teamMember"; + } + | { + 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; + }; + 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"; + }; + }, + any + >; + deleteMany: FunctionReference< + "mutation", + "internal", + { + limit?: number; + model: string; + 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; + 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 + >; + deleteOne: FunctionReference< + "mutation", + "internal", + { + limit?: number; + model: string; + offset?: number; + select?: Array; + sortBy?: { direction: "asc" | "desc"; field: string }; + unique?: boolean; + 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 + >; + findMany: FunctionReference< + "query", + "internal", + { + limit?: number; + model: string; + 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; + 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", + { + limit?: number; + model: string; + offset?: number; + select?: Array; + sortBy?: { direction: "asc" | "desc"; field: string }; + unique?: boolean; + 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 + >; + getCurrentSession: FunctionReference<"query", "internal", {}, any>; + updateMany: FunctionReference< + "mutation", + "internal", + { + input: + | { + limit?: number; + model: "user"; + 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: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; + createdAt?: number; + displayUsername?: null | string; + email?: string; + emailVerified?: boolean; + image?: null | string; + isAnonymous?: null | boolean; + 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; + username?: null | 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: "session"; + 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: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; + createdAt?: number; + expiresAt?: number; + impersonatedBy?: null | string; + ipAddress?: null | string; + token?: string; + updatedAt?: number; + userAgent?: null | string; + 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: "account"; + 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: { + 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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } + | { + limit?: number; + model: "verification"; + 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?: null | number; + expiresAt?: number; + identifier?: string; + updatedAt?: null | number; + value?: 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: "twoFactor"; + 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: { + backupCodes?: string; + secret?: string; + 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: "passkey"; + 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: { + 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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | 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"; + 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: { + 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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } + | { + limit?: number; + model: "oauthAccessToken"; + 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: { + 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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } + | { + limit?: number; + model: "oauthConsent"; + 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: { + clientId?: null | string; + consentGiven?: null | boolean; + createdAt?: null | number; + scopes?: null | string; + updatedAt?: null | number; + userId?: null | 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: "organization"; + 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; + logo?: null | string; + metadata?: null | string; + name?: string; + slug?: null | 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: "member"; + 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; + organizationId?: string; + role?: string; + 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: "invitation"; + 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: { + email?: string; + expiresAt?: number; + inviterId?: string; + organizationId?: string; + role?: null | string; + status?: string; + teamId?: null | 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: "team"; + 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; + name?: string; + organizationId?: string; + updatedAt?: null | 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; + }>; + } + | { + limit?: number; + model: "teamMember"; + 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?: null | number; + teamId?: string; + 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: "ssoProvider"; + 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: { + domain?: string; + issuer?: string; + oidcConfig?: null | string; + organizationId?: null | string; + providerId?: string; + samlConfig?: null | string; + userId?: null | 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: "jwks"; + 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; + privateKey?: string; + publicKey?: 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: "subscription"; + 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: { + 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; + }; + 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: "walletAddress"; + 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: { + address?: string; + chainId?: number; + createdAt?: number; + isPrimary?: null | boolean; + 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: "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?: null | number; + key?: null | string; + lastRequest?: null | 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 + >; + updateOne: FunctionReference< + "mutation", + "internal", + { + input: + | { + model: "user"; + update: { + banExpires?: null | number; + banReason?: null | string; + banned?: null | boolean; + createdAt?: number; + displayUsername?: null | string; + email?: string; + emailVerified?: boolean; + image?: null | string; + isAnonymous?: null | boolean; + 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; + username?: null | 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: "session"; + update: { + activeOrganizationId?: null | string; + activeTeamId?: null | string; + createdAt?: number; + expiresAt?: number; + impersonatedBy?: null | string; + ipAddress?: null | string; + token?: string; + updatedAt?: number; + userAgent?: null | string; + 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: "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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | null; + }>; + } + | { + model: "verification"; + update: { + createdAt?: null | number; + expiresAt?: number; + identifier?: string; + updatedAt?: null | number; + value?: 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: "twoFactor"; + update: { + backupCodes?: string; + secret?: string; + 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: "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: string; + operator?: + | "lt" + | "lte" + | "gt" + | "gte" + | "eq" + | "in" + | "ne" + | "contains" + | "starts_with" + | "ends_with"; + value: + | string + | number + | boolean + | Array + | Array + | 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: { + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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: string; + 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; + }; + 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: "walletAddress"; + update: { + address?: string; + chainId?: number; + createdAt?: number; + isPrimary?: null | boolean; + 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: "rateLimit"; + update: { + count?: null | number; + key?: null | string; + lastRequest?: null | 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/_generated/dataModel.d.ts b/convex/_generated/dataModel.d.ts index fb12533b..8541f319 100644 --- a/convex/_generated/dataModel.d.ts +++ b/convex/_generated/dataModel.d.ts @@ -8,29 +8,29 @@ * @module */ -import { AnyDataModel } from "convex/server"; +import type { + DataModelFromSchemaDefinition, + DocumentByName, + TableNamesInDataModel, + SystemTableNames, +} from "convex/server"; import type { GenericId } from "convex/values"; - -/** - * No `schema.ts` file found! - * - * This generated code has permissive types like `Doc = any` because - * Convex doesn't know your schema. If you'd like more type safety, see - * https://docs.convex.dev/using/schemas for instructions on how to add a - * schema file. - * - * After you change a schema, rerun codegen with `npx convex dev`. - */ +import schema from "../schema.js"; /** * The names of all of your Convex tables. */ -export type TableNames = string; +export type TableNames = TableNamesInDataModel; /** * The type of a document stored in Convex. + * + * @typeParam TableName - A string literal type of the table name (like "users"). */ -export type Doc = any; +export type Doc = DocumentByName< + DataModel, + TableName +>; /** * An identifier for a document in Convex. @@ -42,8 +42,10 @@ export type Doc = any; * * IDs are just strings at runtime, but this type can be used to distinguish them from other * strings when type checking. + * + * @typeParam TableName - A string literal type of the table name (like "users"). */ -export type Id = +export type Id = GenericId; /** @@ -55,4 +57,4 @@ export type Id = * This type is used to parameterize methods like `queryGeneric` and * `mutationGeneric` to make them type-safe. */ -export type DataModel = AnyDataModel; +export type DataModel = DataModelFromSchemaDefinition; diff --git a/convex/auth.config.ts b/convex/auth.config.ts new file mode 100644 index 00000000..f4eb5646 --- /dev/null +++ b/convex/auth.config.ts @@ -0,0 +1,8 @@ +export default { + providers: [ + { + domain: process.env.CONVEX_SITE_URL, + applicationID: "convex", + }, + ], +}; diff --git a/convex/auth.ts b/convex/auth.ts new file mode 100644 index 00000000..b70ee728 --- /dev/null +++ b/convex/auth.ts @@ -0,0 +1,63 @@ +import { + BetterAuth, + type AuthFunctions, + type PublicAuthFunctions, +} from "@convex-dev/better-auth"; +import { api, components, internal } from "./_generated/api"; +import { query } from "./_generated/server"; +import type { Id, DataModel } from "./_generated/dataModel"; + +// 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 betterAuthComponent = new BetterAuth( + components.betterAuth, + { + authFunctions, + publicAuthFunctions, + } +); + +// 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", { + capabilities: [], + }); + }, + + // Delete the user when they are deleted from Better Auth + onDeleteUser: async (ctx, userId) => { + await ctx.db.delete(userId as Id<"users">); + }, + }); + +// Example function for getting the current user +// Feel free to edit, omit, etc. +export const getCurrentUser = query({ + args: {}, + handler: async (ctx) => { + // Get user data from Better Auth - email, name, image, etc. + const userMetadata = await betterAuthComponent.getAuthUser(ctx); + if (!userMetadata) { + return null; + } + // Get user data from your application's database + // (skip this if you have no fields in your users table schema) + const user = await ctx.db.get(userMetadata.userId as Id<"users">); + return { + ...user, + ...userMetadata, + }; + }, +}); \ No newline at end of file diff --git a/convex/convex.config.ts b/convex/convex.config.ts index 0e30da2f..9818392f 100644 --- a/convex/convex.config.ts +++ b/convex/convex.config.ts @@ -1,7 +1,9 @@ import { defineApp } from 'convex/server' import ossStats from '@erquhart/convex-oss-stats/convex.config' +import betterAuth from '@convex-dev/better-auth/convex.config' const app = defineApp() app.use(ossStats) +app.use(betterAuth) export default app diff --git a/convex/http.ts b/convex/http.ts index 492aa464..e3942cee 100644 --- a/convex/http.ts +++ b/convex/http.ts @@ -1,8 +1,12 @@ import { ossStats } from './stats' import { httpRouter } from 'convex/server' +import { betterAuthComponent } from './auth' +import { createAuth } from '../src/libraries/auth' + const http = httpRouter() ossStats.registerRoutes(http) +betterAuthComponent.registerRoutes(http, createAuth) export default http diff --git a/convex/schema.ts b/convex/schema.ts new file mode 100644 index 00000000..750e68b0 --- /dev/null +++ b/convex/schema.ts @@ -0,0 +1,12 @@ +import { defineSchema, defineTable } from "convex/server"; +import { authTables } from "@convex-dev/auth/server"; +import { v } from "convex/values"; + +const schema = defineSchema({ + ...authTables, + users: defineTable({ + capabilities: v.array(v.string()), + }) +}); + +export default schema \ No newline at end of file diff --git a/package.json b/package.json index bda81f9e..605bcdd5 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "linkAll": "node scripts/link.js" }, "dependencies": { - "@clerk/tanstack-react-start": "^0.21.7", + "@auth/core": "0.37.0", + "@convex-dev/auth": "^0.0.88", + "@convex-dev/better-auth": "^0.7.14", "@convex-dev/react-query": "0.0.0-alpha.8", "@erquhart/convex-oss-stats": "^0.5.5", "@floating-ui/react": "^0.27.8", @@ -50,8 +52,9 @@ "airtable": "^0.12.2", "algoliasearch": "^5.23.4", "axios": "^1.6.7", + "better-auth": "^1.3.7", "cmdk": "^1.1.1", - "convex": "^1.17.2", + "convex": "^1.25.4", "d3": "^7.9.0", "date-fns": "^2.30.0", "downshift": "^9.0.9", @@ -76,7 +79,7 @@ "tailwind-merge": "^1.14.0", "tiny-invariant": "^1.3.3", "vite-tsconfig-paths": "^5.0.1", - "zod": "^3.23.8", + "zod": "^4.0.17", "zustand": "^4.5.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5073471..d2d38014 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,21 @@ importers: .: dependencies: - '@clerk/tanstack-react-start': - specifier: ^0.21.7 - version: 0.21.7(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/react-start@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@auth/core': + specifier: 0.37.0 + version: 0.37.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.40.0(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.7.14 + version: 0.7.14(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.40.0(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.8 - version: 0.0.0-alpha.8(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + version: 0.0.0-alpha.8(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) '@erquhart/convex-oss-stats': specifier: ^0.5.5 - version: 0.5.5(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.1) + version: 0.5.5(convex@1.25.4(@clerk/clerk-react@5.40.0(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)(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) @@ -107,12 +113,15 @@ importers: axios: specifier: ^1.6.7 version: 1.7.8 + better-auth: + 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.17.2 - version: 1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.25.4 + version: 1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) d3: specifier: ^7.9.0 version: 7.9.0 @@ -169,7 +178,7 @@ importers: version: 6.0.3(@types/react@18.3.12)(react@19.0.0) remix-utils: specifier: ^8.5.0 - version: 8.5.0(react@19.0.0)(zod@3.24.1) + version: 8.5.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(react@19.0.0)(zod@4.0.17) remove-markdown: specifier: ^0.5.0 version: 0.5.0 @@ -186,8 +195,8 @@ importers: specifier: ^5.0.1 version: 5.0.1(typescript@5.6.3)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) zod: - specifier: ^3.23.8 - version: 3.24.1 + specifier: ^4.0.17 + version: 4.0.17 zustand: specifier: ^4.5.2 version: 4.5.2(@types/react@18.3.12)(react@19.0.0) @@ -306,6 +315,20 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@auth/core@0.37.0': + resolution: {integrity: sha512-LybAgfFC5dta3Mu3al0UbnzMGVBpZRqLMvvXupQOfETtPNlL7rXgTO13EVRTCdvPqMQrVYjODUDvgVfQM1M3Qg==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^6.8.0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -1159,9 +1182,11 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@clerk/backend@2.7.0': - resolution: {integrity: sha512-YrZwRd1X9Am1HGlIx01LoENZ5S7zDBmvHw0srUI5EhNBNY1HCWAd8iBF678o1qV95/sOB27A7BiKJiDIgnTiZg==} - engines: {node: '>=18.17.0'} + '@better-auth/utils@0.2.6': + resolution: {integrity: sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA==} + + '@better-fetch/fetch@1.1.18': + resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} '@clerk/clerk-react@5.40.0': resolution: {integrity: sha512-gRhubPU/U/XEjZv2AujFz5aKKJ5C4ogWxzHILOUjrENJI/EiAyO2B7UtWirW2AC6bHnROoRUQFn0CTi4LVlbig==} @@ -1182,15 +1207,6 @@ packages: react-dom: optional: true - '@clerk/tanstack-react-start@0.21.7': - resolution: {integrity: sha512-FzC1e4EUWuuQpXeqGxtOdfT9ZkG9eIztEjeTRnn71sezLrT0kcjowbRoK7MtkH8BXnQcWz+0kaz3ixJi5TOyDw==} - engines: {node: '>=18.17.0'} - peerDependencies: - '@tanstack/react-router': ^1.127.0 - '@tanstack/react-start': ^1.127.0 - react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/types@4.74.0': resolution: {integrity: sha512-9Wt2dWyCtCp2FngRkbHuQXPMttRjvGb77bmyCdGkPmNqQIZp7m4AZRdPtXhcGgxzaAYJkK6psgolbvuKDk+1jA==} engines: {node: '>=18.17.0'} @@ -1219,6 +1235,25 @@ packages: '@content-collections/core': ^0.x vite: ^5 + '@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.14': + resolution: {integrity: sha512-aZPFMazTG1wDhFyoaYpZx4PrDfcxychcKOAuoZ5JGQJ2F0FT6nVYkjGOqwn72XaO6WxieJBJJvIadRd1QWzXJQ==} + peerDependencies: + 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 + '@convex-dev/crons@0.1.5': resolution: {integrity: sha512-wQvqtWgmttq2iFkU7ObuB2vVmdywwwfY9Fl1j4FISYKypycYHu3rodZ06dkUqBlurzkC/hlYWrHirNKsFsEEUg==} peerDependencies: @@ -1247,12 +1282,6 @@ packages: convex: ~1.16.5 || ~1.17.0 || ~1.18.0 react: ^17.0.2 || ^18.0.0 || ^19.0.0-0 - '@esbuild/aix-ppc64@0.23.0': - resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.3': resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==} engines: {node: '>=18'} @@ -1277,12 +1306,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.23.0': - resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.3': resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==} engines: {node: '>=18'} @@ -1307,12 +1330,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.23.0': - resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.3': resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==} engines: {node: '>=18'} @@ -1337,12 +1354,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.23.0': - resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.3': resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==} engines: {node: '>=18'} @@ -1367,12 +1378,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.23.0': - resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.3': resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==} engines: {node: '>=18'} @@ -1397,12 +1402,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.23.0': - resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.3': resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==} engines: {node: '>=18'} @@ -1427,12 +1426,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.23.0': - resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.3': resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==} engines: {node: '>=18'} @@ -1457,12 +1450,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.0': - resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.3': resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==} engines: {node: '>=18'} @@ -1487,12 +1474,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.23.0': - resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.3': resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==} engines: {node: '>=18'} @@ -1517,12 +1498,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.23.0': - resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.3': resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==} engines: {node: '>=18'} @@ -1547,12 +1522,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.23.0': - resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.3': resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==} engines: {node: '>=18'} @@ -1577,12 +1546,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.23.0': - resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.3': resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==} engines: {node: '>=18'} @@ -1607,12 +1570,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.23.0': - resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.3': resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==} engines: {node: '>=18'} @@ -1637,12 +1594,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.23.0': - resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.3': resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==} engines: {node: '>=18'} @@ -1667,12 +1618,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.23.0': - resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.3': resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==} engines: {node: '>=18'} @@ -1697,12 +1642,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.23.0': - resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.3': resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==} engines: {node: '>=18'} @@ -1727,12 +1666,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.23.0': - resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.3': resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==} engines: {node: '>=18'} @@ -1781,12 +1714,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': - resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.3': resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==} engines: {node: '>=18'} @@ -1811,12 +1738,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.0': - resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.3': resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} engines: {node: '>=18'} @@ -1841,12 +1762,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.0': - resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.3': resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==} engines: {node: '>=18'} @@ -1877,12 +1792,6 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.23.0': - resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.3': resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==} engines: {node: '>=18'} @@ -1907,12 +1816,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.23.0': - resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.3': resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==} engines: {node: '>=18'} @@ -1937,12 +1840,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.23.0': - resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.3': resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==} engines: {node: '>=18'} @@ -1967,12 +1864,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.23.0': - resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.3': resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==} engines: {node: '>=18'} @@ -2046,6 +1937,9 @@ packages: react: ^16 || ^17 || ^18 react-dom: ^16 || ^17 || ^18 + '@hexagon/base64@1.1.28': + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -2089,6 +1983,9 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + '@lit-labs/ssr-dom-shim@1.2.1': resolution: {integrity: sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==} @@ -2151,9 +2048,12 @@ packages: '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} + '@noble/ciphers@0.6.0': + resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2383,6 +2283,21 @@ packages: '@oramacloud/client@2.1.4': resolution: {integrity: sha512-uNPFs4wq/iOPbggCwTkVNbIr64Vfd7ZS/h+cricXVnzXWocjDTfJ3wLL4lr0qiSu41g8z+eCAGBqJ30RO2O4AA==} + '@oslojs/asn1@1.0.0': + resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==} + + '@oslojs/binary@1.0.0': + resolution: {integrity: sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==} + + '@oslojs/crypto@1.0.1': + resolution: {integrity: sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@parcel/watcher-android-arm64@2.4.1': resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} engines: {node: '>= 10.0.0'} @@ -2547,6 +2462,21 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} + '@peculiar/asn1-android@2.4.0': + resolution: {integrity: sha512-YFueREq97CLslZZBI8dKzis7jMfEHSLxM+nr0Zdx1POiXFLjqqwoY5s0F1UimdBiEw/iKlHey2m56MRDv7Jtyg==} + + '@peculiar/asn1-ecc@2.4.0': + resolution: {integrity: sha512-fJiYUBCJBDkjh347zZe5H81BdJ0+OGIg0X9z06v8xXUoql3MFeENUX0JsjCaVaU9A0L85PefLPGYkIoGpTnXLQ==} + + '@peculiar/asn1-rsa@2.4.0': + resolution: {integrity: sha512-6PP75voaEnOSlWR9sD25iCQyLgFZHXbmxvUfnnDcfL6Zh5h2iHW38+bve4LfH7a60x7fkhZZNmiYqAlAff9Img==} + + '@peculiar/asn1-schema@2.4.0': + resolution: {integrity: sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ==} + + '@peculiar/asn1-x509@2.4.0': + resolution: {integrity: sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw==} + '@phosphor-icons/webcomponents@2.1.5': resolution: {integrity: sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==} @@ -3295,6 +3225,13 @@ packages: '@shikijs/transformers@1.10.3': resolution: {integrity: sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==} + '@simplewebauthn/browser@13.1.2': + resolution: {integrity: sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw==} + + '@simplewebauthn/server@13.1.2': + resolution: {integrity: sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g==} + engines: {node: '>=20.0.0'} + '@sindresorhus/is@7.0.2': resolution: {integrity: sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==} engines: {node: '>=18'} @@ -3306,9 +3243,6 @@ packages: '@speed-highlight/core@1.2.7': resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - '@stencil/core@4.20.0': resolution: {integrity: sha512-WPrTHFngvN081RY+dJPneKQLwnOFD60OMCOQGmmSHfCW0f4ujPMzzhwWU1gcSwXPWXz5O+8cBiiCaxAbJU7kAg==} engines: {node: '>=16.0.0', npm: '>=7.10.0'} @@ -4194,6 +4128,10 @@ packages: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} + asn1js@3.0.6: + resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + engines: {node: '>=12.0.0'} + ast-module-types@6.0.1: resolution: {integrity: sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==} engines: {node: '>=18'} @@ -4290,6 +4228,21 @@ packages: before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + 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.13: + resolution: {integrity: sha512-auqdP9lnNOli9tKpZIiv0nEIwmmyaD/RotM3Mucql+Ef88etoZi/t7Ph5LjlmZt/hiSahhNTt6YVnx6++rziXA==} + binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -4505,6 +4458,10 @@ packages: common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -4537,6 +4494,28 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convex-helpers@0.1.104: + resolution: {integrity: sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig==} + hasBin: true + peerDependencies: + '@standard-schema/spec': ^1.0.0 + convex: ^1.24.0 + hono: ^4.0.5 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + typescript: ^5.5 + zod: ^3.22.4 || ^4.0.15 + peerDependenciesMeta: + '@standard-schema/spec': + optional: true + hono: + optional: true + react: + optional: true + typescript: + optional: true + zod: + optional: true + convex-helpers@0.1.67: resolution: {integrity: sha512-nnb0W2FYKbqJt00UH6raZ/J0A1+CbcFdqmFJTDvvSR1P4RhwBj1TBzS3/B1G1nMYebX3gguSwOWfO0s2rcB/wg==} hasBin: true @@ -4553,15 +4532,14 @@ packages: zod: optional: true - convex@1.17.2: - resolution: {integrity: sha512-h12LChRZLGSGTIiqtBMyM7BT+zHxpUjtA1YI2vhFza7x+9E1CYzP8c2En6wKnZ9Dr2PYE4vWqUg8EHkBr0hI5A==} + convex@1.25.4: + resolution: {integrity: sha512-LiGZZTmbe5iHWwDOYfSA00w+uDM8kgLC0ohFJW0VgQlKcs8famHCE6yuplk4wwXyj9Lhb1+yMRfrAD2ZEquqHg==} engines: {node: '>=18.0.0', npm: '>=7.0.0'} hasBin: true peerDependencies: '@auth0/auth0-react': ^2.0.1 '@clerk/clerk-react': ^4.12.8 || ^5.0.0 - react: ^17.0.2 || ^18.0.0 || ^19.0.0-0 - react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0-0 + react: ^18.0.0 || ^19.0.0-0 || ^19.0.0 peerDependenciesMeta: '@auth0/auth0-react': optional: true @@ -4569,8 +4547,6 @@ packages: optional: true react: optional: true - react-dom: - optional: true cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} @@ -4586,6 +4562,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -5126,11 +5106,6 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - esbuild@0.23.0: - resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.3: resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} engines: {node: '>=18'} @@ -5384,9 +5359,6 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-sha256@1.3.0: - resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -5956,6 +5928,10 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-network-error@1.1.0: + resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} + engines: {node: '>=16'} + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -6083,6 +6059,9 @@ packages: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + jpeg-js@0.4.4: resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} @@ -6152,9 +6131,6 @@ packages: resolution: {integrity: sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==} engines: {node: '>=12.20'} - jwt-decode@3.1.2: - resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} - jwt-decode@4.0.0: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} @@ -6184,6 +6160,10 @@ packages: kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + kysely@0.28.5: + resolution: {integrity: sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA==} + engines: {node: '>=20.0.0'} + lambda-local@2.2.0: resolution: {integrity: sha512-bPcgpIXbHnVGfI/omZIlgucDqlf4LrsunwoKue5JdZeGybt8L6KyJz2Zu19ffuZwIwLj2NAI2ZyaqNT6/cetcg==} engines: {node: '>=8'} @@ -6353,6 +6333,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lucia@3.2.2: + resolution: {integrity: sha512-P1FlFBGCMPMXu+EGdVD9W4Mjm0DqsusmKgO7Xc33mI5X1bklmsQb0hfzPhXomQr9waWIBDsiOjvr1e6BTaUqpA==} + deprecated: This package has been deprecated. Please see https://lucia-auth.com/lucia-v3/migrate. + luxon@3.5.0: resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} engines: {node: '>=12'} @@ -6553,6 +6537,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanostores@0.11.4: + resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} + engines: {node: ^18.0.0 || >=20.0.0} + natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -6673,6 +6661,9 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + oauth4webapi@3.7.0: + resolution: {integrity: sha512-Q52wTPUWPsVLVVmTViXPQFMW2h2xv2jnDGxypjpelCFKaOjLsm7AxYuOk1oQgFm95VNDbuggasu9htXrz6XwKw==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -6870,6 +6861,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@3.0.0: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'} @@ -6957,6 +6951,14 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + preact-render-to-string@5.2.3: + resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} + peerDependencies: + preact: '>=10' + + preact@10.11.3: + resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + preact@10.26.5: resolution: {integrity: sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w==} @@ -6974,8 +6976,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true @@ -6988,6 +6990,9 @@ packages: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -7019,6 +7024,13 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + qs@6.13.1: resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} engines: {node: '>=0.6'} @@ -7346,6 +7358,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rou3@0.5.1: + resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -7434,6 +7449,9 @@ packages: set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7562,9 +7580,6 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - standardwebhooks@1.0.0: - resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} - statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -8332,12 +8347,12 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.0.17: + resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==} + zustand@4.5.2: resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} engines: {node: '>=12.7.0'} @@ -8441,6 +8456,16 @@ snapshots: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 + '@auth/core@0.37.0': + dependencies: + '@panva/hkdf': 1.2.1 + '@types/cookie': 0.6.0 + cookie: 0.7.1 + jose: 5.10.0 + oauth4webapi: 3.7.0 + preact: 10.11.3 + preact-render-to-string: 5.2.3(preact@10.11.3) + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -9539,16 +9564,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@clerk/backend@2.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@better-auth/utils@0.2.6': dependencies: - '@clerk/shared': 3.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@clerk/types': 4.74.0 - cookie: 1.0.2 - standardwebhooks: 1.0.0 - tslib: 2.8.1 - transitivePeerDependencies: - - react - - react-dom + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.18': {} '@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: @@ -9557,6 +9577,7 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) tslib: 2.8.1 + optional: true '@clerk/shared@3.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: @@ -9569,22 +9590,12 @@ snapshots: optionalDependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - - '@clerk/tanstack-react-start@0.21.7(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/react-start@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@clerk/backend': 2.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@clerk/clerk-react': 5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@clerk/shared': 3.18.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@clerk/types': 4.74.0 - '@tanstack/react-router': 1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/react-start': 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - tslib: 2.8.1 + optional: true '@clerk/types@4.74.0': dependencies: csstype: 3.1.3 + optional: true '@cloudflare/kv-asset-handler@0.4.0': dependencies: @@ -9605,7 +9616,7 @@ snapshots: tinyglobby: 0.2.13 typescript: 5.6.3 yaml: 2.7.1 - zod: 3.24.1 + zod: 3.25.76 '@content-collections/integrations@0.2.1(@content-collections/core@0.8.2(typescript@5.6.3))': dependencies: @@ -9617,15 +9628,48 @@ snapshots: '@content-collections/integrations': 0.2.1(@content-collections/core@0.8.2(typescript@5.6.3)) vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) - '@convex-dev/crons@0.1.5(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + '@convex-dev/auth@0.0.88(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.40.0(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 + '@oslojs/crypto': 1.0.1 + '@oslojs/encoding': 1.1.0 + convex: 1.25.4(@clerk/clerk-react@5.40.0(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.14(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.40.0(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.25.4(@clerk/clerk-react@5.40.0(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.40.0(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) + type-fest: 4.41.0 + zod: 3.25.76 + transitivePeerDependencies: + - '@standard-schema/spec' + - hono + - typescript + + '@convex-dev/crons@0.1.5(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': dependencies: - convex: 1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.40.0(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.8(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + '@convex-dev/react-query@0.0.0-alpha.8(@tanstack/react-query@5.84.2(react@19.0.0))(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': dependencies: '@tanstack/react-query': 5.84.2(react@19.0.0) - convex: 1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) '@dabh/diagnostics@2.0.3': dependencies: @@ -9644,14 +9688,14 @@ snapshots: tslib: 2.8.1 optional: true - '@erquhart/convex-oss-stats@0.5.5(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.1)': + '@erquhart/convex-oss-stats@0.5.5(convex@1.25.4(@clerk/clerk-react@5.40.0(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)(zod@4.0.17)': dependencies: - '@convex-dev/crons': 0.1.5(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@convex-dev/crons': 0.1.5(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) '@octokit/graphql': 8.1.2 '@octokit/graphql-schema': 15.25.0 cheerio: 1.0.0 - convex: 1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.67(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@3.24.1) + convex: 1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.67(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@4.0.17) date-fns: 4.1.0 framer-motion: 11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nano: 10.1.4 @@ -9668,9 +9712,6 @@ snapshots: - react-dom - zod - '@esbuild/aix-ppc64@0.23.0': - optional: true - '@esbuild/aix-ppc64@0.25.3': optional: true @@ -9683,9 +9724,6 @@ snapshots: '@esbuild/aix-ppc64@0.25.8': optional: true - '@esbuild/android-arm64@0.23.0': - optional: true - '@esbuild/android-arm64@0.25.3': optional: true @@ -9698,9 +9736,6 @@ snapshots: '@esbuild/android-arm64@0.25.8': optional: true - '@esbuild/android-arm@0.23.0': - optional: true - '@esbuild/android-arm@0.25.3': optional: true @@ -9713,9 +9748,6 @@ snapshots: '@esbuild/android-arm@0.25.8': optional: true - '@esbuild/android-x64@0.23.0': - optional: true - '@esbuild/android-x64@0.25.3': optional: true @@ -9728,9 +9760,6 @@ snapshots: '@esbuild/android-x64@0.25.8': optional: true - '@esbuild/darwin-arm64@0.23.0': - optional: true - '@esbuild/darwin-arm64@0.25.3': optional: true @@ -9743,9 +9772,6 @@ snapshots: '@esbuild/darwin-arm64@0.25.8': optional: true - '@esbuild/darwin-x64@0.23.0': - optional: true - '@esbuild/darwin-x64@0.25.3': optional: true @@ -9758,9 +9784,6 @@ snapshots: '@esbuild/darwin-x64@0.25.8': optional: true - '@esbuild/freebsd-arm64@0.23.0': - optional: true - '@esbuild/freebsd-arm64@0.25.3': optional: true @@ -9773,9 +9796,6 @@ snapshots: '@esbuild/freebsd-arm64@0.25.8': optional: true - '@esbuild/freebsd-x64@0.23.0': - optional: true - '@esbuild/freebsd-x64@0.25.3': optional: true @@ -9788,9 +9808,6 @@ snapshots: '@esbuild/freebsd-x64@0.25.8': optional: true - '@esbuild/linux-arm64@0.23.0': - optional: true - '@esbuild/linux-arm64@0.25.3': optional: true @@ -9803,9 +9820,6 @@ snapshots: '@esbuild/linux-arm64@0.25.8': optional: true - '@esbuild/linux-arm@0.23.0': - optional: true - '@esbuild/linux-arm@0.25.3': optional: true @@ -9818,9 +9832,6 @@ snapshots: '@esbuild/linux-arm@0.25.8': optional: true - '@esbuild/linux-ia32@0.23.0': - optional: true - '@esbuild/linux-ia32@0.25.3': optional: true @@ -9833,9 +9844,6 @@ snapshots: '@esbuild/linux-ia32@0.25.8': optional: true - '@esbuild/linux-loong64@0.23.0': - optional: true - '@esbuild/linux-loong64@0.25.3': optional: true @@ -9848,9 +9856,6 @@ snapshots: '@esbuild/linux-loong64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.23.0': - optional: true - '@esbuild/linux-mips64el@0.25.3': optional: true @@ -9863,9 +9868,6 @@ snapshots: '@esbuild/linux-mips64el@0.25.8': optional: true - '@esbuild/linux-ppc64@0.23.0': - optional: true - '@esbuild/linux-ppc64@0.25.3': optional: true @@ -9878,9 +9880,6 @@ snapshots: '@esbuild/linux-ppc64@0.25.8': optional: true - '@esbuild/linux-riscv64@0.23.0': - optional: true - '@esbuild/linux-riscv64@0.25.3': optional: true @@ -9893,9 +9892,6 @@ snapshots: '@esbuild/linux-riscv64@0.25.8': optional: true - '@esbuild/linux-s390x@0.23.0': - optional: true - '@esbuild/linux-s390x@0.25.3': optional: true @@ -9908,9 +9904,6 @@ snapshots: '@esbuild/linux-s390x@0.25.8': optional: true - '@esbuild/linux-x64@0.23.0': - optional: true - '@esbuild/linux-x64@0.25.3': optional: true @@ -9935,9 +9928,6 @@ snapshots: '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.23.0': - optional: true - '@esbuild/netbsd-x64@0.25.3': optional: true @@ -9950,9 +9940,6 @@ snapshots: '@esbuild/netbsd-x64@0.25.8': optional: true - '@esbuild/openbsd-arm64@0.23.0': - optional: true - '@esbuild/openbsd-arm64@0.25.3': optional: true @@ -9965,9 +9952,6 @@ snapshots: '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.23.0': - optional: true - '@esbuild/openbsd-x64@0.25.3': optional: true @@ -9983,9 +9967,6 @@ snapshots: '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/sunos-x64@0.23.0': - optional: true - '@esbuild/sunos-x64@0.25.3': optional: true @@ -9998,9 +9979,6 @@ snapshots: '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/win32-arm64@0.23.0': - optional: true - '@esbuild/win32-arm64@0.25.3': optional: true @@ -10013,9 +9991,6 @@ snapshots: '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/win32-ia32@0.23.0': - optional: true - '@esbuild/win32-ia32@0.25.3': optional: true @@ -10028,9 +10003,6 @@ snapshots: '@esbuild/win32-ia32@0.25.8': optional: true - '@esbuild/win32-x64@0.23.0': - optional: true - '@esbuild/win32-x64@0.25.3': optional: true @@ -10100,6 +10072,8 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) + '@hexagon/base64@1.1.28': {} + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.2 @@ -10148,6 +10122,8 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@levischuck/tiny-cbor@0.2.11': {} + '@lit-labs/ssr-dom-shim@1.2.1': {} '@lit/reactive-element@2.0.4': @@ -10277,7 +10253,7 @@ snapshots: unixify: 1.0.0 urlpattern-polyfill: 8.0.2 yargs: 17.7.2 - zod: 3.24.1 + zod: 3.25.76 transitivePeerDependencies: - encoding - rollup @@ -10287,7 +10263,9 @@ snapshots: dependencies: eslint-scope: 5.1.1 - '@noble/hashes@1.4.0': {} + '@noble/ciphers@0.6.0': {} + + '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -10550,7 +10528,7 @@ snapshots: '@orama/cuid2@2.2.3': dependencies: - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.8.0 '@orama/highlight@0.1.8': {} @@ -10590,6 +10568,21 @@ snapshots: '@orama/orama': 3.0.3 lodash: 4.17.21 + '@oslojs/asn1@1.0.0': + dependencies: + '@oslojs/binary': 1.0.0 + + '@oslojs/binary@1.0.0': {} + + '@oslojs/crypto@1.0.1': + dependencies: + '@oslojs/asn1': 1.0.0 + '@oslojs/binary': 1.0.0 + + '@oslojs/encoding@1.1.0': {} + + '@panva/hkdf@1.2.1': {} + '@parcel/watcher-android-arm64@2.4.1': optional: true @@ -10711,6 +10704,39 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.1 '@parcel/watcher-win32-x64': 2.5.1 + '@peculiar/asn1-android@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.4.0': + dependencies: + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + '@phosphor-icons/webcomponents@2.1.5': dependencies: lit: 3.2.0 @@ -11405,14 +11431,24 @@ snapshots: dependencies: shiki: 1.10.3 + '@simplewebauthn/browser@13.1.2': {} + + '@simplewebauthn/server@13.1.2': + dependencies: + '@hexagon/base64': 1.1.28 + '@levischuck/tiny-cbor': 0.2.11 + '@peculiar/asn1-android': 2.4.0 + '@peculiar/asn1-ecc': 2.4.0 + '@peculiar/asn1-rsa': 2.4.0 + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + '@sindresorhus/is@7.0.2': {} '@sindresorhus/merge-streams@2.3.0': {} '@speed-highlight/core@1.2.7': {} - '@stablelib/base64@1.0.1': {} - '@stencil/core@4.20.0': {} '@stencil/store@2.0.16(@stencil/core@4.20.0)': @@ -12626,6 +12662,12 @@ snapshots: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 + asn1js@3.0.6: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + ast-module-types@6.0.1: {} ast-types-flow@0.0.8: {} @@ -12756,6 +12798,31 @@ snapshots: before-after-hook@3.0.2: {} + 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 + '@noble/ciphers': 0.6.0 + '@noble/hashes': 1.8.0 + '@simplewebauthn/browser': 13.1.2 + '@simplewebauthn/server': 13.1.2 + better-call: 1.0.13 + defu: 6.1.4 + jose: 5.10.0 + kysely: 0.28.5 + nanostores: 0.11.4 + zod: 4.0.17 + optionalDependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + better-call@1.0.13: + dependencies: + '@better-fetch/fetch': 1.1.18 + rou3: 0.5.1 + set-cookie-parser: 2.7.1 + uncrypto: 0.1.3 + binary-extensions@2.2.0: {} binary-search-bounds@2.0.5: {} @@ -13011,6 +13078,8 @@ snapshots: common-path-prefix@3.0.0: {} + common-tags@1.8.2: {} + commondir@1.0.1: {} compatx@0.2.0: {} @@ -13037,22 +13106,29 @@ snapshots: convert-source-map@2.0.0: {} - convex-helpers@0.1.67(convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@3.24.1): + convex-helpers@0.1.104(convex@1.25.4(@clerk/clerk-react@5.40.0(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.25.4(@clerk/clerk-react@5.40.0(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.67(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@4.0.17): dependencies: - convex: 1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + convex: 1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: react: 19.0.0 - zod: 3.24.1 + zod: 4.0.17 - convex@1.17.2(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): dependencies: - esbuild: 0.23.0 - jwt-decode: 3.1.2 - prettier: 3.2.5 + esbuild: 0.25.4 + jwt-decode: 4.0.0 + prettier: 3.5.3 optionalDependencies: '@clerk/clerk-react': 5.40.0(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) cookie-es@1.2.2: {} @@ -13062,6 +13138,8 @@ snapshots: cookie@0.6.0: {} + cookie@0.7.1: {} + cookie@1.0.2: {} copy-file@11.0.0: @@ -13640,33 +13718,6 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - esbuild@0.23.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 - esbuild@0.25.3: optionalDependencies: '@esbuild/aix-ppc64': 0.25.3 @@ -14103,8 +14154,6 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-sha256@1.3.0: {} - fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -14292,7 +14341,8 @@ snapshots: dependencies: is-glob: 4.0.3 - glob-to-regexp@0.4.1: {} + glob-to-regexp@0.4.1: + optional: true glob@10.4.5: dependencies: @@ -14689,6 +14739,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-network-error@1.1.0: {} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -14795,10 +14847,13 @@ snapshots: jiti@2.5.1: {} + jose@5.10.0: {} + jpeg-js@0.4.4: optional: true - js-cookie@3.0.5: {} + js-cookie@3.0.5: + optional: true js-image-generator@1.0.4: dependencies: @@ -14849,8 +14904,6 @@ snapshots: junk@4.0.1: {} - jwt-decode@3.1.2: {} - jwt-decode@4.0.0: {} keyv@4.5.4: @@ -14871,6 +14924,8 @@ snapshots: kuler@2.0.0: {} + kysely@0.28.5: {} + lambda-local@2.2.0: dependencies: commander: 10.0.1 @@ -15046,6 +15101,11 @@ snapshots: lru-cache@7.18.3: {} + lucia@3.2.2: + dependencies: + '@oslojs/crypto': 1.0.1 + '@oslojs/encoding': 1.1.0 + luxon@3.5.0: {} magic-string@0.30.17: @@ -15231,6 +15291,8 @@ snapshots: nanoid@3.3.8: {} + nanostores@0.11.4: {} + natural-compare-lite@1.4.0: {} natural-compare@1.4.0: {} @@ -15449,6 +15511,8 @@ snapshots: pkg-types: 2.2.0 tinyexec: 1.0.1 + oauth4webapi@3.7.0: {} + object-assign@4.1.1: {} object-inspect@1.13.1: {} @@ -15670,6 +15734,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + path-type@3.0.0: dependencies: pify: 3.0.0 @@ -15746,6 +15812,13 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact-render-to-string@5.2.3(preact@10.11.3): + dependencies: + preact: 10.11.3 + pretty-format: 3.8.0 + + preact@10.11.3: {} + preact@10.26.5: {} precinct@12.2.0: @@ -15772,12 +15845,14 @@ snapshots: prettier@2.8.8: {} - prettier@3.2.5: {} + prettier@3.5.3: {} prettier@3.6.2: {} pretty-bytes@6.1.1: {} + pretty-format@3.8.0: {} + process-nextick-args@2.0.1: {} process@0.11.10: {} @@ -15805,6 +15880,12 @@ snapshots: punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + qs@6.13.1: dependencies: side-channel: 1.0.6 @@ -16071,12 +16152,14 @@ snapshots: dependencies: type-fest: 4.30.0 - remix-utils@8.5.0(react@19.0.0)(zod@3.24.1): + remix-utils@8.5.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(react@19.0.0)(zod@4.0.17): dependencies: type-fest: 4.40.0 optionalDependencies: + '@oslojs/crypto': 1.0.1 + '@oslojs/encoding': 1.1.0 react: 19.0.0 - zod: 3.24.1 + zod: 4.0.17 remove-markdown@0.5.0: {} @@ -16173,6 +16256,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.46.2 fsevents: 2.3.3 + rou3@0.5.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -16262,6 +16347,8 @@ snapshots: set-cookie-parser@2.6.0: {} + set-cookie-parser@2.7.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -16396,11 +16483,6 @@ snapshots: standard-as-callback@2.1.0: {} - standardwebhooks@1.0.0: - dependencies: - '@stablelib/base64': 1.0.1 - fast-sha256: 1.3.0 - statuses@2.0.1: {} statuses@2.0.2: {} @@ -16527,6 +16609,7 @@ snapshots: dequal: 2.0.3 react: 19.0.0 use-sync-external-store: 1.5.0(react@19.0.0) + optional: true system-architecture@0.1.0: {} @@ -17173,10 +17256,10 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 - zod@3.24.1: {} - zod@3.25.76: {} + zod@4.0.17: {} + 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/libraries/auth-client.ts b/src/libraries/auth-client.ts new file mode 100644 index 00000000..bfc613e7 --- /dev/null +++ b/src/libraries/auth-client.ts @@ -0,0 +1,8 @@ +import { createAuthClient } from "better-auth/react"; +import { convexClient } from "@convex-dev/better-auth/client/plugins"; + +export const authClient = createAuthClient({ + plugins: [ + convexClient(), + ], +}); \ No newline at end of file diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts new file mode 100644 index 00000000..3e567ade --- /dev/null +++ b/src/libraries/auth.ts @@ -0,0 +1,32 @@ +import { convexAdapter } from "@convex-dev/better-auth"; +import { convex } from "@convex-dev/better-auth/plugins"; +import { betterAuth } from "better-auth"; +import { betterAuthComponent } from "../../convex/auth"; +import { type GenericCtx } from "../../convex/_generated/server"; + +// You'll want to replace this with an environment variable +const siteUrl = "http://localhost:3000"; + +export const createAuth = (ctx: GenericCtx) => + // Configure your Better Auth instance here + 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(), + ], + }); \ No newline at end of file diff --git a/src/libraries/server-auth-utils.ts b/src/libraries/server-auth-utils.ts new file mode 100644 index 00000000..ac424782 --- /dev/null +++ b/src/libraries/server-auth-utils.ts @@ -0,0 +1,8 @@ +import { createAuth } from './auth' +import { reactStartHelpers } from '@convex-dev/better-auth/react-start' +import { env } from '../utils/env' + +export const { fetchSession, reactStartHandler, getCookieName} = + reactStartHelpers(createAuth, { + convexSiteUrl: env.VITE_CONVEX_SITE_URL, + }) \ No newline at end of file diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 9c4b3f08..829ea972 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -18,6 +18,7 @@ import { createServerRootRoute } from '@tanstack/react-start/server' import { Route as rootRouteImport } from './routes/__root' import { Route as SponsorsEmbedRouteImport } from './routes/sponsors-embed' import { Route as MerchRouteImport } from './routes/merch' +import { Route as BuilderRouteImport } from './routes/builder' import { Route as LibrariesRouteRouteImport } from './routes/_libraries/route' import { Route as LibraryIdRouteRouteImport } from './routes/$libraryId/route' import { Route as StatsIndexRouteImport } from './routes/stats/index' @@ -32,6 +33,7 @@ import { Route as LibrariesMaintainersRouteImport } from './routes/_libraries/ma import { Route as LibrariesLoginRouteImport } from './routes/_libraries/login' import { Route as LibrariesLearnRouteImport } from './routes/_libraries/learn' import { Route as LibrariesEthosRouteImport } from './routes/_libraries/ethos' +import { Route as LibrariesDashboardRouteImport } from './routes/_libraries/dashboard' import { Route as LibrariesBlogRouteImport } from './routes/_libraries/blog' import { Route as LibraryIdVersionRouteImport } from './routes/$libraryId/$version' import { Route as StatsNpmIndexRouteImport } from './routes/stats/npm/index' @@ -58,7 +60,7 @@ import { Route as LibraryIdVersionDocsFrameworkIndexRouteImport } from './routes import { Route as LibraryIdVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$libraryId/$version.docs.framework.$framework.index' import { Route as LibraryIdVersionDocsFrameworkFrameworkSplatRouteImport } from './routes/$libraryId/$version.docs.framework.$framework.$' import { Route as LibraryIdVersionDocsFrameworkFrameworkExamplesSplatRouteImport } from './routes/$libraryId/$version.docs.framework.$framework.examples.$' -import { ServerRoute as ApiInitialPayloadServerRouteImport } from './routes/api/initial-payload' +import { ServerRoute as ApiAuthSplatServerRouteImport } from './routes/api/auth/$' import { ServerRoute as LibraryIdVersionDocsChar123Char125DotmdServerRouteImport } from './routes/$libraryId/$version.docs.{$}[.]md' import { ServerRoute as LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRouteImport } from './routes/$libraryId/$version.docs.framework.$framework.{$}[.]md' @@ -74,6 +76,11 @@ const MerchRoute = MerchRouteImport.update({ path: '/merch', getParentRoute: () => rootRouteImport, } as any) +const BuilderRoute = BuilderRouteImport.update({ + id: '/builder', + path: '/builder', + getParentRoute: () => rootRouteImport, +} as any) const LibrariesRouteRoute = LibrariesRouteRouteImport.update({ id: '/_libraries', getParentRoute: () => rootRouteImport, @@ -143,6 +150,11 @@ const LibrariesEthosRoute = LibrariesEthosRouteImport.update({ path: '/ethos', getParentRoute: () => LibrariesRouteRoute, } as any) +const LibrariesDashboardRoute = LibrariesDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => LibrariesRouteRoute, +} as any) const LibrariesBlogRoute = LibrariesBlogRouteImport.update({ id: '/blog', path: '/blog', @@ -291,9 +303,9 @@ const LibraryIdVersionDocsFrameworkFrameworkExamplesSplatRoute = path: '/framework/$framework/examples/$', getParentRoute: () => LibraryIdVersionDocsRoute, } as any) -const ApiInitialPayloadServerRoute = ApiInitialPayloadServerRouteImport.update({ - id: '/api/initial-payload', - path: '/api/initial-payload', +const ApiAuthSplatServerRoute = ApiAuthSplatServerRouteImport.update({ + id: '/api/auth/$', + path: '/api/auth/$', getParentRoute: () => rootServerRouteImport, } as any) const LibraryIdVersionDocsChar123Char125DotmdServerRoute = @@ -313,10 +325,12 @@ const LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute = export interface FileRoutesByFullPath { '/$libraryId': typeof LibraryIdRouteRouteWithChildren + '/builder': typeof BuilderRoute '/merch': typeof MerchRoute '/sponsors-embed': typeof SponsorsEmbedRoute '/$libraryId/$version': typeof LibraryIdVersionRouteWithChildren '/blog': typeof LibrariesBlogRouteWithChildren + '/dashboard': typeof LibrariesDashboardRoute '/ethos': typeof LibrariesEthosRoute '/learn': typeof LibrariesLearnRoute '/login': typeof LibrariesLoginRoute @@ -355,9 +369,11 @@ export interface FileRoutesByFullPath { '/$libraryId/$version/docs/framework/$framework/examples/$': typeof LibraryIdVersionDocsFrameworkFrameworkExamplesSplatRoute } export interface FileRoutesByTo { + '/builder': typeof BuilderRoute '/merch': typeof MerchRoute '/sponsors-embed': typeof SponsorsEmbedRoute '/$libraryId/$version': typeof LibraryIdVersionRouteWithChildren + '/dashboard': typeof LibrariesDashboardRoute '/ethos': typeof LibrariesEthosRoute '/learn': typeof LibrariesLearnRoute '/login': typeof LibrariesLoginRoute @@ -398,10 +414,12 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/$libraryId': typeof LibraryIdRouteRouteWithChildren '/_libraries': typeof LibrariesRouteRouteWithChildren + '/builder': typeof BuilderRoute '/merch': typeof MerchRoute '/sponsors-embed': typeof SponsorsEmbedRoute '/$libraryId/$version': typeof LibraryIdVersionRouteWithChildren '/_libraries/blog': typeof LibrariesBlogRouteWithChildren + '/_libraries/dashboard': typeof LibrariesDashboardRoute '/_libraries/ethos': typeof LibrariesEthosRoute '/_libraries/learn': typeof LibrariesLearnRoute '/_libraries/login': typeof LibrariesLoginRoute @@ -443,10 +461,12 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/$libraryId' + | '/builder' | '/merch' | '/sponsors-embed' | '/$libraryId/$version' | '/blog' + | '/dashboard' | '/ethos' | '/learn' | '/login' @@ -485,9 +505,11 @@ export interface FileRouteTypes { | '/$libraryId/$version/docs/framework/$framework/examples/$' fileRoutesByTo: FileRoutesByTo to: + | '/builder' | '/merch' | '/sponsors-embed' | '/$libraryId/$version' + | '/dashboard' | '/ethos' | '/learn' | '/login' @@ -527,10 +549,12 @@ export interface FileRouteTypes { | '__root__' | '/$libraryId' | '/_libraries' + | '/builder' | '/merch' | '/sponsors-embed' | '/$libraryId/$version' | '/_libraries/blog' + | '/_libraries/dashboard' | '/_libraries/ethos' | '/_libraries/learn' | '/_libraries/login' @@ -572,47 +596,48 @@ export interface FileRouteTypes { export interface RootRouteChildren { LibraryIdRouteRoute: typeof LibraryIdRouteRouteWithChildren LibrariesRouteRoute: typeof LibrariesRouteRouteWithChildren + BuilderRoute: typeof BuilderRoute MerchRoute: typeof MerchRoute SponsorsEmbedRoute: typeof SponsorsEmbedRoute StatsIndexRoute: typeof StatsIndexRoute StatsNpmIndexRoute: typeof StatsNpmIndexRoute } export interface FileServerRoutesByFullPath { - '/api/initial-payload': typeof ApiInitialPayloadServerRoute + '/api/auth/$': typeof ApiAuthSplatServerRoute '/$libraryId/$version/docs/{$}.md': typeof LibraryIdVersionDocsChar123Char125DotmdServerRoute '/$libraryId/$version/docs/framework/$framework/{$}.md': typeof LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute } export interface FileServerRoutesByTo { - '/api/initial-payload': typeof ApiInitialPayloadServerRoute + '/api/auth/$': typeof ApiAuthSplatServerRoute '/$libraryId/$version/docs/{$}.md': typeof LibraryIdVersionDocsChar123Char125DotmdServerRoute '/$libraryId/$version/docs/framework/$framework/{$}.md': typeof LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute } export interface FileServerRoutesById { __root__: typeof rootServerRouteImport - '/api/initial-payload': typeof ApiInitialPayloadServerRoute + '/api/auth/$': typeof ApiAuthSplatServerRoute '/$libraryId/$version/docs/{$}.md': typeof LibraryIdVersionDocsChar123Char125DotmdServerRoute '/$libraryId/$version/docs/framework/$framework/{$}.md': typeof LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute } export interface FileServerRouteTypes { fileServerRoutesByFullPath: FileServerRoutesByFullPath fullPaths: - | '/api/initial-payload' + | '/api/auth/$' | '/$libraryId/$version/docs/{$}.md' | '/$libraryId/$version/docs/framework/$framework/{$}.md' fileServerRoutesByTo: FileServerRoutesByTo to: - | '/api/initial-payload' + | '/api/auth/$' | '/$libraryId/$version/docs/{$}.md' | '/$libraryId/$version/docs/framework/$framework/{$}.md' id: | '__root__' - | '/api/initial-payload' + | '/api/auth/$' | '/$libraryId/$version/docs/{$}.md' | '/$libraryId/$version/docs/framework/$framework/{$}.md' fileServerRoutesById: FileServerRoutesById } export interface RootServerRouteChildren { - ApiInitialPayloadServerRoute: typeof ApiInitialPayloadServerRoute + ApiAuthSplatServerRoute: typeof ApiAuthSplatServerRoute LibraryIdVersionDocsChar123Char125DotmdServerRoute: typeof LibraryIdVersionDocsChar123Char125DotmdServerRoute LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute: typeof LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute } @@ -633,6 +658,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibrariesRouteRouteImport parentRoute: typeof rootRouteImport } + '/builder': { + id: '/builder' + path: '/builder' + fullPath: '/builder' + preLoaderRoute: typeof BuilderRouteImport + parentRoute: typeof rootRouteImport + } '/merch': { id: '/merch' path: '/merch' @@ -661,6 +693,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibrariesBlogRouteImport parentRoute: typeof LibrariesRouteRoute } + '/_libraries/dashboard': { + id: '/_libraries/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof LibrariesDashboardRouteImport + parentRoute: typeof LibrariesRouteRoute + } '/_libraries/ethos': { id: '/_libraries/ethos' path: '/ethos' @@ -773,6 +812,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibrariesBlogSplatRouteImport parentRoute: typeof LibrariesBlogRoute } + '/api/auth/$': { + id: '/api/auth/$' + path: '' + fullPath: '/api/auth/$' + preLoaderRoute: unknown + parentRoute: typeof rootRouteImport + } '/_libraries/blog/': { id: '/_libraries/blog/' path: '/' @@ -952,6 +998,13 @@ declare module '@tanstack/react-start/server' { preLoaderRoute: unknown parentRoute: typeof rootServerRouteImport } + '/builder': { + id: '/builder' + path: '/builder' + fullPath: '/builder' + preLoaderRoute: unknown + parentRoute: typeof rootServerRouteImport + } '/merch': { id: '/merch' path: '/merch' @@ -980,6 +1033,13 @@ declare module '@tanstack/react-start/server' { preLoaderRoute: unknown parentRoute: typeof rootServerRouteImport } + '/_libraries/dashboard': { + id: '/_libraries/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: unknown + parentRoute: typeof rootServerRouteImport + } '/_libraries/ethos': { id: '/_libraries/ethos' path: '/ethos' @@ -1092,6 +1152,13 @@ declare module '@tanstack/react-start/server' { preLoaderRoute: unknown parentRoute: typeof rootServerRouteImport } + '/api/auth/$': { + id: '/api/auth/$' + path: '/api/auth/$' + fullPath: '/api/auth/$' + preLoaderRoute: typeof ApiAuthSplatServerRouteImport + parentRoute: typeof rootServerRouteImport + } '/_libraries/blog/': { id: '/_libraries/blog/' path: '/' @@ -1290,6 +1357,23 @@ declare module './routes/_libraries/route' { unknown > } +declare module './routes/builder' { + const createFileRoute: CreateFileRoute< + '/builder', + FileRoutesByPath['/builder']['parentRoute'], + FileRoutesByPath['/builder']['id'], + FileRoutesByPath['/builder']['path'], + FileRoutesByPath['/builder']['fullPath'] + > + + const createServerFileRoute: CreateServerFileRoute< + ServerFileRoutesByPath['/builder']['parentRoute'], + ServerFileRoutesByPath['/builder']['id'], + ServerFileRoutesByPath['/builder']['path'], + ServerFileRoutesByPath['/builder']['fullPath'], + unknown + > +} declare module './routes/merch' { const createFileRoute: CreateFileRoute< '/merch', @@ -1358,6 +1442,23 @@ declare module './routes/_libraries/blog' { unknown > } +declare module './routes/_libraries/dashboard' { + const createFileRoute: CreateFileRoute< + '/_libraries/dashboard', + FileRoutesByPath['/_libraries/dashboard']['parentRoute'], + FileRoutesByPath['/_libraries/dashboard']['id'], + FileRoutesByPath['/_libraries/dashboard']['path'], + FileRoutesByPath['/_libraries/dashboard']['fullPath'] + > + + const createServerFileRoute: CreateServerFileRoute< + ServerFileRoutesByPath['/_libraries/dashboard']['parentRoute'], + ServerFileRoutesByPath['/_libraries/dashboard']['id'], + ServerFileRoutesByPath['/_libraries/dashboard']['path'], + ServerFileRoutesByPath['/_libraries/dashboard']['fullPath'], + unknown + > +} declare module './routes/_libraries/ethos' { const createFileRoute: CreateFileRoute< '/_libraries/ethos', @@ -1630,6 +1731,23 @@ declare module './routes/_libraries/blog.$' { unknown > } +declare module './routes/api/auth/$' { + const createFileRoute: CreateFileRoute< + '/api/auth/$', + FileRoutesByPath['/api/auth/$']['parentRoute'], + FileRoutesByPath['/api/auth/$']['id'], + FileRoutesByPath['/api/auth/$']['path'], + FileRoutesByPath['/api/auth/$']['fullPath'] + > + + const createServerFileRoute: CreateServerFileRoute< + ServerFileRoutesByPath['/api/auth/$']['parentRoute'], + ServerFileRoutesByPath['/api/auth/$']['id'], + ServerFileRoutesByPath['/api/auth/$']['path'], + ServerFileRoutesByPath['/api/auth/$']['fullPath'], + unknown + > +} declare module './routes/_libraries/blog.index' { const createFileRoute: CreateFileRoute< '/_libraries/blog/', @@ -2090,6 +2208,7 @@ const LibrariesBlogRouteWithChildren = LibrariesBlogRoute._addFileChildren( interface LibrariesRouteRouteChildren { LibrariesBlogRoute: typeof LibrariesBlogRouteWithChildren + LibrariesDashboardRoute: typeof LibrariesDashboardRoute LibrariesEthosRoute: typeof LibrariesEthosRoute LibrariesLearnRoute: typeof LibrariesLearnRoute LibrariesLoginRoute: typeof LibrariesLoginRoute @@ -2117,6 +2236,7 @@ interface LibrariesRouteRouteChildren { const LibrariesRouteRouteChildren: LibrariesRouteRouteChildren = { LibrariesBlogRoute: LibrariesBlogRouteWithChildren, + LibrariesDashboardRoute: LibrariesDashboardRoute, LibrariesEthosRoute: LibrariesEthosRoute, LibrariesLearnRoute: LibrariesLearnRoute, LibrariesLoginRoute: LibrariesLoginRoute, @@ -2149,6 +2269,7 @@ const LibrariesRouteRouteWithChildren = LibrariesRouteRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { LibraryIdRouteRoute: LibraryIdRouteRouteWithChildren, LibrariesRouteRoute: LibrariesRouteRouteWithChildren, + BuilderRoute: BuilderRoute, MerchRoute: MerchRoute, SponsorsEmbedRoute: SponsorsEmbedRoute, StatsIndexRoute: StatsIndexRoute, @@ -2158,7 +2279,7 @@ export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() const rootServerRouteChildren: RootServerRouteChildren = { - ApiInitialPayloadServerRoute: ApiInitialPayloadServerRoute, + ApiAuthSplatServerRoute: ApiAuthSplatServerRoute, LibraryIdVersionDocsChar123Char125DotmdServerRoute: LibraryIdVersionDocsChar123Char125DotmdServerRoute, LibraryIdVersionDocsFrameworkFrameworkChar123Char125DotmdServerRoute: diff --git a/src/router.tsx b/src/router.tsx index d215817a..1b2c5941 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -7,11 +7,11 @@ import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' import { QueryClient } from '@tanstack/react-query' import { GamOnPageChange } from './components/Gam' -import { ClerkProvider } from '@clerk/tanstack-react-start' +import { env } from './utils/env' export function createRouter() { const CONVEX_URL = - (import.meta as any).env.VITE_CONVEX_URL || + env.VITE_CONVEX_URL || // Hardcoded production URL as fallback for local development // Currently set to an instance owned by Convex Devx // TODO: Replace with URL to an instance owned by the TanStack team @@ -41,6 +41,8 @@ export function createRouter() { }, context: { queryClient, + convexClient: convexQueryClient.convexClient, + convexQueryClient, }, Wrap: ({ children }) => ( diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 8790f65c..2ada9035 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -7,9 +7,10 @@ import { useRouterState, HeadContent, Scripts, + useRouteContext, } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' import { QueryClient } from '@tanstack/react-query' -import { ClerkProvider } from '@clerk/tanstack-react-start' import appCss from '~/styles/app.css?url' import carbonStyles from '~/styles/carbon.css?url' import { seo } from '~/utils/seo' @@ -23,9 +24,29 @@ import { BackgroundAnimation } from '~/components/BackgroundAnimation' import { SearchProvider } from '~/contexts/SearchContext' import { SearchModal } from '~/components/SearchModal' import { ThemeProvider } from '~/components/ThemeProvider' +import { ConvexQueryClient } from '@convex-dev/react-query' +import { ConvexReactClient } from 'convex/react' +import { getCookie, getWebRequest } from '@tanstack/react-start/server' +import { ConvexBetterAuthProvider } from '@convex-dev/better-auth/react' +import { authClient } from '../libraries/auth-client' +import { fetchSession, getCookieName } from '../libraries/server-auth-utils' + +// Server side session request +const fetchAuth = createServerFn({ method: 'GET' }).handler(async () => { + const sessionCookieName = await getCookieName() + const token = getCookie(sessionCookieName) + const request = getWebRequest() + const { session } = await fetchSession(request) + return { + userId: session?.user.id, + token, + } +}) export const Route = createRootRouteWithContext<{ queryClient: QueryClient + convexClient: ConvexReactClient + convexQueryClient: ConvexQueryClient }>()({ head: () => ({ meta: [ @@ -123,6 +144,19 @@ export const Route = createRootRouteWithContext<{ ), }) } + + // all queries, mutations and action made with TanStack Query will be + // authenticated by an identity token. + const auth = await fetchAuth() + const { userId, token } = auth + + // 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) + } + + return { userId, token } }, staleTime: Infinity, errorComponent: (props) => { @@ -143,15 +177,13 @@ export const Route = createRootRouteWithContext<{ }) function RootComponent() { - // Import your Publishable Key - const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY - - if (!PUBLISHABLE_KEY) { - throw new Error('Add your Clerk Publishable Key to the .env file') - } + const context = useRouteContext({ from: Route.id }) return ( - + @@ -159,7 +191,7 @@ function RootComponent() { - + ) } diff --git a/src/routes/_libraries/account.$.tsx b/src/routes/_libraries/account.$.tsx index 39360aaa..19448d1f 100644 --- a/src/routes/_libraries/account.$.tsx +++ b/src/routes/_libraries/account.$.tsx @@ -1,42 +1,65 @@ -import * as React from 'react' -import { - SignedIn, - SignedOut, - UserProfile, - SignInButton, - SignOutButton, - useUser, -} from '@clerk/tanstack-react-start' import { useUserSettingsStore } from '~/stores/userSettings' import { FaSignOutAlt } from 'react-icons/fa' +import { + Authenticated, + Unauthenticated, + useConvexAuth, + useQuery, +} from 'convex/react' +import { Link, redirect } from '@tanstack/react-router' +import { api } from 'convex/_generated/api' +import { authClient } from '~/libraries/auth-client' export const Route = createFileRoute({ component: AccountPage, }) -function AccountPage() { - const { isLoaded } = useUser() +function UserSettings() { + const { isLoading } = useConvexAuth() + const user = useQuery(api.auth.getCurrentUser) const adsDisabled = useUserSettingsStore((s) => s.settings.adsDisabled) const toggleAds = useUserSettingsStore((s) => s.toggleAds) + + const signOut = async () => { + await authClient.signOut() + redirect({ to: '/login' }) + } return ( -
- -
- -
-
+ <> +
+

My Account

+
+
+

+ Connections +

+
+
+ + +
+
+
+
+

Preferences

+
- - - +
- +
+ + ) +} - -
+function AccountPage() { + return ( +
+ + + + +

Sign In Required

Please sign in to access your account settings.

- - - +
- +
) } diff --git a/src/routes/_libraries/dashboard.tsx b/src/routes/_libraries/dashboard.tsx new file mode 100644 index 00000000..8e8f2ca9 --- /dev/null +++ b/src/routes/_libraries/dashboard.tsx @@ -0,0 +1,60 @@ +import { useState, useEffect } from 'react' +import { FaArrowRight } from 'react-icons/fa' +import { PiHandWavingLight } from 'react-icons/pi' + +export const Route = createFileRoute({ + component: Dashboard, +}) + +function Dashboard() { + const [waved, setWaved] = useState(false) + useEffect(() => { + // Trigger wave on mount once + setWaved(true) + }, []) + + return ( +
+
+
+ +
+
+
+ +
+
+

+ Welcome! +

+

+ There's not much to see here yet.
We're working on it! +

+ +
+
+
+
+ ) +} diff --git a/src/routes/_libraries/login.tsx b/src/routes/_libraries/login.tsx index fa0fdce2..5a68618c 100644 --- a/src/routes/_libraries/login.tsx +++ b/src/routes/_libraries/login.tsx @@ -1,47 +1,63 @@ -import { - SignedOut, - SignedIn, - UserButton, - Waitlist, - SignIn, -} from '@clerk/tanstack-react-start' -import * as React from 'react' +import { authClient } from '~/libraries/auth-client' +import { useIsDark } from '~/hooks/useIsDark' +import { FaGithub, FaGoogle } from 'react-icons/fa' export const Route = createFileRoute({ component: LoginPage, }) +function SplashImage() { + const isDark = useIsDark() + return ( +
+ Waitlist +
+ ) +} + +function SignInForm() { + return ( +
+ +

+ Sign into your TanStack Account +

+ + +
+ ) +} + function LoginPage() { return ( -
+
- - - - - -
-
- -
-

- Welcome! -

-

- You've been approved from the waitlist and have full access to - TanStack. -

- -
-
+
diff --git a/src/routes/_libraries/route.tsx b/src/routes/_libraries/route.tsx index 4c818a8f..6a897926 100644 --- a/src/routes/_libraries/route.tsx +++ b/src/routes/_libraries/route.tsx @@ -1,6 +1,5 @@ import * as React from 'react' import { Link, Outlet, useLocation } from '@tanstack/react-router' -import { SignedIn, SignedOut, UserButton } from '@clerk/tanstack-react-start' import { CgClose, CgMenuLeft, CgMusicSpeaker } from 'react-icons/cg' import { MdLibraryBooks, MdLineAxis, MdSupport } from 'react-icons/md' import { twMerge } from 'tailwind-merge' @@ -13,6 +12,7 @@ import { FaInstagram, FaSignInAlt, FaTshirt, + FaUser, FaUsers, } from 'react-icons/fa' import { getSponsorsForSponsorPack } from '~/server/sponsors' @@ -22,6 +22,10 @@ import { ThemeToggle } from '~/components/ThemeToggle' import { TbBrandBluesky, TbBrandTwitter } from 'react-icons/tb' import { BiSolidCheckShield } from 'react-icons/bi' import { SearchButton } from '~/components/SearchButton' +import { Authenticated, Unauthenticated, useQuery } from 'convex/react' +import { api } from 'convex/_generated/api' +import { GiFlatHammer } from 'react-icons/gi' +import { PiHammer, PiHammerFill } from 'react-icons/pi' export const Route = createFileRoute({ staleTime: Infinity, @@ -34,6 +38,8 @@ export const Route = createFileRoute({ }) function LibrariesLayout() { + const user = useQuery(api.auth.getCurrentUser); + const activeLibrary = useLocation({ select: (location) => { return libraries.find((library) => { @@ -254,7 +260,7 @@ function LibrariesLayout() {
{/* Auth Section */} - + Login
- + - -
- + +
+
- + { + user?.capabilities.includes('builder') && ( + +
+
+ +
+
Builder
+
+ + ) + } +
) diff --git a/src/routes/api/auth/$.ts b/src/routes/api/auth/$.ts new file mode 100644 index 00000000..e684e0e9 --- /dev/null +++ b/src/routes/api/auth/$.ts @@ -0,0 +1,10 @@ +import { reactStartHandler } from 'src/libraries/server-auth-utils' + +export const ServerRoute = createServerFileRoute().methods({ + GET: ({ request }) => { + return reactStartHandler(request) + }, + POST: ({ request }) => { + return reactStartHandler(request) + }, +}) \ No newline at end of file diff --git a/src/routes/builder.tsx b/src/routes/builder.tsx new file mode 100644 index 00000000..21a0982b --- /dev/null +++ b/src/routes/builder.tsx @@ -0,0 +1,31 @@ +import { convexQuery } from '@convex-dev/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' +import { useNavigate } from '@tanstack/react-router' +import { api } from 'convex/_generated/api' + +export const Route = createFileRoute({ + component: RouteComponent, + loader: async (opts) => { + await opts.context.queryClient.ensureQueryData( + convexQuery(api.auth.getCurrentUser, {}) + ) + }, +}) + +function RouteComponent() { + const { isLoading, data: user } = useSuspenseQuery(convexQuery(api.auth.getCurrentUser, {})) + const navigate = useNavigate() + if (isLoading) { + return null + } + if (!user?.capabilities.includes('builder')) { + navigate({ to: '/login' }) + return null + } + + return ( +
+ Hello "/builder"! +
+ ) +} diff --git a/src/server/auth.ts b/src/server/auth.ts deleted file mode 100644 index 7962c2b3..00000000 --- a/src/server/auth.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { createServerFn } from '@tanstack/react-start' -import { getAuth } from '@clerk/tanstack-react-start/server' -import { getWebRequest } from '@tanstack/react-start/server' -import { redirect } from '@tanstack/react-router' - -/** - * Get the current authenticated user from Clerk - */ -export const getCurrentUser = createServerFn({ method: 'GET' }).handler( - async () => { - const request = getWebRequest() - if (!request) { - return { user: null, isAuthenticated: false } - } - - const { userId } = await getAuth(request) - - return { - userId, - isAuthenticated: !!userId, - } - } -) - -/** - * Server function to check if the current user is authenticated - * In Clerk's waitlist mode, if a user can authenticate, they have been approved from the waitlist - */ -export const checkUserAccess = createServerFn({ method: 'GET' }).handler( - async () => { - const request = getWebRequest() - if (!request) { - return { allowed: false, reason: 'No request context' } - } - - const { userId } = await getAuth(request) - - if (!userId) { - return { - allowed: false, - reason: - 'User not authenticated - please join waitlist or sign in if approved', - isAuthenticated: false, - } - } - - // In waitlist mode, if user is authenticated via Clerk, they have been approved from the waitlist - return { - allowed: true, - reason: 'User is authenticated and approved from waitlist', - isAuthenticated: true, - userId, - } - } -) - -/** - * Require authentication - redirects to waitlist for new users, login for approved users - * Use this in route loaders or beforeLoad hooks for protected routes - */ -export const requireAuth = createServerFn({ method: 'GET' }).handler( - async () => { - const request = getWebRequest() - if (!request) { - throw redirect({ to: '/login' }) - } - - const { userId } = await getAuth(request) - - if (!userId) { - // In login mode, unauthenticated users should go to login - throw redirect({ to: '/login' }) - } - - // In waitlist mode, authenticated users have been approved and have access - return { - userId, - isAuthenticated: true, - hasAccess: true, - } - } -) diff --git a/src/stores/userSettings.ts b/src/stores/userSettings.ts index 86fd2770..b2f2bc24 100644 --- a/src/stores/userSettings.ts +++ b/src/stores/userSettings.ts @@ -1,6 +1,6 @@ -import { useUser } from '@clerk/tanstack-react-start' import { create } from 'zustand' import { persist, createJSONStorage } from 'zustand/middleware' +import { authClient } from '~/libraries/auth-client' export type UserSettings = { adsDisabled: boolean @@ -42,11 +42,11 @@ export const useUserSettingsStore = create()( ) export function useAdsPreference() { - const { isSignedIn } = useUser() + const { data } = authClient.useSession() const { settings } = useUserSettingsStore((s) => ({ settings: s.settings, })) - const adsEnabled = isSignedIn ? !settings.adsDisabled : true + const adsEnabled = data?.user ? !settings.adsDisabled : true return { adsEnabled } } diff --git a/src/styles/app.css b/src/styles/app.css index e0349d1f..f10d1b11 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -735,58 +735,4 @@ mark { font-style: italic; font-weight: 900; src: url('/fonts/inter-v19-latin-900italic.woff2') format('woff2'); -} - -/* Clerk */ - -.cl-cardBox { - @apply shadow-md; -} - -.user-profile-container { - container-type: inline-size; -} - -.cl-userProfile-root { - width: 100%; -} - -.cl-userProfile-root .cl-cardBox { - width: 100%; -} - -/* sm */ -@container (max-width: 30em) { - .cl-userProfile-root .cl-profileSection { - flex-direction: column-reverse; - gap: 0.5rem; - } -} - -/* md */ -@container (max-width: 48em) { - .cl-userProfile-root .cl-navbar { - display: none; - } - .cl-userProfile-root .cl-navbarMobileMenuRow { - display: flex; - } - .cl-userProfile-root .cl-cardBox { - display: flex; - flex-direction: column; - } -} - -/* lg */ -@container (max-width: 62em) { - .cl-userProfile-root .cl-profileSection { - flex-direction: column-reverse; - gap: 0.5rem; - } - .cl-userProfile-root .cl-profileSectionHeader { - align-self: self-start; - margin-top: unset; - transform: none; - padding: 0px; - } -} +} \ No newline at end of file diff --git a/src/utils/env.ts b/src/utils/env.ts index c6336ba8..8162a325 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -6,19 +6,22 @@ const serverEnvSchema = z.object({ AIRTABLE_API_KEY: z.string().optional(), }) -// Define client schema -const viteEnvSchema = z.object({ - VITE_CLERK_PUBLISHABLE_KEY: z.string().optional(), +const clientEnvSchema = z.object({ + VITE_CONVEX_URL: z.string(), + VITE_CONVEX_SITE_URL: z.string(), }) // Validate and parse environment variables const parsedServerEnv = import.meta.env.SSR ? serverEnvSchema.parse(process.env) : {} -const parsedClientEnv = viteEnvSchema.parse(import.meta.env) + +const parsedClientEnv = import.meta.env + ? clientEnvSchema.parse(import.meta.env) + : {} type ParsedServerEnv = z.infer -type ParsedClientEnv = z.infer +type ParsedClientEnv = z.infer type ParsedEnv = ParsedServerEnv & ParsedClientEnv // Merge parsed environments, with server env hidden from client From 404ca8003a9bc37fe6329dcb598123b436b574a2 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Mon, 18 Aug 2025 17:51:50 -0600 Subject: [PATCH 02/10] remove other branch artifcat --- src/routeTree.gen.ts | 31 ------------------------------- src/routes/api/initial-payload.ts | 25 ------------------------- 2 files changed, 56 deletions(-) delete mode 100644 src/routes/api/initial-payload.ts diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 829ea972..28fbb8f4 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -763,13 +763,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibrariesTermsRouteImport parentRoute: typeof LibrariesRouteRoute } - '/api/initial-payload': { - id: '/api/initial-payload' - path: '' - fullPath: '/api/initial-payload' - preLoaderRoute: unknown - parentRoute: typeof rootRouteImport - } '/$libraryId/': { id: '/$libraryId/' path: '/' @@ -1103,13 +1096,6 @@ declare module '@tanstack/react-start/server' { preLoaderRoute: unknown parentRoute: typeof rootServerRouteImport } - '/api/initial-payload': { - id: '/api/initial-payload' - path: '/api/initial-payload' - fullPath: '/api/initial-payload' - preLoaderRoute: typeof ApiInitialPayloadServerRouteImport - parentRoute: typeof rootServerRouteImport - } '/$libraryId/': { id: '/$libraryId/' path: '/' @@ -1612,23 +1598,6 @@ declare module './routes/_libraries/terms' { unknown > } -declare module './routes/api/initial-payload' { - const createFileRoute: CreateFileRoute< - '/api/initial-payload', - FileRoutesByPath['/api/initial-payload']['parentRoute'], - FileRoutesByPath['/api/initial-payload']['id'], - FileRoutesByPath['/api/initial-payload']['path'], - FileRoutesByPath['/api/initial-payload']['fullPath'] - > - - const createServerFileRoute: CreateServerFileRoute< - ServerFileRoutesByPath['/api/initial-payload']['parentRoute'], - ServerFileRoutesByPath['/api/initial-payload']['id'], - ServerFileRoutesByPath['/api/initial-payload']['path'], - ServerFileRoutesByPath['/api/initial-payload']['fullPath'], - unknown - > -} declare module './routes/$libraryId/index' { const createFileRoute: CreateFileRoute< '/$libraryId/', diff --git a/src/routes/api/initial-payload.ts b/src/routes/api/initial-payload.ts deleted file mode 100644 index 3ce86a7c..00000000 --- a/src/routes/api/initial-payload.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { generateInitialPayload } from "@tanstack/cta-ui/lib/engine-handling/generate-initial-payload" -import { setServerEnvironment } from "@tanstack/cta-ui/lib/engine-handling/server-environment" - -setServerEnvironment({ - projectPath: process.cwd(), - options: { - targetDir: './', - projectName: 'dry-run-create-app', - mode: 'file-router', - typescript: true, - tailwind: true, - git: false, - packageManager: 'pnpm', - chosenAddOns: [], - framework: 'react-cra', - }, - mode: 'setup', -}) - -export const ServerRoute = createServerFileRoute().methods({ - GET: async ({ request }) => { - const payload = await generateInitialPayload() - return new Response(JSON.stringify(payload)) - }, -}) \ No newline at end of file From 9de429daf35405a12e85571ed76f6936ab6e3ca0 Mon Sep 17 00:00:00 2001 From: Austin Montoya Date: Tue, 19 Aug 2025 18:49:15 -0700 Subject: [PATCH 03/10] add node types for netlify deploy --- package.json | 1 + pnpm-lock.yaml | 101 +++++++++++++++++++++++++------------------------ 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index 605bcdd5..7664ecac 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@shikijs/transformers": "^1.10.3", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "@types/node": "^24.3.0", "@types/remove-markdown": "^0.3.4", "autoprefixer": "^10.4.18", "dotenv-cli": "^8.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2d38014..be168eaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,7 +64,7 @@ importers: version: 0.5.13(tailwindcss@4.1.11) '@tailwindcss/vite': specifier: ^4.1.11 - version: 4.1.11(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.1.11(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) '@tanstack/react-pacer': specifier: ^0.7.0 version: 0.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -82,7 +82,7 @@ importers: version: 1.130.17(@tanstack/react-query@5.84.2(react@19.0.0))(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-core@1.131.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/react-start': specifier: 1.131.2 - version: 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + version: 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(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.3)(yaml@2.8.1)) '@types/d3': specifier: ^7.4.3 version: 7.4.3 @@ -103,7 +103,7 @@ importers: version: 2.17.0(react@19.0.0) '@vitejs/plugin-react': specifier: ^4.3.3 - version: 4.3.4(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + version: 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.3)(yaml@2.8.1)) airtable: specifier: ^0.12.2 version: 0.12.2 @@ -193,7 +193,7 @@ importers: version: 1.3.3 vite-tsconfig-paths: specifier: ^5.0.1 - version: 5.0.1(typescript@5.6.3)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + version: 5.0.1(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.3)(yaml@2.8.1)) zod: specifier: ^4.0.17 version: 4.0.17 @@ -206,10 +206,13 @@ importers: version: 0.8.2(typescript@5.6.3) '@content-collections/vite': specifier: ^0.2.4 - version: 0.2.4(@content-collections/core@0.8.2(typescript@5.6.3))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + version: 0.2.4(@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.3)(yaml@2.8.1)) '@shikijs/transformers': specifier: ^1.10.3 version: 1.10.3 + '@types/node': + specifier: ^24.3.0 + version: 24.3.0 '@types/react': specifier: ^18.3.12 version: 18.3.12 @@ -248,7 +251,7 @@ importers: version: 5.6.3 vite: specifier: ^6.3.5 - version: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + version: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) packages: @@ -3687,8 +3690,8 @@ packages: '@types/node@14.18.63': resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} - '@types/node@24.2.1': - resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} '@types/normalize-package-data@2.4.3': resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==} @@ -9622,11 +9625,11 @@ snapshots: dependencies: '@content-collections/core': 0.8.2(typescript@5.6.3) - '@content-collections/vite@0.2.4(@content-collections/core@0.8.2(typescript@5.6.3))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@content-collections/vite@0.2.4(@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.3)(yaml@2.8.1))': dependencies: '@content-collections/core': 0.8.2(typescript@5.6.3) '@content-collections/integrations': 0.2.1(@content-collections/core@0.8.2(typescript@5.6.3)) - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) '@convex-dev/auth@0.0.88(@auth/core@0.37.0)(convex@1.25.4(@clerk/clerk-react@5.40.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': dependencies: @@ -11527,14 +11530,14 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.1.11 - '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.11 '@tailwindcss/oxide': 4.1.11 tailwindcss: 4.1.11 - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) - '@tanstack/directive-functions-plugin@1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/directive-functions-plugin@1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.0 @@ -11543,7 +11546,7 @@ snapshots: '@tanstack/router-utils': 1.131.2 babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -11606,12 +11609,12 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-plugin@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/react-start-plugin@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1)))(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: - '@tanstack/start-plugin-core': 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) - '@vitejs/plugin-react': 4.3.4(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/start-plugin-core': 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1)) + '@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.3)(yaml@2.8.1)) pathe: 2.0.3 - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -11657,17 +11660,17 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@tanstack/react-start@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/react-start@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(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.3)(yaml@2.8.1))': dependencies: '@tanstack/react-start-client': 1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/react-start-plugin': 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/react-start-plugin': 1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1)))(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) '@tanstack/react-start-server': 1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/start-server-functions-client': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) - '@tanstack/start-server-functions-server': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) - '@vitejs/plugin-react': 4.3.4(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/start-server-functions-client': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/start-server-functions-server': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@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.3)(yaml@2.8.1)) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11746,7 +11749,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.131.2(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/router-plugin@1.131.2(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) @@ -11764,7 +11767,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -11779,7 +11782,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/server-functions-plugin@1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/server-functions-plugin@1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.0 @@ -11788,7 +11791,7 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 - '@tanstack/directive-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/directive-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -11803,16 +11806,16 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/start-plugin-core@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/start-plugin-core@1.131.2(@netlify/blobs@10.0.8)(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.28.0 '@babel/types': 7.28.2 '@tanstack/router-core': 1.131.2 '@tanstack/router-generator': 1.131.2 - '@tanstack/router-plugin': 1.131.2(@tanstack/react-router@1.131.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/router-plugin': 1.131.2(@tanstack/react-router@1.131.2(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.3)(yaml@2.8.1)) '@tanstack/router-utils': 1.131.2 - '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) '@tanstack/start-server-core': 1.131.2 '@types/babel__code-frame': 7.0.6 '@types/babel__core': 7.20.5 @@ -11822,8 +11825,8 @@ snapshots: nitropack: 2.12.4(@netlify/blobs@10.0.8) pathe: 2.0.3 ufo: 1.6.1 - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) - vitefu: 1.1.1(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vitefu: 1.1.1(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) xmlbuilder2: 3.1.1 zod: 3.25.76 transitivePeerDependencies: @@ -11870,9 +11873,9 @@ snapshots: tiny-warning: 1.0.3 unctx: 2.4.1 - '@tanstack/start-server-functions-client@1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/start-server-functions-client@1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: - '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) '@tanstack/start-server-functions-fetcher': 1.131.2 transitivePeerDependencies: - supports-color @@ -11883,9 +11886,9 @@ snapshots: '@tanstack/router-core': 1.131.2 '@tanstack/start-client-core': 1.131.2 - '@tanstack/start-server-functions-server@1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@tanstack/start-server-functions-server@1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': dependencies: - '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) + '@tanstack/server-functions-plugin': 1.131.2(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)) tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color @@ -12077,10 +12080,9 @@ snapshots: '@types/node@14.18.63': {} - '@types/node@24.2.1': + '@types/node@24.3.0': dependencies: undici-types: 7.10.0 - optional: true '@types/normalize-package-data@2.4.3': {} @@ -12115,7 +12117,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.0 optional: true '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0)(typescript@5.6.3)': @@ -12344,14 +12346,14 @@ snapshots: prop-types: 15.8.1 react: 19.0.0 - '@vitejs/plugin-react@4.3.4(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1))': + '@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.3)(yaml@2.8.1))': dependencies: '@babel/core': 7.26.9 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -16804,8 +16806,7 @@ snapshots: magic-string: 0.30.17 unplugin: 2.3.5 - undici-types@7.10.0: - optional: true + undici-types@7.10.0: {} undici@6.21.0: {} @@ -17031,18 +17032,18 @@ snapshots: unist-util-stringify-position: 2.0.3 vfile-message: 2.0.4 - vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)): + vite-tsconfig-paths@5.0.1(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.3)(yaml@2.8.1)): dependencies: debug: 4.4.0 globrex: 0.1.2 tsconfck: 3.1.4(typescript@5.6.3) optionalDependencies: - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1): + vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) @@ -17051,7 +17052,7 @@ snapshots: rollup: 4.40.2 tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.0 fsevents: 2.3.3 jiti: 2.5.1 lightningcss: 1.30.1 @@ -17059,9 +17060,9 @@ snapshots: tsx: 4.20.3 yaml: 2.8.1 - vitefu@1.1.1(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)): + vitefu@1.1.1(vite@6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1)): optionalDependencies: - vite: 6.3.5(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.1) vue@3.5.18(typescript@5.6.3): dependencies: From e70b2aeb875dbf9537987f95b301aba8c60113ee Mon Sep 17 00:00:00 2001 From: Austin Montoya Date: Tue, 19 Aug 2025 19:30:54 -0700 Subject: [PATCH 04/10] fix site url when deploying to netlify --- src/libraries/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index 3e567ade..905c82ca 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -5,7 +5,7 @@ import { betterAuthComponent } from "../../convex/auth"; import { type GenericCtx } from "../../convex/_generated/server"; // You'll want to replace this with an environment variable -const siteUrl = "http://localhost:3000"; +const siteUrl = process.env.DEPLOY_PRIME_URL || "http://localhost:3000"; export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here From 0a00fd2fb851a273e2292dc099b2a64dc9bdc0cf Mon Sep 17 00:00:00 2001 From: Austin Montoya Date: Tue, 19 Aug 2025 19:34:44 -0700 Subject: [PATCH 05/10] try diff env var --- src/libraries/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index 905c82ca..09c790db 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -5,7 +5,7 @@ import { betterAuthComponent } from "../../convex/auth"; import { type GenericCtx } from "../../convex/_generated/server"; // You'll want to replace this with an environment variable -const siteUrl = process.env.DEPLOY_PRIME_URL || "http://localhost:3000"; +const siteUrl = process.env.URL || "http://localhost:3000"; export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here From 6ca99b8d500bcf9edd6b84c4bef219270052455d Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Tue, 19 Aug 2025 21:01:14 -0600 Subject: [PATCH 06/10] merge --- src/libraries/auth.ts | 15 ++++++++------- src/libraries/server-auth-utils.ts | 4 ++-- src/router.tsx | 7 +------ src/utils/env.ts | 6 ++++++ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index 09c790db..6583a7be 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -1,11 +1,12 @@ -import { convexAdapter } from "@convex-dev/better-auth"; -import { convex } from "@convex-dev/better-auth/plugins"; -import { betterAuth } from "better-auth"; -import { betterAuthComponent } from "../../convex/auth"; -import { type GenericCtx } from "../../convex/_generated/server"; +import { convexAdapter } from '@convex-dev/better-auth' +import { convex } from '@convex-dev/better-auth/plugins' +import { betterAuth } from 'better-auth' +import { betterAuthComponent } from '../../convex/auth' +import { type GenericCtx } from '../../convex/_generated/server' +import { env } from '~/utils/env' // You'll want to replace this with an environment variable -const siteUrl = process.env.URL || "http://localhost:3000"; +const siteUrl = env.URL || 'http://localhost:3000' export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here @@ -29,4 +30,4 @@ export const createAuth = (ctx: GenericCtx) => // The Convex plugin is required convex(), ], - }); \ No newline at end of file + }) diff --git a/src/libraries/server-auth-utils.ts b/src/libraries/server-auth-utils.ts index ac424782..1225b938 100644 --- a/src/libraries/server-auth-utils.ts +++ b/src/libraries/server-auth-utils.ts @@ -2,7 +2,7 @@ import { createAuth } from './auth' import { reactStartHelpers } from '@convex-dev/better-auth/react-start' import { env } from '../utils/env' -export const { fetchSession, reactStartHandler, getCookieName} = +export const { fetchSession, reactStartHandler, getCookieName } = reactStartHelpers(createAuth, { convexSiteUrl: env.VITE_CONVEX_SITE_URL, - }) \ No newline at end of file + }) diff --git a/src/router.tsx b/src/router.tsx index 1b2c5941..defd3263 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -10,12 +10,7 @@ import { GamOnPageChange } from './components/Gam' import { env } from './utils/env' export function createRouter() { - const CONVEX_URL = - env.VITE_CONVEX_URL || - // Hardcoded production URL as fallback for local development - // Currently set to an instance owned by Convex Devx - // TODO: Replace with URL to an instance owned by the TanStack team - 'https://befitting-badger-629.convex.cloud' + const CONVEX_URL = env.VITE_CONVEX_URL const convexQueryClient = new ConvexQueryClient(CONVEX_URL) const queryClient: QueryClient = new QueryClient({ diff --git a/src/utils/env.ts b/src/utils/env.ts index 8162a325..b25e7625 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -4,11 +4,17 @@ import { z } from 'zod' const serverEnvSchema = z.object({ GITHUB_AUTH_TOKEN: z.string().default('USE_A_REAL_KEY_IN_PRODUCTION'), AIRTABLE_API_KEY: z.string().optional(), + VITE_CLERK_PUBLISHABLE_KEY: z.string().optional(), + GITHUB_OAUTH_CLIENT_ID: z.string().optional(), + GITHUB_OAUTH_CLIENT_SECRET: z.string().optional(), + GOOGLE_OAUTH_CLIENT_ID: z.string().optional(), + GOOGLE_OAUTH_CLIENT_SECRET: z.string().optional(), }) const clientEnvSchema = z.object({ VITE_CONVEX_URL: z.string(), VITE_CONVEX_SITE_URL: z.string(), + URL: z.string().default('http://localhost:3000'), }) // Validate and parse environment variables From 1c37e83fc60b86386642ae091408a2e8041b21ef Mon Sep 17 00:00:00 2001 From: Austin Montoya Date: Tue, 19 Aug 2025 20:02:23 -0700 Subject: [PATCH 07/10] add unique env var name --- src/libraries/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index 6583a7be..d057697f 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -6,7 +6,7 @@ import { type GenericCtx } from '../../convex/_generated/server' import { env } from '~/utils/env' // You'll want to replace this with an environment variable -const siteUrl = env.URL || 'http://localhost:3000' +const siteUrl = process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000"; export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here From b1754135c8c2d6d4acbe7095d46a71f11ceceb86 Mon Sep 17 00:00:00 2001 From: Austin Montoya Date: Tue, 19 Aug 2025 20:03:51 -0700 Subject: [PATCH 08/10] fix image paths --- src/routes/_libraries/login.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/routes/_libraries/login.tsx b/src/routes/_libraries/login.tsx index 5a68618c..dd9cd956 100644 --- a/src/routes/_libraries/login.tsx +++ b/src/routes/_libraries/login.tsx @@ -1,6 +1,8 @@ import { authClient } from '~/libraries/auth-client' import { useIsDark } from '~/hooks/useIsDark' import { FaGithub, FaGoogle } from 'react-icons/fa' +import splashLightImg from '~/images/splash-light.png' +import splashDarkImg from '~/images/splash-dark.png' export const Route = createFileRoute({ component: LoginPage, @@ -13,8 +15,8 @@ function SplashImage() { Waitlist Date: Tue, 19 Aug 2025 20:26:44 -0700 Subject: [PATCH 09/10] add multiple fallbacks --- src/libraries/auth.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index d057697f..f961fa57 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -3,10 +3,9 @@ import { convex } from '@convex-dev/better-auth/plugins' import { betterAuth } from 'better-auth' import { betterAuthComponent } from '../../convex/auth' import { type GenericCtx } from '../../convex/_generated/server' -import { env } from '~/utils/env' // You'll want to replace this with an environment variable -const siteUrl = process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000"; +const siteUrl = process.env.BETTER_AUTH_BASE_URL || process.env.URL || "http://localhost:3000"; export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here From e8b3a81bff2d89b68d8811b5bda7dcbdf12a77a8 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Wed, 20 Aug 2025 10:05:07 -0600 Subject: [PATCH 10/10] merge --- src/libraries/auth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/auth.ts b/src/libraries/auth.ts index f961fa57..73bec22b 100644 --- a/src/libraries/auth.ts +++ b/src/libraries/auth.ts @@ -5,7 +5,8 @@ import { betterAuthComponent } from '../../convex/auth' import { type GenericCtx } from '../../convex/_generated/server' // You'll want to replace this with an environment variable -const siteUrl = process.env.BETTER_AUTH_BASE_URL || process.env.URL || "http://localhost:3000"; +const siteUrl = + process.env.BETTER_AUTH_BASE_URL || process.env.URL || 'http://localhost:3000' export const createAuth = (ctx: GenericCtx) => // Configure your Better Auth instance here