Skip to content

feat(mobile): build out barebones mobile app#843

Merged
saddlepaddle merged 11 commits into
mainfrom
mobile
Jan 20, 2026
Merged

feat(mobile): build out barebones mobile app#843
saddlepaddle merged 11 commits into
mainfrom
mobile

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Jan 20, 2026

Summary

Reanimated doesn't work yet, has some hacky stuff, but is at least a good checkpoint to build from.

Changes

Better-Auth Expo Integration ✅

  • Fixed expo() server plugin import - changed from better-auth/plugins to @better-auth/expo (this was the critical bug causing 403 errors)
  • Added @better-auth/expo dependency to packages/auth
  • Added development Expo URL patterns to trustedOrigins:
    • exp:// - Trust all Expo URLs (prefix matching)
    • exp://** - Trust all Expo URLs (wildcard matching)
    • exp://192.168.*.*:*/** - Trust local IP ranges with any port
  • Removed unnecessary custom Origin header from client (expoClient plugin handles this automatically)
  • Removed nested biome.json from mobile app (Biome runs at root level only per monorepo guidelines)

Mobile App Structure Refactoring ✅

  • Created screens/ directory that mirrors app/ structure 1:1
  • Moved screen components to screens/ with proper colocation support:
    • screens/RootLayout/RootLayout.tsx (from app/_layout.tsx)
    • screens/index/HomeScreen.tsx (from app/index.tsx)
    • screens/(auth)/sign-in/SignInScreen.tsx (from app/(auth)/sign-in.tsx)
  • Updated app/ files to just export from screens/ directory
  • Added barrel exports (index.ts) for each screen directory
  • Now components in screens/ can be properly colocated with their dependencies following monorepo guidelines

Test Plan

  • OAuth flow works without 403 MISSING_OR_NULL_ORIGIN errors
  • GitHub and Google sign-in buttons generate correct OAuth redirect URLs
  • App structure follows monorepo colocation guidelines
  • Lint passes (auto-fixed import ordering and formatting)
  • Typecheck passes for mobile app
  • Metro bundler builds successfully with cleared cache
  • Expo dev server runs without errors

Configuration Verified

  • Server has expo() plugin from correct import path
  • Client uses expoClient with correct scheme ("superset")
  • All required dependencies installed (better-auth, @better-auth/expo, expo-network, expo-secure-store, etc.)
  • Metro config has unstable_enablePackageExports = true
  • app.json has matching scheme configuration
  • trustedOrigins includes both production (superset://) and development (exp://) patterns

Screenshots

OAuth redirect URLs are being generated successfully (see Expo dev server logs).

Summary by CodeRabbit

  • New Features

    • Added mobile app support with Expo-based React Native framework
    • Introduced mobile authentication flow with sign-in and session management
    • Added mobile UI component library for cross-platform consistency
  • Bug Fixes

    • Desktop app now correctly displays all members without organization-based filtering
  • Chores

    • Updated dependencies: React, TailwindCSS, better-auth, and React Query across multiple applications
    • Added mobile app configuration and tooling setup

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new Expo-based mobile application alongside dependency version updates across existing applications. It adds Expo authentication support to the auth server, creates mobile UI components and routing infrastructure, updates package dependencies, and modifies member query filtering.

Changes

Cohort / File(s) Summary
Dependency Updates Across Apps
apps/admin/package.json, apps/api/package.json, apps/cli/package.json, apps/desktop/package.json, apps/marketing/package.json, apps/web/package.json, apps/docs/package.json, packages/email/package.json, packages/ui/package.json
React/react-dom pinned to 19.1.0 (from ^19.2.3), @types/react to ~19.1.0 (from ^19.2.7), better-auth to 1.4.16 (from ^1.4.9), @tanstack/react-query to ^5.90.19, and tailwindcss to ^4.1.18.
Mobile App Configuration
apps/mobile/app.json, apps/mobile/babel.config.js, apps/mobile/components.json, apps/mobile/eas.json, apps/mobile/global.css, apps/mobile/metro.config.js, apps/mobile/package.json, apps/mobile/tsconfig.json, apps/mobile/.gitignore, apps/mobile/uniwind-env.d.ts, apps/mobile/uniwind-types.d.ts
New Expo project configuration, Metro bundler setup, Tailwind/Uniwind theming, build configuration (EAS), and TypeScript setup for mobile app.
Mobile UI Components
apps/mobile/components/ui/button.tsx, apps/mobile/components/ui/card.tsx, apps/mobile/components/ui/input.tsx, apps/mobile/components/ui/switch.tsx, apps/mobile/components/ui/text.tsx
New React Native UI component library with variant-driven styling, platform-specific adaptations, and accessibility support.
Mobile Routing & Screens
apps/mobile/app/_layout.tsx, apps/mobile/app/index.tsx, apps/mobile/app/(auth)/sign-in.tsx, apps/mobile/screens/(auth)/sign-in/SignInScreen.tsx, apps/mobile/screens/(auth)/sign-in/index.ts, apps/mobile/screens/index/HomeScreen.tsx, apps/mobile/screens/index/index.ts
New Expo Router layout with QueryClient provider, root index with session-based redirect, authentication routes with GitHub/Google OAuth, and home screen with sign-out functionality.
Mobile Utilities & Environment
apps/mobile/lib/auth/client.ts, apps/mobile/lib/base-url.ts, apps/mobile/lib/env.ts, apps/mobile/lib/theme.ts, apps/mobile/lib/utils.ts
Auth client configuration with Expo plugin, environment variable validation (Zod), base URL resolution with dev server IP detection, and centralized theme tokens for light/dark modes.
Auth Server Expo Integration
packages/auth/package.json, packages/auth/src/server.ts
Added @better-auth/expo dependency and registered Expo plugin with trusted origins including superset:// scheme and development-time Expo URLs.
Members Query Update
apps/desktop/src/renderer/routes/_authenticated/settings/members/components/MembersSettings/MembersSettings.tsx
Removed activeOrganizationId filter from members query, now selects all members without organization filtering.
Planning Documentation
plans/mobile-app-structure-comparison.md
Added architectural comparison document detailing Expo Router integration patterns, directory structure, routing logic, and migration guidance for mobile app.

Sequence Diagram

sequenceDiagram
    participant MobileApp as Mobile App
    participant AuthClient as Auth Client
    participant AuthServer as Auth Server
    participant Storage as Secure Storage

    MobileApp->>AuthClient: useSession()
    AuthClient->>Storage: Check stored session
    Storage-->>AuthClient: Session data (or null)
    alt No Session
        AuthClient-->>MobileApp: null
        MobileApp->>MobileApp: Redirect to /(auth)/sign-in
        MobileApp->>MobileApp: Render SignInScreen
        Note over MobileApp: User taps GitHub/Google
        MobileApp->>AuthClient: signIn.social({provider})
        AuthClient->>AuthServer: OAuth request + scheme:superset
        AuthServer->>AuthServer: Validate & create session
        AuthServer-->>AuthClient: Return session token
        AuthClient->>Storage: Store session securely
        AuthClient-->>MobileApp: useSession() returns session
        MobileApp->>MobileApp: Redirect to home screen
    else Session Exists
        AuthClient-->>MobileApp: session object
        MobileApp->>MobileApp: Render HomeScreen
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes span multiple application modules with extensive new mobile app scaffold code (configuration, components, screens, utilities) and repetitive dependency updates across 9 package.json files. While dependency updates are homogeneous and low-risk, the new mobile app introduces a diverse set of new files following established patterns (Expo Router, React Native UI components, Zod validation). The auth server modification is straightforward (plugin registration), and the members query change is minimal. The primary review effort stems from the breadth of new mobile app code and ensuring consistency across components and configuration.

Possibly related PRs

Poem

🐰 A mobile app hops into view,
With Expo routes and screens so new,
Auth flows dance 'tween app and server,
While React versions shift—careful observer!
Dark themes and buttons, all in place,
The warren grows at a faster pace. 🏡

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main objective: building out a barebones mobile app with proper Expo structure and Better-Auth integration.
Description check ✅ Passed The PR description is comprehensive and well-organized, covering Better-Auth fixes, mobile app structure refactoring, test verification, and configuration details that align with the repository template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🤖 Fix all issues with AI agents
In `@apps/api/package.json`:
- Around line 32-33: The dependency version for `@types/react-dom` is mismatched
with react-dom (react-dom is set to "19.1.0" while `@types/react-dom` is pinned to
^19.2.3); update the `@types/react-dom` entry in package.json to a type version
aligned with React 19.1 (for example "~19.1.0") so it matches react-dom and
`@types/react`, ensuring consistent types across the project.

In `@apps/desktop/package.json`:
- Line 55: Update the `@types/react` dependency in package.json to satisfy
`@types/react-dom`'s peer requirement: change the version constraint for
"@types/react" from "~19.1.0" to a 19.2.x-compatible range (e.g., "~19.2.0" or
"^19.2.0") so it aligns with "@types/react-dom@19.2.3" and removes the peer
conflict while leaving other dependencies unchanged.

In `@apps/docs/package.json`:
- Around line 23-24: The dependency versions for TypeScript typings are
mismatched: update the `@types` packages in package.json so `@types/react` and
`@types/react-dom` use the same minor series; specifically change the `@types/react`
entry to ^19.2.x (to match `@types/react-dom`@^19.2.3) or alternatively set
`@types/react-dom` to ~19.1.x—edit the "@types/react" and "@types/react-dom"
entries accordingly to keep them aligned.

In `@apps/mobile/app.json`:
- Line 43: In app.json update the "owner" field value: locate the JSON key
"owner" (currently set to "supserset-sh") and correct the typo by replacing it
with "superset-sh" so the owner string matches the intended organization name.

In `@apps/mobile/components.json`:
- Around line 6-11: Update the tailwind entry in components.json to remove the
non-existent tailwind.config.js reference: either delete the "config":
"tailwind.config.js" key from the "tailwind" object in components.json or
replace it with a valid config path if you intend to use one; ensure the "css":
"global.css" and "cssVariables": true settings remain intact so the project
continues to use the CSS-first Tailwind v4 setup (refer to the "tailwind" object
and the "config" key when making the change).

In `@apps/mobile/components/ui/button.tsx`:
- Around line 102-117: The ButtonProps type is unnecessarily combining
React.ComponentProps and React.RefAttributes; replace the intersection with
React.ComponentPropsWithRef<typeof Pressable> to simplify and include refs
automatically. Update the type alias ButtonProps to use
ComponentPropsWithRef<typeof Pressable>, keeping the rest of the Button
component (function Button, the Pressable spread {...props}, and usage of
buttonVariants/buttonTextVariants and cn) unchanged so props and refs continue
to be forwarded correctly.

In `@apps/mobile/components/ui/card.tsx`:
- Around line 5-69: Split each component (Card, CardHeader, CardTitle,
CardDescription, CardContent, CardFooter) into its own file and update their
exports; remove the incorrect use of React.RefAttributes from props or,
preferably, implement proper ref forwarding by wrapping each functional
component with React.forwardRef and accepting (props, ref) to pass the ref to
the underlying View/Text element (e.g., forwardRef for Card should forward to
the inner View, CardTitle/CardDescription forward to Text, etc.), and update
their prop types to use React.ComponentProps<typeof View>/Text plus
React.Ref<HTMLDivElement | Text> as appropriate; ensure you export default each
component from its new file and update any imports where these components are
used.

In `@apps/mobile/components/ui/text.tsx`:
- Around line 55-62: The ROLE mapping uses forced type assertions for
Platform.select({ web: "blockquote" as Role }) and "code" which silence
TypeScript because React Native's Role type lacks these web-only ARIA roles;
either add a brief inline comment above the ROLE constant explaining the
web-only assertion and why it's safe (Platform.select guarantees runtime-only on
web), or create a proper type augmentation to extend react-native's Role with
'blockquote' and 'code' (e.g., add an ambient declaration module for
'react-native' in your types directory) and remove the as Role casts so ROLE
(and related types TextVariant/Role) are properly typed without assertions.

In `@apps/mobile/global.css`:
- Around line 10-11: Remove the inconsistent CSS custom property --color-radius
from the `@variant` light block and ensure the theme uses the established --radius
variable; locate the `@theme` block that defines --radius and the `@variant` light
block containing --color-radius, delete the --color-radius declaration, and
verify no other rules reference --color-radius so the layout relies on the
existing --radius: 0.5rem variable.

In `@apps/mobile/lib/auth/client.ts`:
- Around line 5-6: The code silently falls back to "http://localhost:3001" when
EXPO_PUBLIC_API_URL is missing; change the logic around the API_URL/BASE_URL
constant so that if process.env.EXPO_PUBLIC_API_URL is undefined and the app is
not in development (e.g., process.env.NODE_ENV !== 'development' or __DEV__),
you either throw an error or log a clear warning and fail fast instead of using
localhost; update the constant name (API_URL → BASE_URL) and ensure the new
BASE_URL is passed into createAuthClient where API_URL was previously used so
the client initialization uses the validated environment value.

In `@apps/mobile/lib/database.ts`:
- Around line 12-84: This module uses raw SQL in initDatabase, getTodos,
addTodo, toggleTodo, and deleteTodo; replace these with Drizzle ORM calls using
the centralized schema from packages/db/src (import the todos table and db
client types), initialize the Drizzle client in initDatabase (or reuse existing
exported db instance) instead of SQLite.execAsync, rewrite getTodos to run
db.select(todos).orderBy(todos.created_at.desc()), addTodo to use
db.insert(todos).values({ title }).returning(), toggleTodo to use
db.update(todos).set({ completed: sql`NOT ${todos.completed}` }) or fetch
current value then update, and deleteTodo to use
db.delete(todos).where(eq(todos.id, id)); ensure type mappings (completed
boolean, createdAt field) match your Todo type and adjust returned row property
names to createdAt, and remove raw SQL strings while importing helpers (eq, sql)
from drizzle-orm/sqlite as needed.

In `@apps/mobile/package.json`:
- Line 16: The dependency entry for "@electric-sql/client" has an empty version
string which is fragile; update the apps/mobile/package.json by either removing
the "@electric-sql/client" dependency if you rely on the root resolution
override, or replace the empty string with a concrete reference (e.g., a
workspace reference like "workspace:*", the PR tarball URL used in the root
resolution, or the published version) so package resolution is explicit and
predictable.

In `@apps/mobile/screens/index/HomeScreen.tsx`:
- Around line 3-30: Update the handleSignOut implementation in HomeScreen to
catch errors from signOut() instead of letting unhandled rejections occur: wrap
the await signOut() call in a try/catch, call console.error with a clear prefix
(e.g., "HomeScreen: signOut failed") and the caught error, and show user
feedback via an Alert (e.g., Alert.alert with a short message) so failures are
logged and surfaced to the user.

In `@apps/web/package.json`:
- Around line 36-37: Update package.json to mitigate CVE-2025-55182 by bumping
"react" from 19.1.0 to 19.1.2 and ensure "react-dom" is also set to 19.1.2
(replace the existing 19.1.0 entries), and align the `@types` entry for react-dom
to a matching ~19.1.x range instead of ^19.2.3; run package manager install and
lockfile update and run tests/build to verify nothing else breaks.

In `@packages/auth/package.json`:
- Line 21: Update the dependency version for "@better-auth/expo" in package.json
to a published release; locate the dependency entry for "@better-auth/expo" and
replace "1.4.15" with a valid npm version (e.g., "1.2.11" or a valid pre-release
like "1.3.8-beta.4") or align it with the version used elsewhere (apps/mobile)
after verifying that version exists on npm, then run npm/yarn install to confirm
the package resolves.
🧹 Nitpick comments (6)
packages/auth/src/server.ts (1)

20-37: Extract trusted origin literals into named constants.

This block is getting policy-heavy; hoisting the new mobile/Expo origins into module-level constants makes the intent clearer and avoids magic strings inline.

♻️ Suggested refactor
+const MOBILE_APP_ORIGINS = ["superset://"];
+const EXPO_DEV_ORIGINS = ["exp://", "exp://**", "exp://192.168.*.*:*/**"];
+
 export const auth = betterAuth({
   trustedOrigins: [
     env.NEXT_PUBLIC_WEB_URL,
     env.NEXT_PUBLIC_API_URL,
     env.NEXT_PUBLIC_MARKETING_URL,
     env.NEXT_PUBLIC_ADMIN_URL,
     // Electron desktop app origins
     ...(env.NEXT_PUBLIC_DESKTOP_URL ? [env.NEXT_PUBLIC_DESKTOP_URL] : []),
     "superset://app", // Production Electron app
     // React Native mobile app origins
-    "superset://", // Production mobile app
+    ...MOBILE_APP_ORIGINS, // Production mobile app
     // Expo development mode - exp:// scheme with local IP ranges
-    ...(process.env.NODE_ENV === "development"
-      ? [
-          "exp://",
-          "exp://**",
-          "exp://192.168.*.*:*/**",
-        ]
-      : []),
+    ...(process.env.NODE_ENV === "development" ? EXPO_DEV_ORIGINS : []),
   ],
As per coding guidelines, extract hardcoded values to named constants.
packages/ui/package.json (1)

89-91: Exact version in peerDependencies is overly restrictive.

Using an exact version "react": "19.1.0" in peerDependencies means consumers must use exactly this version. This could cause peer dependency warnings or failures for apps using React 19.0.x or 19.2.x.

Consider using a range that accommodates minor version differences:

🔧 Proposed fix
 	"peerDependencies": {
-		"react": "19.1.0"
+		"react": "^19.0.0"
 	}
apps/mobile/components/ui/input.tsx (1)

4-32: Missing ref forwarding for TextInput.

The component accepts React.RefAttributes<TextInput> in its props type but doesn't actually forward the ref to the underlying TextInput. This prevents parent components from accessing the input for programmatic focus, blur, or selection operations.

♻️ Proposed fix to enable ref forwarding
+import * as React from "react";
 import { Platform, TextInput, type TextInputProps } from "react-native";
 import { cn } from "@/lib/utils";

-function Input({
+const Input = React.forwardRef<TextInput, TextInputProps>(function Input({
 	className,
 	...props
-}: TextInputProps & React.RefAttributes<TextInput>) {
+}, ref) {
 	return (
 		<TextInput
+			ref={ref}
 			className={cn(
 				"dark:bg-input/30 border-input bg-background text-foreground flex h-10 w-full min-w-0 flex-row items-center rounded-md border px-3 py-1 text-base leading-5 shadow-sm shadow-black/5 sm:h-9",
 				props.editable === false &&
 					cn(
 						"opacity-50",
 						Platform.select({
 							web: "disabled:pointer-events-none disabled:cursor-not-allowed",
 						}),
 					),
 				Platform.select({
 					web: cn(
 						"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground outline-none transition-[color,box-shadow] md:text-sm",
 						"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
 						"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
 					),
 					native: "placeholder:text-muted-foreground/50",
 				}),
 				className,
 			)}
 			{...props}
 		/>
 	);
-}
+});

 export { Input };
apps/mobile/components/ui/switch.tsx (1)

5-35: Missing ref forwarding for Switch primitive.

Similar to the Input component, this accepts React.RefAttributes<SwitchPrimitives.RootRef> but doesn't forward the ref to the underlying SwitchPrimitives.Root. Consider using React.forwardRef if programmatic access to the switch is needed.

♻️ Proposed fix
 import * as SwitchPrimitives from "@rn-primitives/switch";
+import * as React from "react";
 import { Platform } from "react-native";
 import { cn } from "@/lib/utils";

-function Switch({
+const Switch = React.forwardRef<SwitchPrimitives.RootRef, SwitchPrimitives.RootProps>(function Switch({
 	className,
 	...props
-}: SwitchPrimitives.RootProps & React.RefAttributes<SwitchPrimitives.RootRef>) {
+}, ref) {
 	return (
 		<SwitchPrimitives.Root
+			ref={ref}
 			className={cn(
 				// ... existing classes
 			)}
 			{...props}
 		>
 			{/* ... */}
 		</SwitchPrimitives.Root>
 	);
-}
+});
apps/mobile/screens/(auth)/sign-in/SignInScreen.tsx (1)

16-71: Extract provider strings and callback URL into module constants.

This aligns with the project’s guidance on avoiding hardcoded values and improves reuse in the UI and handler.

♻️ Proposed refactor
+const CALLBACK_URL = "/";
+const GITHUB_PROVIDER = "github" as const;
+const GOOGLE_PROVIDER = "google" as const;
+type Provider = typeof GITHUB_PROVIDER | typeof GOOGLE_PROVIDER;
+
 export default function SignInScreen() {
-	const [loading, setLoading] = useState<"github" | "google" | null>(null);
+	const [loading, setLoading] = useState<Provider | null>(null);
 	const { data: session } = useSession();
 
 	// Redirect if already signed in
 	if (session) {
 		return <Redirect href="/" />;
 	}
 
-	const handleSignIn = async (provider: "github" | "google") => {
+	const handleSignIn = async (provider: Provider) => {
 		console.log("[sign-in] Button clicked:", provider);
 		try {
 			setLoading(provider);
 			console.log("[sign-in] Calling signIn.social...");
 			await signIn.social({
 				provider,
-				callbackURL: "/",
+				callbackURL: CALLBACK_URL,
 			});
 		} catch (error) {
 			// ...
 		} finally {
 			setLoading(null);
 		}
 	};
@@
-						onPress={() => handleSignIn("github")}
+						onPress={() => handleSignIn(GITHUB_PROVIDER)}
@@
-						onPress={() => handleSignIn("google")}
+						onPress={() => handleSignIn(GOOGLE_PROVIDER)}

As per coding guidelines, extract hardcoded values to module-level constants.

apps/mobile/components/ui/card.tsx (1)

5-80: Split into one component per file.

This file defines six components. Please move each component into its own file and re-export via an index to match the one-component-per-file standard. As per coding guidelines, one component per file.

Comment thread apps/api/package.json
Comment thread apps/desktop/package.json
Comment thread apps/docs/package.json
Comment on lines +23 to +24
"react": "19.1.0",
"react-dom": "19.1.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/docs/package.json

Repository: superset-sh/superset

Length of output: 1316


🌐 Web query:

React 19.1.0 Next.js 16 compatibility

💡 Result:

Short answer: Yes — Next.js 16 is designed to work with React 19.x, so React 19.1.0 is generally compatible with Next.js 16. Install matching react and react-dom versions (exactly the same), and update any third‑party packages whose peerDependencies still restrict to React 18. (nextjs.org)

Notes / cautions:

  • Next’s upgrade guide recommends using react@latest / react-dom@latest when moving to Next 16. (nextjs.org)
  • Some ecosystem packages (or @next/third‑parties helpers) may still list React 18 ranges and require bumping their peerDependencies; you may see peer‑dependency warnings and will need to update those packages or use --legacy-peer-deps temporarily. (github.com)
  • Security: several advisories in late 2025 affected React Server Components in certain 19.x releases — patch to a non‑vulnerable React 19 release if you rely on RSC. Verify you’re on a patched 19.x version. (techradar.com)

If you want, I can: (1) check your package.json for specific conflicts, or (2) recommend the exact react/react-dom version to install (including patched versions).

Citations:


🌐 Web query:

tailwindcss 4.1.18 @tailwindcss/postcss 4.0.9 compatibility

💡 Result:

Yes — Tailwind CSS v4.1.18 is compatible with the @tailwindcss/postcss 4.x plugin (including 4.0.9). Tailwind v4 requires using the separate PostCSS package (@tailwindcss/postcss), and v4.1.18 is a v4 release. [1][2][3]

Sources:

  • Tailwind v4 PostCSS install docs. [1]
  • @tailwindcss/postcss v4.0.9 npm page. [2]
  • tailwindcss v4.1.18 GitHub release notes. [3]

🌐 Web query:

@types/react-dom 19.2.3 @types/react 19.1.0 compatibility alignment

💡 Result:

Short answer: they can conflict — @types/react-dom 19.2.3 expects the React types from the 19.2.x line, so using @types/react 19.1.0 can produce TypeScript/JSX type errors. Align the @types/* minor versions (use @types/react 19.2.x to match @types/react-dom 19.2.3, or downgrade @types/react-dom to 19.1.x). [1][2][3]

Commands:

  • to upgrade react types: npm install -D @types/react@^19.2 @types/react-dom@^19.2
  • or to downgrade react-dom types: npm install -D @types/react-dom@^19.1

Sources:

  • @types/react-dom package info. [1]
  • @types/react releases overview. [2]
  • DefinitelyTyped discussion showing JSX/type issues when versions are out of sync. [3]

Align @types/react and @types/react-dom to the same minor version.

Line 33 pins @types/react to ~19.1.0 while line 34 uses @types/react-dom at ^19.2.3. This version mismatch will cause TypeScript/JSX type conflicts. Update @types/react to ^19.2.x to match @types/react-dom@^19.2.3, or downgrade @types/react-dom to ~19.1.x.

React 19.1.0 is compatible with Next.js 16, and Tailwind CSS 4.1.18 is compatible with @tailwindcss/postcss 4.0.9, so no changes needed there.

🤖 Prompt for AI Agents
In `@apps/docs/package.json` around lines 23 - 24, The dependency versions for
TypeScript typings are mismatched: update the `@types` packages in package.json so
`@types/react` and `@types/react-dom` use the same minor series; specifically change
the `@types/react` entry to ^19.2.x (to match `@types/react-dom`@^19.2.3) or
alternatively set `@types/react-dom` to ~19.1.x—edit the "@types/react" and
"@types/react-dom" entries accordingly to keep them aligned.

Comment thread apps/mobile/app.json
"projectId": "fa9332a8-896a-4d2a-be5b-d82469b46e5d"
}
},
"owner": "supserset-sh"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in the owner field.

The owner is set to "supserset-sh" but should likely be "superset-sh" (extra 's' removed).

🔤 Proposed fix
-		"owner": "supserset-sh"
+		"owner": "superset-sh"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"owner": "supserset-sh"
"owner": "superset-sh"
🤖 Prompt for AI Agents
In `@apps/mobile/app.json` at line 43, In app.json update the "owner" field value:
locate the JSON key "owner" (currently set to "supserset-sh") and correct the
typo by replacing it with "superset-sh" so the owner string matches the intended
organization name.

Comment on lines +6 to +11
"tailwind": {
"config": "tailwind.config.js",
"css": "global.css",
"baseColor": "neutral",
"cssVariables": true
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify tailwind.config.js exists in apps/mobile/
fd -t f "tailwind.config" apps/mobile/

# Also check for CSS-based Tailwind config
if [ -f "apps/mobile/global.css" ]; then
  echo "=== global.css content (first 30 lines) ==="
  cat apps/mobile/global.css | head -30
else
  echo "global.css not found"
fi

# Check directory structure of apps/mobile
echo "=== apps/mobile/ directory structure ==="
fd -t f . apps/mobile/ -x basename {} | head -20

Repository: superset-sh/superset

Length of output: 1342


Remove or update the tailwind.config.js reference in components.json.

The tailwind.config.js file does not exist in apps/mobile/. The project is correctly using Tailwind CSS v4 with CSS-first configuration—global.css imports Tailwind via @import "tailwindcss" and defines theme variables. The reference to a non-existent config file in components.json may confuse tools or developers. Either remove the config reference or create the file if needed for your setup.

🤖 Prompt for AI Agents
In `@apps/mobile/components.json` around lines 6 - 11, Update the tailwind entry
in components.json to remove the non-existent tailwind.config.js reference:
either delete the "config": "tailwind.config.js" key from the "tailwind" object
in components.json or replace it with a valid config path if you intend to use
one; ensure the "css": "global.css" and "cssVariables": true settings remain
intact so the project continues to use the CSS-first Tailwind v4 setup (refer to
the "tailwind" object and the "config" key when making the change).

Comment thread apps/mobile/lib/database.ts Outdated
Comment on lines +12 to +84
export async function initDatabase() {
if (db) return db;

db = await SQLite.openDatabaseAsync("superset.db");

// Create todos table
await db.execAsync(`
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
completed INTEGER DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`);

return db;
}

export async function getTodos(): Promise<Todo[]> {
const database = await initDatabase();
const result = await database.getAllAsync<{
id: number;
title: string;
completed: number;
created_at: string;
}>("SELECT * FROM todos ORDER BY created_at DESC");

return result.map((row) => ({
id: row.id,
title: row.title,
completed: Boolean(row.completed),
createdAt: row.created_at,
}));
}

export async function addTodo(title: string): Promise<Todo> {
const database = await initDatabase();
const result = await database.runAsync(
"INSERT INTO todos (title) VALUES (?)",
title,
);

const newTodo = await database.getFirstAsync<{
id: number;
title: string;
completed: number;
created_at: string;
}>("SELECT * FROM todos WHERE id = ?", result.lastInsertRowId);

if (!newTodo) {
throw new Error("Failed to create todo");
}

return {
id: newTodo.id,
title: newTodo.title,
completed: Boolean(newTodo.completed),
createdAt: newTodo.created_at,
};
}

export async function toggleTodo(id: number): Promise<void> {
const database = await initDatabase();
await database.runAsync(
"UPDATE todos SET completed = NOT completed WHERE id = ?",
id,
);
}

export async function deleteTodo(id: number): Promise<void> {
const database = await initDatabase();
await database.runAsync("DELETE FROM todos WHERE id = ?", id);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if Drizzle ORM is in the project dependencies
find . -name "package.json" -type f | head -5 | xargs grep -l "drizzle" 2>/dev/null || echo "Searching root package.json..."
cat package.json | grep -i drizzle

Repository: superset-sh/superset

Length of output: 141


🏁 Script executed:

# Check if packages/db/src/ exists and what's inside
ls -la packages/db/src/ 2>/dev/null | head -20

Repository: superset-sh/superset

Length of output: 452


🏁 Script executed:

# Search for existing Drizzle ORM usage patterns in apps directory
rg "from.*drizzle" apps/ --type ts --type tsx -A 2 | head -50

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Check the file structure to confirm it's under apps/
ls -la apps/mobile/lib/ | grep database

Repository: superset-sh/superset

Length of output: 124


🏁 Script executed:

# Check packages/db/src/client.ts to confirm Drizzle setup
cat -n packages/db/src/client.ts

Repository: superset-sh/superset

Length of output: 766


🏁 Script executed:

# Search for drizzle imports in apps (without type restriction)
rg "from.*drizzle" apps/ -A 2 | head -50

Repository: superset-sh/superset

Length of output: 3908


🏁 Script executed:

# Check root package.json for drizzle
cat package.json | grep -A 5 -B 5 drizzle

Repository: superset-sh/superset

Length of output: 46


Replace raw SQL with Drizzle ORM in app code.

This module performs CRUD via raw SQL strings. The repository requires all database operations in apps/**/*.{ts,tsx} to use Drizzle ORM per project standards. Migrate these statements to Drizzle ORM to align with the existing pattern used in other app modules (apps/api, apps/desktop) and the centralized schema in packages/db/src/.

🤖 Prompt for AI Agents
In `@apps/mobile/lib/database.ts` around lines 12 - 84, This module uses raw SQL
in initDatabase, getTodos, addTodo, toggleTodo, and deleteTodo; replace these
with Drizzle ORM calls using the centralized schema from packages/db/src (import
the todos table and db client types), initialize the Drizzle client in
initDatabase (or reuse existing exported db instance) instead of
SQLite.execAsync, rewrite getTodos to run
db.select(todos).orderBy(todos.created_at.desc()), addTodo to use
db.insert(todos).values({ title }).returning(), toggleTodo to use
db.update(todos).set({ completed: sql`NOT ${todos.completed}` }) or fetch
current value then update, and deleteTodo to use
db.delete(todos).where(eq(todos.id, id)); ensure type mappings (completed
boolean, createdAt field) match your Todo type and adjust returned row property
names to createdAt, and remove raw SQL strings while importing helpers (eq, sql)
from drizzle-orm/sqlite as needed.

Comment thread apps/mobile/package.json Outdated
Comment on lines +3 to +30
import { ScrollView, View } from "react-native";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Text } from "@/components/ui/text";
import { signOut, useSession } from "@/lib/auth/client";

export default function HomeScreen() {
const [switchValue, setSwitchValue] = useState(false);
const [inputValue, setInputValue] = useState("");
const { data: session } = useSession();

// Redirect to sign in if not authenticated
if (!session) {
return <Redirect href="/(auth)/sign-in" />;
}

const handleSignOut = async () => {
await signOut();
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle sign-out failures with logging + user feedback.

signOut() errors currently surface as unhandled rejections without context. Add a try/catch with a prefixed log and Alert for the user.

🐛 Proposed fix
-import { ScrollView, View } from "react-native";
+import { Alert, ScrollView, View } from "react-native";
@@
 	const handleSignOut = async () => {
-		await signOut();
+		try {
+			await signOut();
+		} catch (error) {
+			console.error("[home/sign-out] Failed to sign out:", error);
+			Alert.alert(
+				"Sign Out Failed",
+				error instanceof Error ? error.message : String(error),
+			);
+		}
 	};

As per coding guidelines, never swallow errors silently and use prefixed console logs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { ScrollView, View } from "react-native";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Text } from "@/components/ui/text";
import { signOut, useSession } from "@/lib/auth/client";
export default function HomeScreen() {
const [switchValue, setSwitchValue] = useState(false);
const [inputValue, setInputValue] = useState("");
const { data: session } = useSession();
// Redirect to sign in if not authenticated
if (!session) {
return <Redirect href="/(auth)/sign-in" />;
}
const handleSignOut = async () => {
await signOut();
};
import { Alert, ScrollView, View } from "react-native";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Text } from "@/components/ui/text";
import { signOut, useSession } from "@/lib/auth/client";
export default function HomeScreen() {
const [switchValue, setSwitchValue] = useState(false);
const [inputValue, setInputValue] = useState("");
const { data: session } = useSession();
// Redirect to sign in if not authenticated
if (!session) {
return <Redirect href="/(auth)/sign-in" />;
}
const handleSignOut = async () => {
try {
await signOut();
} catch (error) {
console.error("[home/sign-out] Failed to sign out:", error);
Alert.alert(
"Sign Out Failed",
error instanceof Error ? error.message : String(error),
);
}
};
🤖 Prompt for AI Agents
In `@apps/mobile/screens/index/HomeScreen.tsx` around lines 3 - 30, Update the
handleSignOut implementation in HomeScreen to catch errors from signOut()
instead of letting unhandled rejections occur: wrap the await signOut() call in
a try/catch, call console.error with a clear prefix (e.g., "HomeScreen: signOut
failed") and the caught error, and show user feedback via an Alert (e.g.,
Alert.alert with a short message) so failures are logged and surfaced to the
user.

Comment thread apps/web/package.json
Comment on lines +36 to +37
"react": "19.1.0",
"react-dom": "19.1.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd apps/web && cat -n package.json | head -60

Repository: superset-sh/superset

Length of output: 1936


🌐 Web query:

Next.js 16 React 19.1.0 compatibility support

💡 Result:

Short answer: Next.js 16 is compatible with React 19, but React 19.1.0 is affected by a critical RSC remote‑code‑execution vulnerability (CVE‑2025‑55182). Do not run Next.js 16 with unpatched React 19.1.0 in production — upgrade React to one of the patched releases (e.g., 19.1.2) and upgrade Next.js to a patched 16.x release (16.0.7+). [1][2][3]

Sources

  • Next.js 16 release/upgrading notes (React 19 support and React Compiler). [1]
  • React security advisory — RSC RCE (CVE‑2025‑55182) and patched React releases (19.0.1, 19.1.2, 19.2.1). [2]
  • Next.js / Vercel security advisory and patch guidance (Next.js 16 patched releases). [3]

🌐 Web query:

tailwindcss 4.1.18 @tailwindcss/postcss 4.0.9 compatibility

💡 Result:

Yes — @tailwindcss/postcss@4.0.9 is the PostCSS plugin for Tailwind v4 and is compatible with tailwindcss@4.1.18. Install both plus PostCSS (v8.x) and use @tailwindcss/postcss in your PostCSS config. [1][2][3][4]

References

  • Tailwind docs: install Tailwind v4 with @tailwindcss/postcss via PostCSS. [1]
  • npm: @tailwindcss/postcss v4.0.9 release/version page. [2]
  • npm/jsdocs: tailwindcss v4.1.18 release/version page. [3]
  • Notes: Tailwind v4’s PostCSS plugin moved to @tailwindcss/postcss and requires PostCSS 8.x. [4]

Would you like the exact install commands and a sample postcss.config.js?


🌐 Web query:

@types/react-dom 19.1.0 react-dom version alignment

💡 Result:

Short answer: @types/react-dom@19.1.0 is the TypeScript declarations for react-dom 19.x — you should use it with react-dom 19.x (and the matching @types/react 19.x). See the @types/react-dom package page and common guidance about matching major versions. [1][2]

Sources:

  • @types/react-dom npm (19.1.0) — package details/peer dependency info. [1]
  • Community guidance: pick @types versions that match your React/TS setup (examples). [2]

Upgrade React 19.1.0 to 19.1.2 immediately — CVE-2025-55182 remote code execution vulnerability in unpatched React 19.1.0. Also align @types/react-dom to ~19.1.x instead of ^19.2.3 to match react-dom 19.1.0. (Tailwindcss 4.1.18 and @tailwindcss/postcss 4.0.9 are compatible; no change needed there.)

🤖 Prompt for AI Agents
In `@apps/web/package.json` around lines 36 - 37, Update package.json to mitigate
CVE-2025-55182 by bumping "react" from 19.1.0 to 19.1.2 and ensure "react-dom"
is also set to 19.1.2 (replace the existing 19.1.0 entries), and align the
`@types` entry for react-dom to a matching ~19.1.x range instead of ^19.2.3; run
package manager install and lockfile update and run tests/build to verify
nothing else breaks.

Comment thread packages/auth/package.json Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@plans/mobile-app-structure-comparison.md`:
- Around line 14-65: Add language identifiers to the fenced code blocks that
contain the directory tree examples in the
plans/mobile-app-structure-comparison.md file (replace the opening ``` with
```text or ```tree for each directory layout block so markdownlint MD040 is
satisfied); specifically update each triple-backtick fence that wraps the
directory-layout snippets (the blocks beginning with "apps/mobile/" and the
other two similar tree blocks) to include a language tag like text or tree.

Comment on lines +14 to +65
```
apps/mobile/
├── src/ # Source root
│ ├── app/ # Expo Router - ROUTING LOGIC ONLY
│ │ ├── _layout.tsx # Root providers & navigation config (STAYS HERE)
│ │ ├── index.tsx # Redirect logic only (STAYS HERE)
│ │ └── (tabs)/
│ │ ├── _layout.tsx # Tab navigation config (STAYS HERE)
│ │ ├── chat/
│ │ │ ├── _layout.tsx # Stack navigation config (STAYS HERE)
│ │ │ ├── index.tsx # exports from screens/ (ROUTING ONLY)
│ │ │ ├── new.tsx # exports from screens/ (ROUTING ONLY)
│ │ │ └── [id].tsx # exports from screens/ (ROUTING ONLY)
│ │ └── today/
│ │ └── index.tsx # exports from screens/ (ROUTING ONLY)
│ │
│ ├── screens/ # BUSINESS LOGIC & UI
│ │ ├── (tabs)/
│ │ │ ├── chat/
│ │ │ │ ├── chats/
│ │ │ │ │ └── ChatListScreen/
│ │ │ │ │ ├── ChatListScreen.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── components/ # Colocated components
│ │ │ │ │ └── ChatItem/
│ │ │ │ │ ├── ChatItem.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── components/
│ │ │ │ │ └── ChatItemRightActions/
│ │ │ │ │ ├── ChatItemRightActions.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── new/
│ │ │ │ │ ├── NewChatScreen.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ └── [id]/
│ │ │ │ ├── ChatScreen.tsx
│ │ │ │ └── index.ts
│ │ │ └── today/
│ │ │ └── index/
│ │ │ ├── TodayScreen.tsx
│ │ │ └── index.ts
│ │ └── components/ # Shared screen components
│ │ ├── OfflineScreen/
│ │ └── PosthogTracker/
│ │
│ ├── components/ # Global UI components
│ ├── hooks/ # Shared hooks
│ ├── providers/ # Context providers
│ ├── stores/ # State management
│ ├── utils/ # Utility functions
│ └── globals.css
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced code blocks.

markdownlint MD040 will flag these fences. Please specify a language (e.g., text or tree for directory layouts).

✅ Suggested fix
-```
+```text

(Apply to the fences starting at Lines 14, 68, 283, 318.)

</details>


Also applies to: 68-92, 283-298, 318-344

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

14-14: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @plans/mobile-app-structure-comparison.md around lines 14 - 65, Add language
identifiers to the fenced code blocks that contain the directory tree examples
in the plans/mobile-app-structure-comparison.md file (replace the opening withtext or ```tree for each directory layout block so markdownlint MD040 is
satisfied); specifically update each triple-backtick fence that wraps the
directory-layout snippets (the blocks beginning with "apps/mobile/" and the
other two similar tree blocks) to include a language tag like text or tree.


</details>

<!-- fingerprinting:phantom:triton:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

@saddlepaddle saddlepaddle changed the title feat(mobile): implement better-auth Expo integration feat(mobile): build out barebones mobile app Jan 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 20, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/admin/package.json`:
- Around line 46-50: The package.json pins "@types/react" to "~19.1.0" which
conflicts with "@types/react-dom@^19.2.3" that expects "^19.2.0"; update the
"@types/react" entry in package.json to a compatible version (e.g. "^19.2.0" or
matching "^19.2.x") to satisfy the peer requirement, then reinstall dependencies
and regenerate the lockfile (run npm/yarn install) so types and lockfile are
consistent; locate the dependency line for "@types/react" in package.json to
make this change.

In `@apps/mobile/screens/`(auth)/sign-in/SignInScreen.tsx:
- Around line 18-29: Wrap the verbose console.log/console.error calls around the
signIn.social flow with an environment check (e.g., __DEV__) so only
non-sensitive result/error details are printed in development; keep the minimal
console.error("[sign-in] Error caught:", error) for production but move the
JSON.stringify(error, null, 2) and detailed result logging into an if (__DEV__)
block around the signIn.social call results and catch block (references:
signIn.social, setLoading, provider, result, error).
♻️ Duplicate comments (10)
apps/api/package.json (1)

41-42: Align @types/react-dom with react-dom 19.1.0.

@types/react-dom is pinned to ^19.2.3 while react-dom is 19.1.0, which risks type drift and typecheck failures. Consider aligning it to ~19.1.0.

🔧 Suggested change
-    "@types/react-dom": "^19.2.3",
+    "@types/react-dom": "~19.1.0",
plans/mobile-app-structure-comparison.md (1)

14-14: Add language identifiers to fenced code blocks.

The directory tree code blocks are missing language identifiers, which triggers markdownlint MD040. Please specify a language (e.g., text or tree) for each fence.

✅ Suggested fix
-```
+```text

Apply this change to the opening fences at lines 14, 68, 283, and 318.

Also applies to: 68-68, 283-283, 318-318

apps/desktop/package.json (1)

143-158: Resolve @types/react / @types/react-dom peer mismatch; verify Tailwind plugin compatibility.

This repeats a previously flagged issue: @types/react-dom@^19.2.3 requires @types/react ^19.2.0, but it’s pinned to ~19.1.0. Also confirm @tailwindcss/vite@^4.0.9 is compatible with tailwindcss@^4.1.18.

🐛 Suggested fix for the types mismatch
-    "@types/react": "~19.1.0",
+    "@types/react": "~19.2.0",
#!/bin/bash
# Verify `@types/react-dom` peer requirement and Tailwind plugin compatibility.
npm view `@types/react-dom`@19.2.3 peerDependencies
npm view tailwindcss@4.1.18 peerDependencies
npm view `@tailwindcss/vite`@4.0.9 peerDependencies
apps/mobile/app.json (1)

43-43: Fix the Expo owner typo to avoid mis-attribution.

The owner value appears misspelled and should match the organization name.

🔧 Proposed fix
-		"owner": "supserset-sh"
+		"owner": "superset-sh"
apps/mobile/lib/auth/client.ts (1)

5-10: Avoid silent localhost fallback outside dev.

If EXPO_PUBLIC_API_URL is missing in production, the client will quietly point to localhost, leading to confusing failures. Prefer a dev-only fallback and fail fast in non-dev builds.

🔧 Proposed fix
-// Get API URL from env
-const API_URL = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3001";
+const LOG_PREFIX = "[auth/client]";
+const API_URL = process.env.EXPO_PUBLIC_API_URL;
+
+if (!API_URL) {
+	if (__DEV__) {
+		console.warn(`${LOG_PREFIX} EXPO_PUBLIC_API_URL not set, using localhost fallback`);
+	} else {
+		throw new Error(`${LOG_PREFIX} EXPO_PUBLIC_API_URL must be set in production`);
+	}
+}
+
+const BASE_URL = API_URL || "http://localhost:3001";
 
 export const authClient = createAuthClient({
-	baseURL: API_URL,
+	baseURL: BASE_URL,
 	plugins: [
 		expoClient({
 			scheme: "superset",
As per coding guidelines, use prefixed console logs.
apps/mobile/package.json (1)

15-17: Pin @electric-sql/client to an explicit version or workspace reference.

An empty version string is brittle and can resolve unpredictably.

🔧 Example fix (adjust to match root resolution)
 	"dependencies": {
 		"@better-auth/expo": "^1.4.15",
-		"@electric-sql/client": "",
+		"@electric-sql/client": "workspace:*",

Run this to confirm how the root workspace resolves the package and align the version accordingly:

#!/bin/bash
rg -n '"@electric-sql/client"' package.json
rg -n '"@electric-sql/client"' -g 'pnpm-lock.yaml' -g 'yarn.lock' -g 'package-lock.json'
apps/mobile/global.css (1)

10-12: Remove stray --color-radius token.

You already define --radius in @theme; this looks like a typo/unused variable.

🔧 Proposed fix
 		`@variant` light {
-			--color-radius: 0.5rem;
 			--color-background: hsl(0 0% 100%);
apps/mobile/components/ui/text.tsx (1)

55-61: Avoid untyped casts for web-only roles.
Consider a local type augmentation or inline comment to justify the web-only Role values so typing stays safe.

apps/mobile/screens/index/HomeScreen.tsx (1)

21-23: Handle sign-out failures explicitly.
Wrap signOut() in try/catch and surface/log errors so failures aren’t silent. As per coding guidelines, ...

apps/mobile/lib/database.ts (1)

12-84: Replace raw SQL with Drizzle ORM.
This module uses raw SQL for schema creation and CRUD; app code should use Drizzle instead. Based on learnings, ...

🧹 Nitpick comments (2)
packages/auth/src/server.ts (1)

32-38: Consolidate redundant Expo origin patterns.

The three Expo development patterns are redundant. According to Better Auth's matching behavior, "exp://" without wildcards uses prefix matching and already matches any URL starting with "exp://" (including all IP ranges, ports, and paths). The patterns "exp://**" and "exp://192.168.*.*:*/**" are unnecessary. Use only "exp://" for clarity and reduced complexity.

apps/mobile/app/(auth)/sign-in.tsx (1)

3-3: Prefer direct import to reduce barrel coupling.
Import the screen from its concrete file (e.g., @/screens/(auth)/sign-in/SignInScreen) to avoid barrel indirection. As per coding guidelines, ...

Comment thread apps/admin/package.json
Comment on lines +46 to +50
"@types/react": "~19.1.0",
"@types/react-dom": "^19.2.3",
"babel-plugin-react-compiler": "^1.0.0",
"dotenv": "^17.2.3",
"tailwindcss": "^4.0.9",
"tailwindcss": "^4.1.18",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

npm view `@types/react-dom`@19.2.3 peerDependencies

Repository: superset-sh/superset

Length of output: 93


Align @types/react with @types/react-dom peer requirements.

@types/react-dom@^19.2.3 requires @types/react ^19.2.0, but it's pinned to ~19.1.0. This creates a peer dependency conflict and can break type compatibility.

🐛 Suggested fix
-    "@types/react": "~19.1.0",
+    "@types/react": "~19.2.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@types/react": "~19.1.0",
"@types/react-dom": "^19.2.3",
"babel-plugin-react-compiler": "^1.0.0",
"dotenv": "^17.2.3",
"tailwindcss": "^4.0.9",
"tailwindcss": "^4.1.18",
"@types/react": "~19.2.0",
"@types/react-dom": "^19.2.3",
"babel-plugin-react-compiler": "^1.0.0",
"dotenv": "^17.2.3",
"tailwindcss": "^4.1.18",
🤖 Prompt for AI Agents
In `@apps/admin/package.json` around lines 46 - 50, The package.json pins
"@types/react" to "~19.1.0" which conflicts with "@types/react-dom@^19.2.3" that
expects "^19.2.0"; update the "@types/react" entry in package.json to a
compatible version (e.g. "^19.2.0" or matching "^19.2.x") to satisfy the peer
requirement, then reinstall dependencies and regenerate the lockfile (run
npm/yarn install) so types and lockfile are consistent; locate the dependency
line for "@types/react" in package.json to make this change.

Comment on lines +18 to +29
console.log("[sign-in] Button clicked:", provider);
try {
setLoading(provider);
console.log("[sign-in] Calling signIn.social...");
const result = await signIn.social({
provider,
callbackURL: "/",
});
console.log("[sign-in] signIn.social result:", result);
} catch (error) {
console.error("[sign-in] Error caught:", error);
console.error("[sign-in] Error details:", JSON.stringify(error, null, 2));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat apps/mobile/screens/\(auth\)/sign-in/SignInScreen.tsx

Repository: superset-sh/superset

Length of output: 2241


🏁 Script executed:

fd "signIn" --type f -e ts -e tsx | head -20

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

rg "signIn\.social" -A 10 -B 5 --type ts --type tsx | head -100

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

find . -path "*/lib/auth/client*" -type f

Repository: superset-sh/superset

Length of output: 96


🏁 Script executed:

cat lib/auth/client.ts 2>/dev/null || cat lib/auth/client.js 2>/dev/null || find . -name "client.ts" -o -name "client.js" | grep auth

Repository: superset-sh/superset

Length of output: 126


🏁 Script executed:

rg "export.*signIn" -A 15 -B 2 --type ts --type js | head -150

Repository: superset-sh/superset

Length of output: 228


🏁 Script executed:

cat apps/mobile/lib/auth/client.ts

Repository: superset-sh/superset

Length of output: 575


🏁 Script executed:

cat packages/auth/src/client.ts | head -200

Repository: superset-sh/superset

Length of output: 568


🏁 Script executed:

rg "signIn\.social" -A 5 --type ts --type tsx | head -100

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

rg "better-auth" package.json

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

rg "__DEV__\|NODE_ENV\|isDev" apps/mobile --type ts --type tsx | head -20

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

rg "signIn.social" -A 5 | head -100

Repository: superset-sh/superset

Length of output: 3051


🏁 Script executed:

rg "__DEV__|NODE_ENV" apps/mobile | head -20

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

cat package.json | grep -A 5 "better-auth"

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

rg "better-auth" apps/mobile/package.json 2>/dev/null || rg "better-auth" package.json

Repository: superset-sh/superset

Length of output: 124


🏁 Script executed:

rg "import.*better-auth" --type ts | head -20

Repository: superset-sh/superset

Length of output: 848


🏁 Script executed:

find . -type f -name "*.json" | xargs grep -l "better-auth" 2>/dev/null | head -5

Repository: superset-sh/superset

Length of output: 192


🏁 Script executed:

rg "const result" apps/mobile --type ts -B 3 -A 3 | head -50

Repository: superset-sh/superset

Length of output: 1384


🏁 Script executed:

web_search
better-auth signIn.social return type response tokens

Repository: superset-sh/superset

Length of output: 164


🌐 Web query:

better-auth signIn.social return type response tokens sensitive data

💡 Result:

Short answer — authClient.signIn.social returns a client response object (typically { data, error }) and does NOT hand you the social provider's raw access/refresh tokens in that response for the normal redirect flow. Instead:

  • For the normal redirect flow you get a response with data.url (the provider redirect URL) or an object indicating success; the provider tokens are exchanged/kept server-side and session cookies are set by Better Auth. Sensitive provider tokens are not exposed to the client in that response. [1][2]

  • If you use the idToken / idToken.accessToken option (client obtains the provider token and you send it to Better Auth), you supply the token to signIn.social so the server can validate it; the call still does not return raw provider credentials back to the client — the server uses them to create the session. [2]

  • The client API uses the { data, error } pattern and also supports asResponse/asResponse-style options for lower-level responses; check for data.url when using disableRedirect or proxy flows. [1][3]

If you want exact TypeScript types or the full response shape, point me to the version of better-auth you’re using (package version) and I’ll fetch the precise client types.

Sources: Better Auth docs (Basic Usage / Social Sign-On, Google social idToken example) and client reference. [1] [2] [3]

References
[1] Better Auth — Basic Usage / Social Sign‑On.
[2] Better Auth — Google social sign‑in (idToken example).
[3] Better Auth docs — client response shape ({ data, error }, asResponse, data.url).


🏁 Script executed:

rg "console.log|console.error" apps/mobile/screens --type ts -B 2 -A 1 | head -80

Repository: superset-sh/superset

Length of output: 1295


Guard verbose logging with environment checks.

The signIn.social() response does not expose raw OAuth tokens (better-auth keeps provider tokens server-side), but the result and error objects may contain user data or error details that shouldn't be logged in production. Wrap the detailed logging statements with __DEV__ or environment checks, while keeping basic error logging for debugging:

if (__DEV__) {
  console.log("[sign-in] signIn.social result:", result);
  console.error("[sign-in] Error details:", JSON.stringify(error, null, 2));
}
🤖 Prompt for AI Agents
In `@apps/mobile/screens/`(auth)/sign-in/SignInScreen.tsx around lines 18 - 29,
Wrap the verbose console.log/console.error calls around the signIn.social flow
with an environment check (e.g., __DEV__) so only non-sensitive result/error
details are printed in development; keep the minimal console.error("[sign-in]
Error caught:", error) for production but move the JSON.stringify(error, null,
2) and detailed result logging into an if (__DEV__) block around the
signIn.social call results and catch block (references: signIn.social,
setLoading, provider, result, error).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/auth/package.json`:
- Around line 21-26: Replace the non-existent dependency version for the
`@better-auth/expo` entry in package.json: locate the "@better-auth/expo"
dependency and update its version string from "1.4.16" to a published version
(e.g., a 1.3.x release such as "1.3.8-beta.4" or another confirmed npm version)
so installs won't fail; ensure the updated version matches the package registry
and run a quick install to verify.
♻️ Duplicate comments (3)
apps/api/package.json (1)

41-42: Align @types/react-dom to match @types/react at ~19.1.0.

@types/react is pinned to ~19.1.0 but @types/react-dom remains at ^19.2.3. This version mismatch can cause TypeScript/JSX type conflicts since react-dom types 19.2.x expect React types from the 19.2.x line.

Suggested fix
 	"@types/react": "~19.1.0",
-	"@types/react-dom": "^19.2.3",
+	"@types/react-dom": "~19.1.0",
apps/docs/package.json (1)

37-38: Align @types/react-dom to match @types/react at ~19.1.0.

Same issue as in apps/api/package.json: @types/react is ~19.1.0 while @types/react-dom is ^19.2.3. Update to ~19.1.0 for consistency.

Suggested fix
 	"@types/react": "~19.1.0",
-	"@types/react-dom": "^19.2.3",
+	"@types/react-dom": "~19.1.0",
apps/desktop/package.json (1)

143-144: Update @types/react to align with @types/react-dom peer dependency.

@types/react at ~19.1.0 is incompatible with @types/react-dom@^19.2.3, which requires @types/react ^19.2.0 as a peer dependency. Either update @types/react to ~19.2.0 or downgrade @types/react-dom to a version compatible with the 19.1.x types.

🛠️ Suggested fix

Option 1 (Preferred): Update @types/react to match @types/react-dom peer requirement:

-		"@types/react": "~19.1.0",
+		"@types/react": "~19.2.0",

Option 2: Downgrade @types/react-dom to align with React 19.1.0:

-		"@types/react-dom": "^19.2.3",
+		"@types/react-dom": "~19.1.0",
🧹 Nitpick comments (3)
packages/auth/tsconfig.json (1)

4-5: Reasonable workaround for dependency type conflicts.

Adding skipLibCheck: true to resolve the Better-Auth/Expo duplicate type conflicts is a pragmatic fix. This is a common pattern when dealing with complex dependency graphs that have conflicting .d.ts files.

Consider adding a brief comment above this option explaining why it's needed, so future maintainers know it can potentially be removed if upstream type conflicts are resolved.

apps/mobile/lib/env.ts (1)

16-24: Consider wrapping parse with a descriptive error for better DX.

If EXPO_PUBLIC_API_URL is missing, Zod throws a generic validation error. Adding a try-catch with a clearer message would help developers troubleshoot faster.

Optional improvement
-export const env = envSchema.parse({
+const parsed = envSchema.safeParse({
 	NODE_ENV: process.env.NODE_ENV as unknown,
 	EXPO_PUBLIC_API_URL: process.env.EXPO_PUBLIC_API_URL as unknown,
 	EXPO_PUBLIC_WEB_URL: process.env.EXPO_PUBLIC_WEB_URL as unknown,
 	EXPO_PUBLIC_DEEP_LINK_SCHEME: process.env
 		.EXPO_PUBLIC_DEEP_LINK_SCHEME as unknown,
 	EXPO_PUBLIC_DEEP_LINK_DOMAIN: process.env
 		.EXPO_PUBLIC_DEEP_LINK_DOMAIN as unknown,
 });
+
+if (!parsed.success) {
+	console.error("[env] Invalid environment configuration:", parsed.error.flatten());
+	throw new Error("[env] Missing or invalid environment variables. Check EXPO_PUBLIC_API_URL is set.");
+}
+
+export const env = parsed.data;
apps/mobile/lib/base-url.ts (1)

34-39: Consider using URL object consistently for hostname replacement.

Creating a URL object but then using string.replace() could miss edge cases (e.g., hostname appearing in path). Using the URL object's properties directly would be more robust.

More robust URL manipulation
 		if (devServerIp) {
 			const urlObj = new URL(apiUrl);
-			const replacedUrl = apiUrl.replace(urlObj.hostname, devServerIp);
+			urlObj.hostname = devServerIp;
+			const replacedUrl = urlObj.toString().replace(/\/$/, "");
 			console.log(
 				`[base-url] Auto-detected dev server IP: ${devServerIp}, using ${replacedUrl}`,
 			);
 			return replacedUrl;
 		}

Comment on lines +21 to +26
"@better-auth/expo": "1.4.16",
"@superset/db": "workspace:*",
"@superset/shared": "workspace:*",
"@t3-oss/env-core": "^0.13.8",
"@t3-oss/env-nextjs": "^0.13.8",
"better-auth": "^1.4.9",
"better-auth": "1.4.16",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify both better-auth packages exist at version 1.4.16
echo "Checking better-auth@1.4.16..."
curl -s "https://registry.npmjs.org/better-auth/1.4.16" | jq -r '.version // "NOT FOUND"'

echo "Checking `@better-auth/expo`@1.4.16..."
curl -s "https://registry.npmjs.org/@better-auth%2Fexpo/1.4.16" | jq -r '.version // "NOT FOUND"'

Repository: superset-sh/superset

Length of output: 184


🌐 Web query:

better-auth@1.4.16 npm package version

💡 Result:

Yes — better-auth version 1.4.16 is published. You can install it with:
npm install better-auth@1.4.16. [1][2]

Sources:

  • Release/archive (tarball) listing for better-auth 1.4.16. [1]
  • Snyk / package metadata showing latest 1.4.16. [2]

🌐 Web query:

@better-auth/expo@1.4.16 npm package version

💡 Result:

I couldn't find @better-auth/expo published on npm as version 1.4.16. The Better Auth monorepo has a 1.4.16 release archive, but the @better-auth/expo package versions published to npm appear to be in the 1.0–1.3 range (examples: 1.3.0-beta.11 / 1.3.8-beta.4). [1][2]

Sources:

  • npm package pages for @better-auth/expo (published versions). [1]
  • better-auth v1.4.16 archive (monorepo release contents). [2]

Fix @better-auth/expo version: 1.4.16 does not exist on npm.

better-auth@1.4.16 is published and valid, but @better-auth/expo@1.4.16 does not exist on npm. Published versions of @better-auth/expo are in the 1.0–1.3 range (e.g., 1.3.8-beta.4). Update line 21 to a version that actually exists on npm to prevent install failures.

🤖 Prompt for AI Agents
In `@packages/auth/package.json` around lines 21 - 26, Replace the non-existent
dependency version for the `@better-auth/expo` entry in package.json: locate the
"@better-auth/expo" dependency and update its version string from "1.4.16" to a
published version (e.g., a 1.3.x release such as "1.3.8-beta.4" or another
confirmed npm version) so installs won't fail; ensure the updated version
matches the package registry and run a quick install to verify.

- Add zod-based env validation in lib/env.ts for type-safe environment variables
- Implement auto dev server IP detection in lib/base-url.ts following T3 pattern
  - Automatically replaces localhost with Expo dev server IP in development
  - Fails fast if localhost is used in production
  - Logs auto-detected IP for debugging
- Update auth client to use getBaseUrl() for automatic localhost handling
- Remove unused database.ts and expo-sqlite dependency
- Remove inline comments from theme.ts
- Add @types/node for TypeScript process.env support
- Update uniwind types (auto-generated)
- Add @react-navigation/native dependency to mobile app
- Update better-auth packages to 1.4.16 for consistency
- Add skipLibCheck to auth package to work around bun cache path issues
- Clear and reinstall node_modules/.bun to resolve duplicate dependency caches
- Format uniwind-types.d.ts

This fixes TypeScript errors caused by bun caching multiple versions of
drizzle-orm and @better-auth/core with different hashes, which prevented
portable type references.
- Update @tanstack/db from ^0.5.16 to 0.5.22
- Add ts-expect-error for RefLeaf<string> comparison type inference issue
- Add null check before querying members
- Remove unnecessary comment from mobile app/index.tsx

The TanStack DB eq() function has a type inference issue when comparing
a RefLeaf<string> (column reference) to a string literal in where clauses.
The functionality works correctly at runtime per TanStack DB docs, but
TypeScript can't match the overload signatures. Added @ts-expect-error
with explanation.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/mobile/app/`(auth)/sign-in.tsx:
- Around line 5-12: The SignInRoute is not checking useSession's isPending flag,
causing a flicker of SignInScreen during hydration; update SignInRoute to
destructure isPending from useSession (e.g., const { data: session, isPending }
= useSession()) and return early (null or a loader) while isPending is true,
then keep the existing session check to Redirect to "/" when session exists
before rendering SignInScreen; modify the function containing SignInRoute, which
renders SignInScreen and Redirect, to implement this gating behavior.

In `@apps/mobile/components/ui/text.tsx`:
- Around line 73-93: The Text component currently ignores refs; wrap the
functional component with React.forwardRef and accept a ref parameter (e.g.,
function Text(props, ref) or const Text = React.forwardRef(function Text(props,
ref) { ... } ) ), keep the existing props/types (including
React.RefAttributes<RNText>), and pass the ref through to the rendered Component
(Component ref={ref}). Update the exported symbol (Text) to be the forwarded-ref
version so consumers can attach refs to RNText.
♻️ Duplicate comments (4)
apps/desktop/package.json (1)

143-144: Peer dependency mismatch between @types/react and @types/react-dom.

@types/react-dom@^19.2.3 requires @types/react version ^19.2.0 as a peer dependency, but line 143 pins @types/react to ~19.1.0. This will cause peer dependency warnings and potential type incompatibilities.

Update @types/react to ~19.2.0 to satisfy the peer dependency constraint, or downgrade @types/react-dom to a 19.1.x version.

plans/mobile-app-structure-comparison.md (1)

14-14: Add language identifiers to fenced code blocks.

The fenced code blocks at lines 14, 68, 283, and 318 are missing language identifiers, which violates markdownlint MD040. Specify text or tree for directory layout blocks.

✅ Suggested fix

Apply to all four blocks:

-```
+```text

Also applies to: 68-68, 283-283, 318-318

apps/mobile/components/ui/text.tsx (1)

55-62: Web-only role casts still bypass RN Role typing.
This was already called out in a previous review; keeping it here as a duplicate reminder.

packages/auth/package.json (1)

21-26: Verify @better-auth/expo@1.4.16 is published (install blocker if not).

Earlier review notes flagged this version as unpublished; if that’s still true, installs will fail. Please confirm the version exists and is compatible with better-auth@1.4.16, and adjust to a published release if needed.

#!/bin/bash
# Verify versions exist on npm registry
curl -s "https://registry.npmjs.org/better-auth/1.4.16" | jq -r '.version // "NOT FOUND"'
curl -s "https://registry.npmjs.org/@better-auth%2Fexpo/1.4.16" | jq -r '.version // "NOT FOUND"'
🧹 Nitpick comments (3)
apps/mobile/lib/theme.ts (1)

3-45: Extract shared token literals to top-level constants.
Several values (surface/foreground/muted/border and radius) are repeated across light/dark; centralizing them reduces drift and aligns with repo guidance. As per coding guidelines, extract hardcoded values to named constants.

♻️ Suggested refactor
 import { DarkTheme, DefaultTheme, type Theme } from "@react-navigation/native";
 
+const RADIUS = "0.5rem";
+const LIGHT_SURFACE = "hsl(0 0% 100%)";
+const LIGHT_FOREGROUND = "hsl(240 10% 3.9%)";
+const LIGHT_MUTED = "hsl(240 4.8% 95.9%)";
+const LIGHT_BORDER = "hsl(240 5.9% 90%)";
+const DARK_SURFACE = "hsl(240 10% 3.9%)";
+const DARK_FOREGROUND = "hsl(0 0% 98%)";
+const DARK_MUTED = "hsl(240 3.7% 15.9%)";
+
 export const THEME = {
 	light: {
-		background: "hsl(0 0% 100%)",
-		foreground: "hsl(240 10% 3.9%)",
-		card: "hsl(0 0% 100%)",
-		cardForeground: "hsl(240 10% 3.9%)",
-		popover: "hsl(0 0% 100%)",
-		popoverForeground: "hsl(240 10% 3.9%)",
+		background: LIGHT_SURFACE,
+		foreground: LIGHT_FOREGROUND,
+		card: LIGHT_SURFACE,
+		cardForeground: LIGHT_FOREGROUND,
+		popover: LIGHT_SURFACE,
+		popoverForeground: LIGHT_FOREGROUND,
 		primary: "hsl(240 5.9% 10%)",
 		primaryForeground: "hsl(0 0% 98%)",
-		secondary: "hsl(240 4.8% 95.9%)",
+		secondary: LIGHT_MUTED,
 		secondaryForeground: "hsl(240 5.9% 10%)",
-		muted: "hsl(240 4.8% 95.9%)",
+		muted: LIGHT_MUTED,
 		mutedForeground: "hsl(240 3.8% 46.1%)",
-		accent: "hsl(240 4.8% 95.9%)",
+		accent: LIGHT_MUTED,
 		accentForeground: "hsl(240 5.9% 10%)",
 		destructive: "hsl(0 84.2% 60.2%)",
-		border: "hsl(240 5.9% 90%)",
-		input: "hsl(240 5.9% 90%)",
+		border: LIGHT_BORDER,
+		input: LIGHT_BORDER,
 		ring: "hsl(240 5.9% 10%)",
-		radius: "0.5rem",
+		radius: RADIUS,
 	},
 	dark: {
-		background: "hsl(240 10% 3.9%)",
-		foreground: "hsl(0 0% 98%)",
-		card: "hsl(240 10% 3.9%)",
-		cardForeground: "hsl(0 0% 98%)",
-		popover: "hsl(240 10% 3.9%)",
-		popoverForeground: "hsl(0 0% 98%)",
+		background: DARK_SURFACE,
+		foreground: DARK_FOREGROUND,
+		card: DARK_SURFACE,
+		cardForeground: DARK_FOREGROUND,
+		popover: DARK_SURFACE,
+		popoverForeground: DARK_FOREGROUND,
 		primary: "hsl(0 0% 98%)",
 		primaryForeground: "hsl(240 5.9% 10%)",
-		secondary: "hsl(240 3.7% 15.9%)",
+		secondary: DARK_MUTED,
 		secondaryForeground: "hsl(0 0% 98%)",
-		muted: "hsl(240 3.7% 15.9%)",
+		muted: DARK_MUTED,
 		mutedForeground: "hsl(240 5% 64.9%)",
-		accent: "hsl(240 3.7% 15.9%)",
+		accent: DARK_MUTED,
 		accentForeground: "hsl(0 0% 98%)",
 		destructive: "hsl(0 62.8% 30.6%)",
-		border: "hsl(240 3.7% 15.9%)",
-		input: "hsl(240 3.7% 15.9%)",
+		border: DARK_MUTED,
+		input: DARK_MUTED,
 		ring: "hsl(240 4.9% 83.9%)",
-		radius: "0.5rem",
+		radius: RADIUS,
 	},
 };
apps/mobile/app/(auth)/sign-in.tsx (1)

3-3: Prefer concrete import over barrel export.

Guidelines recommend importing the file directly to avoid barrel‑induced cycles.

♻️ Proposed change
-import SignInScreen from "@/screens/(auth)/sign-in";
+import SignInScreen from "@/screens/(auth)/sign-in/SignInScreen";
apps/mobile/lib/auth/client.ts (1)

9-16: Extract magic string values to module constants.

The hardcoded "superset" values should be extracted to named constants at the top of the module for consistency and to avoid drift.

♻️ Proposed refactor
+const AUTH_SCHEME = "superset";
+const AUTH_STORAGE_PREFIX = "superset";
+
 export const authClient = createAuthClient({
 	baseURL: BASE_URL,
 	plugins: [
 		expoClient({
-			scheme: "superset",
-			storagePrefix: "superset",
+			scheme: AUTH_SCHEME,
+			storagePrefix: AUTH_STORAGE_PREFIX,
 			storage: SecureStore,
 		}),
 	],
 });

Comment on lines +5 to +12
export default function SignInRoute() {
const { data: session } = useSession();

if (session) {
return <Redirect href="/" />;
}

return <SignInScreen />;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

better-auth useSession hook loading status documentation

💡 Result:

Summary — useSession loading status

  • The useSession hook (from the React client) returns an isPending boolean that represents the loading state (true while session fetch is pending). [1] (better-auth.com)
  • Example return shape shown in the docs: { data: session, isPending, error, refetch }. Use isPending to show loading UI. [2] (better-auth.com)
  • Note: there are open GitHub issues about isPending/hydration and cases where useSession doesn’t update automatically after server-side auth changes — these can cause isPending or session state to behave unexpectedly in some SSR/React 19 setups. Check the linked issues if you see stale or always-true isPending. [3] (github.com)

Sources

Do you want the exact code snippet from the docs or links to the issues?

Citations:


🏁 Script executed:

# Search for useSession usage in the codebase
rg "useSession" --type tsx --type ts -B 2 -A 5

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Look for isPending usage specifically
rg "isPending" --type tsx --type ts -B 2 -A 2

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Check if there are other auth route files for comparison
fd "auth|sign" --type f -e tsx -e ts

Repository: superset-sh/superset

Length of output: 897


🏁 Script executed:

# Search for useSession usage
rg "useSession" -B 2 -A 5

Repository: superset-sh/superset

Length of output: 16935


🏁 Script executed:

# Search for isPending usage
rg "isPending" -B 2 -A 2

Repository: superset-sh/superset

Length of output: 47630


🏁 Script executed:

# Read the sign-in route file
cat -n apps/mobile/app/\(auth\)/sign-in.tsx

Repository: superset-sh/superset

Length of output: 458


🏁 Script executed:

# Read the SignInScreen component
cat -n apps/mobile/screens/\(auth\)/sign-in/SignInScreen.tsx

Repository: superset-sh/superset

Length of output: 2794


Gate on isPending to prevent sign-in screen flicker during hydration.

The useSession() hook returns an isPending flag that's true while the session is loading. Without checking it, this route may briefly render the sign-in screen before redirecting an already-authenticated user. Use the isPending flag to avoid this flicker:

const { data: session, isPending } = useSession();

if (isPending) {
  return null; // or a loading indicator
}

if (session) {
  return <Redirect href="/" />;
}

return <SignInScreen />;

The desktop app implements this pattern correctly in apps/desktop/src/renderer/routes/sign-in/page.tsx.

🤖 Prompt for AI Agents
In `@apps/mobile/app/`(auth)/sign-in.tsx around lines 5 - 12, The SignInRoute is
not checking useSession's isPending flag, causing a flicker of SignInScreen
during hydration; update SignInRoute to destructure isPending from useSession
(e.g., const { data: session, isPending } = useSession()) and return early (null
or a loader) while isPending is true, then keep the existing session check to
Redirect to "/" when session exists before rendering SignInScreen; modify the
function containing SignInRoute, which renders SignInScreen and Redirect, to
implement this gating behavior.

Comment on lines +73 to +93
function Text({
className,
asChild = false,
variant = "default",
...props
}: React.ComponentProps<typeof RNText> &
TextVariantProps &
React.RefAttributes<RNText> & {
asChild?: boolean;
}) {
const textClass = React.useContext(TextClassContext);
const Component = asChild ? Slot.Text : RNText;
return (
<Component
className={cn(textVariants({ variant }), textClass, className)}
role={variant ? ROLE[variant] : undefined}
aria-level={variant ? ARIA_LEVEL[variant] : undefined}
{...props}
/>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/mobile/components/ui/text.tsx

Repository: superset-sh/superset

Length of output: 3181


🏁 Script executed:

rg -A 5 "Slot\.Text" apps/mobile/components/ui/

Repository: superset-sh/superset

Length of output: 516


🌐 Web query:

@rn-primitives/slot Slot.Text ref forwarding support

💡 Result:

Short answer: No — Slot.Text itself does not provide special ref-forwarding; it merges and passes props to its child but doesn't convert a forwarded ref into a child's ref for you. If you need a forwarded ref on a slottable primitive, use the library's ref helpers (e.g., useAugmentedRef / forwardRef in your wrapper) to attach/compose refs. See the Slot docs and the hooks (useAugmentedRef) docs. [1][2][3]

Sources

  • Slot primitive docs (usage/props). [1]
  • Hooks docs — useAugmentedRef (for composing/using forwarded refs). [2]
  • rn-primitives GitHub (primitives include slot + hooks). [3]

Forward refs using React.forwardRef; current implementation silently ignores them.

React.RefAttributes<RNText> is declared in the type signature (line 80), but Text is not wrapped in forwardRef. Spreading {...props} does not forward the special ref prop—it is filtered out by React. This means any refs passed by consumers are dropped.

Proposed fix
- function Text({
- 	className,
- 	asChild = false,
- 	variant = "default",
- 	...props
- }: React.ComponentProps<typeof RNText> &
- 	TextVariantProps &
- 	React.RefAttributes<RNText> & {
- 		asChild?: boolean;
- 	}) {
- 	const textClass = React.useContext(TextClassContext);
- 	const Component = asChild ? Slot.Text : RNText;
- 	return (
- 		<Component
- 			className={cn(textVariants({ variant }), textClass, className)}
- 			role={variant ? ROLE[variant] : undefined}
- 			aria-level={variant ? ARIA_LEVEL[variant] : undefined}
- 			{...props}
- 		/>
- 	);
- }
+ const Text = React.forwardRef<
+ 	RNText,
+ 	React.ComponentProps<typeof RNText> &
+ 		TextVariantProps & {
+ 			asChild?: boolean;
+ 		}
+ >(function Text(
+ 	{ className, asChild = false, variant = "default", ...props },
+ 	ref,
+ ) {
+ 	const textClass = React.useContext(TextClassContext);
+ 	const Component = asChild ? Slot.Text : RNText;
+ 	return (
+ 		<Component
+ 			ref={ref}
+ 			className={cn(textVariants({ variant }), textClass, className)}
+ 			role={variant ? ROLE[variant] : undefined}
+ 			aria-level={variant ? ARIA_LEVEL[variant] : undefined}
+ 			{...props}
+ 		/>
+ 	);
+ });
+
+ Text.displayName = "Text";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function Text({
className,
asChild = false,
variant = "default",
...props
}: React.ComponentProps<typeof RNText> &
TextVariantProps &
React.RefAttributes<RNText> & {
asChild?: boolean;
}) {
const textClass = React.useContext(TextClassContext);
const Component = asChild ? Slot.Text : RNText;
return (
<Component
className={cn(textVariants({ variant }), textClass, className)}
role={variant ? ROLE[variant] : undefined}
aria-level={variant ? ARIA_LEVEL[variant] : undefined}
{...props}
/>
);
}
const Text = React.forwardRef<
RNText,
React.ComponentProps<typeof RNText> &
TextVariantProps & {
asChild?: boolean;
}
>(function Text(
{ className, asChild = false, variant = "default", ...props },
ref,
) {
const textClass = React.useContext(TextClassContext);
const Component = asChild ? Slot.Text : RNText;
return (
<Component
ref={ref}
className={cn(textVariants({ variant }), textClass, className)}
role={variant ? ROLE[variant] : undefined}
aria-level={variant ? ARIA_LEVEL[variant] : undefined}
{...props}
/>
);
});
Text.displayName = "Text";
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/text.tsx` around lines 73 - 93, The Text component
currently ignores refs; wrap the functional component with React.forwardRef and
accept a ref parameter (e.g., function Text(props, ref) or const Text =
React.forwardRef(function Text(props, ref) { ... } ) ), keep the existing
props/types (including React.RefAttributes<RNText>), and pass the ref through to
the rendered Component (Component ref={ref}). Update the exported symbol (Text)
to be the forwarded-ref version so consumers can attach refs to RNText.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant