Skip to content

Social Login - #27

Merged
chakrihacker merged 7 commits into
mainfrom
feat/social-login
Nov 26, 2025
Merged

chakrihacker merged 7 commits into
mainfrom
feat/social-login

Conversation

@chakrihacker

@chakrihacker chakrihacker commented Nov 25, 2025 •

Copy link
Copy Markdown
Contributor

User description

PR Type

Enhancement, Configuration

Description

  • Implement comprehensive social login authentication with support for Google, Facebook, GitHub, Apple, and LinkedIn providers

  • Add environment variable validation using Zod schema for type-safe configuration management

  • Create new social login landing page (/auth) replacing the previous sign-in redirect flow

  • Refactor mobile authentication screens (sign-in, sign-up, forgot-password, reset-password) with improved layout structure using Screen wrapper component

  • Replace Card components with semantic View containers throughout authentication pages for better maintainability

  • Add typography variants and accessibility support to Text component with semantic HTML roles

  • Create reusable Screen wrapper component combining SafeAreaView, SystemBars, and KeyboardAvoidingView with platform-specific handling

  • Add Icon wrapper component for Lucide icons with Nativewind support

  • Update authentication redirect paths across the application to point to new /auth landing page

  • Update development configuration with trusted origins for Expo's exp:// scheme

  • Update Biome from 1.9.4 to 2.3.7 and TypeScript to 5.9.3

  • Update Turborepo configuration with TUI mode and new db:reset task

  • Update VSCode default formatter from Prettier to Biome

Diagram Walkthrough

flowchart LR
  A["Environment Variables<br/>Zod Validation"] -->|"Type-safe config"| B["Auth Configuration<br/>Social Providers"]
  B -->|"Google, Facebook,<br/>GitHub, Apple, LinkedIn"| C["Social Login<br/>Landing Page"]
  C -->|"Authenticate"| D["Mobile Auth Screens<br/>Refactored Layout"]
  E["Screen Wrapper<br/>Component"] -->|"Wraps"| D
  F["Typography & Icon<br/>Components"] -->|"Enhance"| D
  D -->|"Redirect to"| G["Protected Routes<br/>Updated Paths"]
Loading

File Walkthrough

Relevant files
Configuration changes
7 files
env.ts
Environment variable validation schema with Zod                   

apps/api/src/env.ts

  • New file implementing environment variable validation using Zod schema
  • Defines required and optional environment variables for database,
    authentication, email services, and observability
  • Validates environment variables on startup and exits with error
    logging if validation fails
  • Exports validated env object for type-safe access throughout the
    application
+70/-0   
index.ts
Environment validation initialization                                       

apps/api/src/index.ts

  • Adds import of @src/env module to ensure environment validation runs
    at application startup
+1/-0     
turbo.json
Update Turborepo configuration and add db:reset task         

turbo.json

  • Added "ui": "tui" configuration for Turborepo UI
  • Added "interactive": true to dev task configuration
  • Added new db:reset task with cache disabled and persistent flag
+7/-1     
.env.example
Add social login provider environment variables                   

apps/api/.env.example

  • Added social login provider configuration variables for Google,
    Facebook, GitHub, Apple, and LinkedIn
  • Each provider includes CLIENT_ID and CLIENT_SECRET environment
    variables
  • Added comments indicating social login configuration section
+11/-0   
components.json
Add shadcn/ui configuration to components.json                     

apps/mobile/components.json

  • Added $schema reference for shadcn/ui configuration
  • Added style, rsc, tsx, and tailwind configuration sections
  • Updated aliases to include utils, ui, and hooks paths
  • Added baseColor and cssVariables tailwind settings
+15/-2   
settings.json
Update VSCode default formatter to Biome                                 

.vscode/settings.json

  • Changed default formatter from Prettier to Biome
+1/-1     
biome.jsonc
Update Biome configuration and schema version                       

biome.jsonc

  • Updated Biome schema version from 2.2.0 to 2.3.7
  • Updated extends configuration to use ultracite/core and
    ultracite/react presets
+2/-2     
Enhancement
19 files
auth.ts
Social login providers and development origin configuration

apps/api/src/lib/auth.ts

  • Imports validated env object from new env configuration file
  • Adds development-mode trusted origins for Expo's exp:// scheme with
    local IP ranges
  • Configures social provider authentication (Google, Facebook, GitHub,
    Apple, LinkedIn) with credentials from environment variables
  • Replaces hardcoded basePath with commented configuration
+35/-1   
config.ts
Email configuration using validated environment variables

apps/api/src/lib/email/config.ts

  • Replaces direct process.env access with validated env object from new
    configuration
  • Simplifies SMTP port and secure flag handling using nullish coalescing
    operator
  • Ensures type-safe email configuration with validated environment
    variables
+14/-13 
button.tsx
Button component styling and platform-specific improvements

apps/mobile/src/components/ui/button.tsx

  • Refactors button component styling with improved platform-specific
    handling using Platform.select()
  • Updates variant styles with better dark mode support and focus states
  • Simplifies size variants with responsive breakpoints
  • Removes ref parameter from function signature and improves className
    handling
+60/-33 
reset-password.tsx
Reset password screen UI simplification and navigation     

apps/mobile/src/app/(public)/auth/reset-password.tsx

  • Removes Card component usage and simplifies layout structure
  • Adds back button with ChevronLeft icon for navigation
  • Refactors UI layout using View components with gap utilities instead
    of Card subcomponents
  • Improves responsive design and adds null coalescing to token parameter
+56/-60 
sign-in.tsx
Refactor sign-in page layout and navigation structure       

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

  • Replaced Card component with Screen wrapper and custom layout using
    View components
  • Added back navigation button with ChevronLeft icon at the top
  • Converted card header/footer structure to semantic View containers
    with gap-based spacing
  • Updated Link components to use asChild prop for proper navigation
    behavior
+79/-77 
forgot-password.tsx
Refactor forgot-password page with Screen wrapper               

apps/mobile/src/app/(public)/auth/forgot-password.tsx

  • Replaced Card component with Screen wrapper and custom View-based
    layout
  • Added back navigation button with ChevronLeft icon
  • Converted card structure to semantic View containers with consistent
    spacing
  • Imported useRouter hook for navigation functionality
+54/-51 
index.tsx
Implement social login authentication landing page             

apps/mobile/src/app/(public)/auth/index.tsx

  • Replaced redirect-only implementation with full social login
    authentication page
  • Added social login buttons for Google, Facebook, GitHub, Apple, and
    LinkedIn providers
  • Implemented handleSocialLogin function using authClient.signIn.social
  • Added email login option and terms of service disclaimer
+100/-5 
text.tsx
Add typography variants and accessibility support               

apps/mobile/src/components/ui/text.tsx

  • Added textVariants CVA configuration with semantic typography variants
    (h1-h4, p, blockquote, code, etc.)
  • Implemented platform-specific styling using Platform.select for web
    and native
  • Added ROLE and ARIA_LEVEL mappings for accessibility support
  • Enhanced Text component to accept variant prop and apply semantic HTML
    roles
+70/-7   
sign-up-success-card.tsx
Replace Card component with semantic View layout                 

apps/mobile/src/components/auth/sign-up-success-card.tsx

  • Removed Card component imports and replaced with semantic View
    containers
  • Converted CardHeader, CardContent, CardFooter to View components with
    gap-based spacing
  • Updated text styling to use Text component with inline className for
    typography
+12/-19 
sign-up-form.tsx
Replace Card component with semantic View structure           

apps/mobile/src/components/auth/sign-up-form.tsx

  • Removed Card component and replaced with View containers
  • Converted card structure (header, content, footer) to semantic View
    components
  • Updated text styling to use Text component with font-bold and
    muted-foreground classes
  • Adjusted button className from flex-1 to maintain consistent sizing
+13/-20 
icon.tsx
Add Icon wrapper component for Lucide icons                           

apps/mobile/src/components/ui/icon.tsx

  • Created new Icon wrapper component for Lucide icons with Nativewind
    support
  • Implemented cssInterop configuration to map className to icon size
    properties
  • Added comprehensive JSDoc documentation with usage examples
  • Provides reusable icon component with foreground color default
+54/-0   
index.tsx
Update splash screen with safe area and auth redirect       

apps/mobile/src/app/index.tsx

  • Wrapped loading state with SafeAreaView for proper screen insets
  • Changed unauthenticated redirect from /(public)/auth/sign-in to
    /(public)/auth
  • Added react-native-safe-area-context import for safe area handling
+4/-3     
_layout.tsx
Simplify auth layout and update routing structure               

apps/mobile/src/app/(public)/auth/_layout.tsx

  • Removed SafeAreaView wrapper and Header component from layout
  • Removed social-sign-in screen from Stack configuration
  • Added index screen to Stack for social login landing page
  • Updated authenticated redirect path to
    /(public)/(protected)/home/(tabs)
+7/-12   
screen.tsx
Create reusable Screen wrapper component                                 

apps/mobile/src/components/screen.tsx

  • Created new Screen wrapper component combining SafeAreaView,
    SystemBars, and KeyboardAvoidingView
  • Implemented platform-specific keyboard offset handling (iOS vs
    Android)
  • Added support for customizable SystemBarsProps and
    KeyboardAvoidingViewProps
  • Integrated color scheme detection for system bar styling
+42/-0   
sign-up.tsx
Wrap sign-up page with Screen and add navigation                 

apps/mobile/src/app/(public)/auth/sign-up.tsx

  • Wrapped SignUpContainer with Screen component for consistent layout
  • Added back navigation button with ChevronLeft icon
  • Imported useRouter hook and Screen component
  • Added conditional rendering of back button based on navigation history
+22/-1   
sign-up.container.tsx
Add padding to sign-up container                                                 

apps/mobile/src/containers/auth/sign-up.container.tsx

  • Added p-6 padding class to main container View for consistent spacing
+1/-1     
_layout.tsx
Update admin layout auth redirect path                                     

apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx

  • Updated unauthenticated redirect path from /auth/sign-in to /auth
+1/-1     
HeaderUser.tsx
Update HeaderUser login redirect path                                       

apps/mobile/src/containers/core/HeaderUser.tsx

  • Updated login button redirect from /auth/sign-in to /auth
+1/-1     
_layout.tsx
Update protected layout auth redirect path                             

apps/mobile/src/app/(public)/(protected)/_layout.tsx

  • Updated unauthenticated redirect path from /auth/sign-in to /auth
+1/-1     
Dependencies
3 files
pnpm-lock.yaml
Dependency updates and cleanup                                                     

pnpm-lock.yaml

  • Updates @biomejs/biome from 1.9.4 to 2.3.7 with corresponding
    platform-specific CLI packages
  • Updates ultracite from 5.3.3 to 6.3.6 with new dependency structure
  • Removes esbuild, rollup, vitest, and related testing dependencies
  • Adds new glob and minimatch versions for improved file matching
+129/-936
package.json
Ultracite dependency version bump                                               

package.json

  • Updates ultracite dependency from 5.3.3 to 6.3.6
+1/-1     
pnpm-workspace.yaml
Update Biome and TypeScript dependencies                                 

pnpm-workspace.yaml

  • Updated @biomejs/biome version from 1.9.4 to 2.3.7
  • Updated typescript version from ^5.7.3 to ^5.9.3
+2/-2     
Additional files
2 files
social-sign-in.tsx +0/-12   
auth-client.ts +0/-1     

CodeAnt-AI Description

Introduce social login landing page and refreshed auth screens

What Changed

  • /auth now shows a dedicated landing screen with Google, Facebook, GitHub, Apple, and LinkedIn buttons plus a safe-area-aware layout so users can pick social login or continue with email.
  • Sign-in, sign-up, forgot/reset password flows now use the shared Screen/Header components, consistent spacing, and easier navigation with back controls and updated button/link alignment.
  • Backend configuration now validates required environment variables, feeds them into email setup, and registers all social providers plus development trusted origins so social sign-in works reliably.

Impact

✅ More social login choices on auth landing
✅ Consistent auth flow layout across mobile screens
✅ Grounded environment validation before auth starts

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…endencies, and refine mobile splash screen layout.
…new environment variables and dynamic trusted origins for development.
…ariants and accessibility, and refactor auth pages for consistent layout.
…er, and enhance mobile screen component with keyboard avoiding and system bar handling.
@codeant-ai

codeant-ai Bot commented Nov 25, 2025

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@sourcery-ai

sourcery-ai Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

Reviewer's Guide

Implements social login flow and redesigns the mobile auth screens while wiring backend auth and email configuration to use a typed env module, plus updates shared UI primitives (Button, Text, Screen, Icon) to support the new UX and platform behaviors.

Sequence diagram for the new social login flow

sequenceDiagram
  actor User
  participant MobileAuthIndex as MobileAuthIndexScreen
  participant AuthClient as AuthClient
  participant ApiAuth as ApiAuthService
  participant SocialProvider as SocialProvider

  User->>MobileAuthIndex: Tap social login button
  MobileAuthIndex->>AuthClient: signIn.social(provider, callbackURL)
  AuthClient->>ApiAuth: POST /api/auth/social/{provider}
  ApiAuth->>SocialProvider: Redirect user to provider auth
  SocialProvider-->>User: Show provider login UI
  User-->>SocialProvider: Authenticate and consent
  SocialProvider->>ApiAuth: Redirect to callback with code
  ApiAuth->>ApiAuth: Exchange code for tokens
  ApiAuth->>ApiAuth: Create_or_update_user
  ApiAuth->>AuthClient: Set session cookies
  ApiAuth-->>AuthClient: Response with redirect URL
  AuthClient-->>MobileAuthIndex: Resolve signIn.social
  MobileAuthIndex-->>User: Navigate to /
Loading

Class diagram for env configuration and social auth setup

classDiagram
  class EnvSchema {
    +DATABASE_URL string
    +BETTER_AUTH_SECRET string
    +BETTER_AUTH_URL url
    +GOOGLE_CLIENT_ID string
    +GOOGLE_CLIENT_SECRET string
    +FACEBOOK_CLIENT_ID string?
    +FACEBOOK_CLIENT_SECRET string?
    +GITHUB_CLIENT_ID string?
    +GITHUB_CLIENT_SECRET string?
    +APPLE_CLIENT_ID string?
    +APPLE_CLIENT_SECRET string?
    +LINKEDIN_CLIENT_ID string?
    +LINKEDIN_CLIENT_SECRET string?
    +EMAIL_PROVIDER enum nodemailer resnd plunk smtp
    +RESEND_API_KEY string?
    +PLUNK_API_KEY string?
    +SMTP_HOST string?
    +SMTP_PORT number?
    +SMTP_SECURE boolean?
    +SMTP_USER string?
    +SMTP_PASS string?
    +NODEMAILER_SERVICE string?
    +NODEMAILER_USER string?
    +NODEMAILER_PASS string?
    +EMAIL_FROM email
    +EMAIL_FROM_NAME string
    +AXIOM_DOMAIN url?
    +AXIOM_TOKEN string?
    +DATASET_NAME string?
    +REDIS_URL url
    +NODE_ENV enum development test production
  }

  class EnvModule {
    +env EnvSchema
  }

  class AuthConfig {
    +trustedOrigins string[]
    +database PrismaAdapter
    +socialProviders SocialProviders
    +emailAndPassword EmailAndPasswordConfig
  }

  class SocialProviders {
    +google GoogleConfig
    +facebook FacebookConfig
    +github GithubConfig
    +apple AppleConfig
    +linkedin LinkedinConfig
  }

  class GoogleConfig {
    +prompt string
    +clientId string
    +clientSecret string
  }

  class OAuthProviderConfig {
    +clientId string
    +clientSecret string
  }

  class EmailConfig {
    +provider string
    +from string
    +fromName string
    +resendApiKey string?
    +plunkApiKey string?
    +smtpHost string?
    +smtpPort number
    +smtpSecure boolean
    +smtpUser string?
    +smtpPass string?
    +nodemailerService string?
    +nodemailerUser string?
    +nodemailerPass string?
  }

  EnvModule --> EnvSchema
  AuthConfig --> SocialProviders
  SocialProviders --> GoogleConfig
  SocialProviders --> OAuthProviderConfig
  EnvModule --> AuthConfig
  EnvModule --> EmailConfig
Loading

Class diagram for updated UI primitives and Screen component

classDiagram
  class Screen {
    +ReactNode children
    +string className
    +SystemBarsProps SystemBarsProps
    +KeyboardAvoidingViewProps KeyboardAvoidingViewProps
    +render() void
  }

  class Button {
    +variant default destructive outline secondary ghost link
    +size default sm lg icon
    +onPress() void
    +disabled boolean
    +className string
  }

  class ButtonVariants {
    +variant string
    +size string
  }

  class ButtonTextVariants {
    +variant string
    +size string
  }

  class Text {
    +variant default h1 h2 h3 h4 p blockquote code lead large small muted
    +asChild boolean
    +className string
  }

  class TextVariants {
    +variant string
  }

  class Icon {
    +as LucideIcon
    +className string
    +size number
  }

  Screen --> Button : contains
  Screen --> Text : contains
  Button --> ButtonVariants
  Button --> Text : uses TextClassContext
  Text --> TextVariants
  Icon --> Text : can be used alongside
Loading

File-Level Changes

Change Details Files
Add a new /auth landing screen that offers social login providers and email sign-in, and update navigation to route unauthenticated users through it.
  • Replace /auth index redirect with a new Screen-based welcome UI that calls authClient.signIn.social for Google, Facebook, GitHub, Apple, and LinkedIn and offers a fallback email sign-in button.
  • Show an optional back button by checking router.canGoBack and navigating back when pressed.
  • Update redirects throughout the app (splash, protected layouts, header login) to send unauthenticated users to /auth instead of the sign-in sub-route, and adjust the auth stack layout to include the new index screen and remove unused social-sign-in.
apps/mobile/src/app/(public)/auth/index.tsx
apps/mobile/src/app/index.tsx
apps/mobile/src/app/(public)/(protected)/_layout.tsx
apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
apps/mobile/src/app/(public)/auth/_layout.tsx
apps/mobile/src/containers/core/HeaderUser.tsx
Refactor sign-in, sign-up, forgot-password, and reset-password flows to use a shared Screen layout with improved spacing, back navigation, and consistent copy instead of Card-based UI.
  • Wrap auth forms in the new Screen component with safe-area, keyboard-avoiding behavior, and local ChevronLeft ghost buttons that call router.back when available.
  • Flatten Card-based markup into simple View/Text/Button hierarchies with updated spacing, typography, and alignment while preserving existing behaviors like forgot password links, resend verification, and validation disabling.
  • Adjust sign-up container padding and keep success card and form behaviors while converting their layout from Cards to View-based sections with updated headings and text styles.
apps/mobile/src/app/(public)/auth/sign-in.tsx
apps/mobile/src/app/(public)/auth/reset-password.tsx
apps/mobile/src/app/(public)/auth/forgot-password.tsx
apps/mobile/src/app/(public)/auth/sign-up.tsx
apps/mobile/src/components/auth/sign-up-form.tsx
apps/mobile/src/components/auth/sign-up-success-card.tsx
apps/mobile/src/containers/auth/sign-up.container.tsx
Introduce shared UI primitives (Screen, Icon) and enhance Button and Text primitives to support richer styling, platform-aware behavior, and typography variants.
  • Add Screen component that wraps children in SafeAreaView, SystemBars, and KeyboardAvoidingView with platform-specific behavior, and use it for auth-related screens.
  • Add Icon wrapper around Lucide icons with nativewind cssInterop support, enabling className-based icon styling and consistent default text color.
  • Refactor Button to be flex-row oriented with updated variants and sizes using Platform.select, simplify disabled handling, and adjust button text styling via TextClassContext, while also tweaking the Text component to support semantic variants (h1–h4, p, code, etc.) and platform-specific roles/ARIA attributes.
apps/mobile/src/components/screen.tsx
apps/mobile/src/components/ui/icon.tsx
apps/mobile/src/components/ui/button.tsx
apps/mobile/src/components/ui/text.tsx
Wire backend auth and email configuration to a new validated env module and configure social auth providers and trusted origins for Expo.
  • Introduce apps/api/src/env.ts that validates process.env using zod, logs and exits on invalid configuration, and exports a typed env object.
  • Update auth.ts to consume the env module, comment out the basePath, extend trustedOrigins with Expo dev schemes when NODE_ENV is development, and configure socialProviders (Google, Facebook, GitHub, Apple, LinkedIn) using env client IDs/secrets.
  • Update email config to use env-based values with proper types and defaults, replacing direct process.env usage.
apps/api/src/env.ts
apps/api/src/lib/auth.ts
apps/api/src/lib/email/config.ts
Adjust client auth base URL and minor logic tweaks to support the new auth flow and web compatibility.
  • Ensure mobile authClient points at env.EXPO_PUBLIC_API_URL/api/auth and remove commented alternate baseURL.
  • Change web reset-password token extraction to always return a string (empty string fallback) for URLSearchParams.get.
  • Update splash and protected layout redirects and header login links to align with the new /auth entry point instead of /auth/sign-in.
apps/mobile/src/lib/auth-client.ts
apps/mobile/src/app/(public)/auth/reset-password.tsx
apps/mobile/src/app/index.tsx
apps/mobile/src/app/(public)/(protected)/_layout.tsx
apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
apps/mobile/src/containers/core/HeaderUser.tsx
Upgrade tooling and lockfile dependencies to newer versions compatible with the updated codebase.
  • Bump Typescript and Biome versions in the pnpm workspace catalog and align package.json to use the new TypeScript catalog version.
  • Update ultracite dependency and regenerate pnpm-lock.yaml and other config files accordingly.
pnpm-workspace.yaml
package.json
pnpm-lock.yaml
turbo.json
biome.jsonc
.vscode/settings.json
apps/mobile/components.json
apps/api/.env.example
apps/api/src/index.ts

Possibly linked issues

  • #: PR introduces social auth providers and a new /auth screen, directly implementing the Social Login feature request.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added multi-provider social login support (Google, Facebook, GitHub, Apple, LinkedIn)
    • Redesigned mobile authentication screens with improved navigation flow
    • Enhanced mobile UI with new header and screen layout components
  • Chores

    • Updated code formatter and development tools
    • Improved build configuration and environment setup
    • Enhanced CI/CD pipeline for container deployments

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

Walkthrough

Adds Zod-based environment validation and social-provider env keys, wires env loader into API bootstrap, integrates social providers into auth, refactors mobile auth UIs to Screen/Header layouts (removing Card-based components), introduces new mobile primitives (Screen, Header, Icon), platform-aware UI variants, tooling updates, and several dependency/version bumps.

Changes

Cohort / File(s) Summary
Environment & Bootstrap
apps/api/.env.example, apps/api/src/env.ts, apps/api/src/index.ts
Add social-provider keys to .env.example; new env.ts with Zod schema + cross-field provider validation and exported env/envSchema; import @src/env at API bootstrap.
API Auth & Email
apps/api/src/lib/auth.ts, apps/api/src/lib/email/config.ts
Read credentials from env; add conditional socialProviders entries for google/facebook/github/apple/linkedin; email config now sources all values from env.
Mobile Auth UX & Routes
apps/mobile/src/app/(public)/auth/*, apps/mobile/src/app/(public)/(protected)/*, apps/mobile/src/app/index.tsx, apps/mobile/src/app/(public)/auth/social-sign-in.tsx
Replace Card layouts with Screen/Header; consolidate auth landing to include social sign-in; update redirects from /auth/sign-in → /auth; remove standalone social-sign-in component.
New Mobile Primitives
apps/mobile/src/components/screen.tsx, apps/mobile/src/components/header.tsx, apps/mobile/src/components/ui/icon.tsx
Add Screen (SafeAreaView + SystemBars + KeyboardAvoidingView), Header (back button), and Icon wrapper for Lucide icons.
UI Variant System & Buttons/Text
apps/mobile/src/components/ui/button.tsx, apps/mobile/src/components/ui/text.tsx, apps/mobile/src/components/ui/password.tsx
Introduce platform-aware variants via Platform.select for Button/Text; adjust Button props/ref handling; Text gains typed variants and ARIA mapping; PasswordInput uses Icon wrapper and tighter typing.
Component Refactors (Cards → Views)
apps/mobile/src/components/auth/sign-up-form.tsx, apps/mobile/src/components/auth/sign-up-success-card.tsx, apps/mobile/src/components/*, apps/mobile/src/containers/auth/*
Replace Card components with View/Text-based layouts across auth components and containers; preserve behavior, with minor prop ordering/type tweaks.
Mobile Dev Tools & Theming
apps/mobile/src/components/dev-tools/tools.tsx, apps/mobile/src/components/ThemeToggle.tsx, apps/mobile/src/app/_layout.tsx
Add FloatingDevTools integration and customTools (theme toggle); minor ThemeToggle formatting tweaks; render dev tools in root layout.
Mobile Config & Packages
apps/mobile/components.json, apps/mobile/package.json, apps/mobile/src/lib/auth-client.ts
Add aliases and Tailwind config in components.json; add dependencies (@react-buoy/*, expo-clipboard); remove commented localhost baseURL in auth-client.
Formatting & Tooling Config
.vscode/settings.json, biome.jsonc, package.json, pnpm-workspace.yaml, turbo.json
Switch VSCode default formatter to Biome; bump Biome schema and change ultracite extends; upgrade ultracite and TypeScript versions; add turbo.ui: "tui", make dev task interactive, and add db:reset task.
CI & Docker
.github/workflows/docker-build.yml
Change image tag generation to use short SHA and PR-aware prefix; comment out raw latest tag generation.
Tests & Utilities
apps/api/src/env.test.ts, apps/mobile/src/utils/remap-props.ts
Add env schema tests covering provider completeness rules; add FontAwesome remapProps mapping for className→style and color→color.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MobileApp as Mobile App
    participant AuthClient
    participant API as API Server
    participant Provider as OAuth Provider

    rect rgb(240,250,240)
    Note over MobileApp,API: Social login flow (uses env-backed creds)
    end

    User->>MobileApp: Tap social provider button
    MobileApp->>AuthClient: authClient.signIn.social({ provider, callbackURL })
    AuthClient->>API: POST /auth/social/signin
    API->>Provider: OAuth handshake (uses env.GOOGLE_*/GITHUB_*)
    Provider-->>API: callback (code/token)
    API->>AuthClient: session created / callback response
    AuthClient->>MobileApp: redirect to callbackURL
    MobileApp->>User: Navigate to authenticated screen
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

  • Areas needing extra attention:
    • env.ts: Zod schema, superRefine cross-field validation, and process exit behavior.
    • auth.ts: conditional socialProviders wiring and trustedOrigins expansion.
    • Button/Text variant Platform.select logic for web vs native parity.
    • Routing changes (many redirects from /auth/sign-in → /auth) and removal of social-sign-in component.
    • Screen/KeyboardAvoidingView behavior on iOS vs Android.

Suggested labels

size:XL

Poem

🐰 I hopped through env and boot,
Keys in paw, providers in suit,
Cards unstacked to open view,
Icons bright and variants too,
Biome tidied — hop, review!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Social Login' directly and clearly summarizes the primary change: implementing comprehensive social login authentication with support for multiple providers.
Description check ✅ Passed The description is well-organized and directly related to the changeset, detailing the implementation of social login providers, environment validation, UI refactoring, and tooling updates.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/social-login

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.

@qodo-code-review

qodo-code-review Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
⚪
Overly permissive CORS/trusted origins

Description: The added permissive trusted origins in development allow all exp:// URLs including
wildcards (e.g., 'exp:///' and broad private IP ranges), which increases risk of
token/session leakage during OAuth callbacks if inadvertently enabled or misconfigured
beyond local dev; ensure this is strictly gated by NODE_ENV and not propagated to
production builds.
auth.ts [21-27]

Referred Code
...(env.NODE_ENV === "development" ? [
    "exp://*/*",                 // Trust all Expo development URLs
    "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
    "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
    "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
    "exp://localhost:*/*"        // Trust localhost
] : [])
OAuth redirect misconfiguration

Description: Social provider client IDs and secrets are sourced from environment variables without
explicit per-environment restrictions or redirect URI pinning (commented out), which can
enable OAuth redirect manipulation or misuse if defaults are misconfigured; ensure
redirect URIs are explicitly set and secrets are not empty in production.
auth.ts [32-55]

Referred Code
socialProviders: {
  google: {
    prompt: "select_account", 
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    // redirectURI: `${env.BETTER_AUTH_URL}/api/auth/callback/google`,
  },
  facebook: {
    clientId: env.FACEBOOK_CLIENT_ID || "",
    clientSecret: env.FACEBOOK_CLIENT_SECRET || "",
  },
  github: {
    clientId: env.GITHUB_CLIENT_ID || "",
    clientSecret: env.GITHUB_CLIENT_SECRET || "",
  },
  apple: {
    clientId: env.APPLE_CLIENT_ID || "",
    clientSecret: env.APPLE_CLIENT_SECRET || "",
  },
  linkedin: {
    clientId: env.LINKEDIN_CLIENT_ID || "",


 ... (clipped 3 lines)
Insecure SMTP TLS parsing

Description: The 'SMTP_SECURE' variable is parsed by comparing the string value strictly to "true",
causing any other truthy value (e.g., "1", "TRUE") to be treated as false and potentially
disabling TLS in email transport unexpectedly, weakening transport security.
env.ts [34-37]

Referred Code
SMTP_SECURE: z
  .string()
  .transform((s) => s === "true")
  .optional(),
Missing OAuth state/PKCE validation

Description: The mobile social login handler accepts a provider string from local calls and navigates
to provider auth without additional state/nonce handling in the client, which may reduce
CSRF protections if the underlying library is not enforcing state/PKCE; verify better-auth
client enforces state/PKCE for all providers.
index.tsx [13-20]

Referred Code
const handleSocialLogin = async (
  provider: "google" | "facebook" | "github" | "apple" | "linkedin"
) => {
  await authClient.signIn.social({
    provider,
    callbackURL: "/",
  });
};
Disabled button still clickable

Description: The Pressable Button applies 'opacity-50' when disabled but still forwards all props
including onPress, relying on native disabled behavior which Pressable does not enforce;
without blocking onPress when disabled, actions may still trigger leading to unintended
submissions.
button.tsx [95-103]

Referred Code
function Button({ className, variant, size, ...props }: ButtonProps) {
  return (
    <TextClassContext.Provider value={buttonTextVariants({ variant, size })}>
      <Pressable
        className={cn(props.disabled && 'opacity-50', buttonVariants({ variant, size }), className)}
        role="button"
        {...props}
      />
    </TextClassContext.Provider>
Ticket Compliance
⚪
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
⚪
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

⚪
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing auth logs: Social login and authentication configuration were added without corresponding audit/event
logging of login attempts and outcomes.

Referred Code
socialProviders: {
  google: {
    prompt: "select_account", 
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    // redirectURI: `${env.BETTER_AUTH_URL}/api/auth/callback/google`,
  },
  facebook: {
    clientId: env.FACEBOOK_CLIENT_ID || "",
    clientSecret: env.FACEBOOK_CLIENT_SECRET || "",
  },
  github: {
    clientId: env.GITHUB_CLIENT_ID || "",
    clientSecret: env.GITHUB_CLIENT_SECRET || "",
  },
  apple: {
    clientId: env.APPLE_CLIENT_ID || "",
    clientSecret: env.APPLE_CLIENT_SECRET || "",
  },
  linkedin: {
    clientId: env.LINKEDIN_CLIENT_ID || "",


 ... (clipped 3 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Process exit on parse: Environment validation exits the process on failure without a recoverable path or
structured error handling which may be acceptable for boot but warrants review.

Referred Code
const parsed = envSchema.safeParse(process.env);

if (!parsed.success) {
  logger.error("❌ Invalid environment variables:");
  logger.error(JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2));
  process.exit(1);
}

export const env = parsed.data;

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Env error detail: On invalid environment variables, detailed field errors are logged which could reveal
configuration details; confirm logs are internal-only and safe.

Referred Code
if (!parsed.success) {
  logger.error("❌ Invalid environment variables:");
  logger.error(JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2));
  process.exit(1);

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Secrets in logs: The environment validation logs all field errors as JSON which may include names of secret
keys; ensure values are never logged and logs are secured.

Referred Code
logger.error("❌ Invalid environment variables:");
logger.error(JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2));
process.exit(1);

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Trusted origins broad: Development trusted origins include wide wildcards for exp:// and private IP ranges which
is likely intentional for dev but should be confirmed and gated by NODE_ENV.

Referred Code
...(env.NODE_ENV === "development" ? [
    "exp://*/*",                 // Trust all Expo development URLs
    "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
    "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
    "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
    "exp://localhost:*/*"        // Trust localhost
] : [])

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Nov 25, 2025

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In the auth backend, several social providers are always registered with empty-string client IDs/secrets when env vars are missing (e.g., facebook/github/linkedin in auth.ts); consider only enabling a provider when its credentials are actually present to avoid misconfigured or partially working OAuth flows.
  • In the new /auth social login screen, the FontAwesome icons use hard-coded color="black"; it would be more consistent with theming to derive the color from your design system (e.g., via text- classes or theme tokens) so icons adapt correctly to light/dark modes.
  • The updated trustedOrigins list for Better Auth includes very broad patterns like "exp://*/*"; it would be safer to restrict these to the minimal set of development origins actually needed rather than using wide wildcards.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the auth backend, several social providers are always registered with empty-string client IDs/secrets when env vars are missing (e.g., facebook/github/linkedin in auth.ts); consider only enabling a provider when its credentials are actually present to avoid misconfigured or partially working OAuth flows.
- In the new /auth social login screen, the FontAwesome icons use hard-coded `color="black"`; it would be more consistent with theming to derive the color from your design system (e.g., via `text-` classes or theme tokens) so icons adapt correctly to light/dark modes.
- The updated `trustedOrigins` list for Better Auth includes very broad patterns like `"exp://*/*"`; it would be safer to restrict these to the minimal set of development origins actually needed rather than using wide wildcards.

## Individual Comments

### Comment 1
<location> `apps/mobile/src/components/ui/button.tsx:91-92` </location>
<code_context>
 );

-type ButtonProps = React.ComponentProps<typeof Pressable> & VariantProps<typeof buttonVariants>;
+type ButtonProps = React.ComponentProps<typeof Pressable> &
+  React.RefAttributes<typeof Pressable> &
+  VariantProps<typeof buttonVariants>;

</code_context>

<issue_to_address>
**suggestion (bug_risk):** ButtonProps typing around `ref` is misleading and not actually supported by the component implementation.

Because `Button` is a plain function component (not using `forwardRef`), adding `React.RefAttributes<typeof Pressable>` makes TypeScript accept a `ref` that will be treated as a normal prop and never attached to the underlying `Pressable`. It’s also not the right shape for a `Pressable` instance ref.

To fix this:
- If refs aren’t supported, drop `React.RefAttributes<typeof Pressable>` and use `React.ComponentPropsWithoutRef<typeof Pressable>`.
- If refs should be supported, wrap `Button` in `React.forwardRef<Pressable, ButtonProps>` (or similar) and handle the ref properly.

This keeps the type definition aligned with the actual behavior and avoids confusing consumers relying on refs.

Suggested implementation:

```typescript
type ButtonProps = React.ComponentPropsWithoutRef<typeof Pressable> &
  VariantProps<typeof buttonVariants>;

```

- No further code changes are strictly required if the `Button` component does not currently support refs (i.e., it is not wrapped with `React.forwardRef` and does not handle a `ref` prop).
- If there are any usages of `Button` that currently pass a `ref` expecting it to be attached to the underlying `Pressable`, those call sites should be updated to not rely on refs, or the component should be refactored to use `React.forwardRef` if ref support is desired in the future.
</issue_to_address>

### Comment 2
<location> `apps/api/src/lib/auth.ts:21-26` </location>
<code_context>
     "http://localhost:8081",
+
+    // Development mode - Expo's exp:// scheme with local IP ranges
+    ...(env.NODE_ENV === "development" ? [
+        "exp://*/*",                 // Trust all Expo development URLs
+        "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
+        "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
+        "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
+        "exp://localhost:*/*"        // Trust localhost
+    ] : [])
   ],
</code_context>

<issue_to_address>
**🚨 question (security):** The wildcard-heavy trustedOrigins entries may not be interpreted as intended and could broaden trust too much.

These dev `trustedOrigins` entries use very broad patterns (e.g., `"exp://*/*"`, `"exp://10.0.0.*:*/*"`). Depending on how `betterAuth` parses them, they may either be unsupported and silently ignored or match more origins than intended, which can hide misconfigurations and make origin issues harder to diagnose.

Please:
- Verify whether `betterAuth` supports this wildcard syntax.
- If not, replace with explicit Expo dev origins (e.g., the concrete `exp://` URLs you use).
- If it does, tighten the patterns to only the specific hosts/ports you actually need in development.
</issue_to_address>

### Comment 3
<location> `apps/mobile/src/app/(public)/auth/reset-password.tsx:105` </location>
<code_context>
-            <View className="flex-row justify-center items-center">
-              <Text className="text-sm text-muted-foreground">
-                Remember your password?{" "}
+    <View className="flex-1 justify-center items-center p-4">
+      {router.canGoBack() && (
+        <Button
</code_context>

<issue_to_address>
**suggestion:** Reset password screen is not using the shared Screen wrapper, which may cause layout/status bar inconsistencies.

Other auth screens were updated to use the shared `Screen` component for safe area handling, system bar configuration, and keyboard avoidance. This screen still uses a bare `View` as the root, which can lead to status bar overlap, inconsistent keyboard behavior on password fields, and visual differences from the rest of the auth flow. Please wrap this screen in `Screen` as well for consistent behavior and layout.

Suggested implementation:

```typescript
import { View, Text } from "react-native";
import { Screen } from "@/components/Screen";

```

```typescript
export default function ResetPassword() {

```

```typescript
  return (
    <Screen>
      <View className="flex-1 justify-center items-center p-4">
        {router.canGoBack() && (
          <Button

```

```typescript
        {router.canGoBack() && (
          <Button

```

```typescript
      )}
      </View>
    </Screen>
  );

```

- The exact import path and named export for `Screen` (`@/components/Screen`, `@/components/ui/screen`, default vs named export, etc.) should match how it is done in the other auth screens (e.g. login, signup). Adjust the import line accordingly.
- The `Screen` component may expect specific props for consistent behavior across auth screens (e.g. `preset`, `safeAreaEdges`, `statusBarStyle`, `keyboardOffset`). Please copy the same `Screen` usage (component name and props) from the other updated auth screens to ensure consistent safe area handling, system bar configuration, and keyboard avoidance behavior.
- If the other auth screens use `Screen` as the outermost wrapper without an extra `View` for layout, you can move the `className` from the `View` directly onto `Screen` (or use the equivalent props) and remove the nested `View` to keep the structure consistent.
</issue_to_address>

### Comment 4
<location> `apps/mobile/src/app/(public)/auth/index.tsx:13-19` </location>
<code_context>
-	return <Redirect href="/(public)/auth/sign-in" />;
+  const router = useRouter();
+
+  const handleSocialLogin = async (
+    provider: "google" | "facebook" | "github" | "apple" | "linkedin"
+  ) => {
+    await authClient.signIn.social({
+      provider,
+      callbackURL: "/",
+    });
+  };
+
</code_context>

<issue_to_address>
**suggestion:** Social login handler lacks error handling, so failures will be silent for users.

Right now `handleSocialLogin` just awaits `authClient.signIn.social` and ignores failures, so users get no indication if something goes wrong (e.g., network issues, provider misconfig, popup blocked). Please wrap this call in a try/catch, show a user-visible error (e.g., toast/snackbar), and optionally log/track the error to aid debugging when provider configuration is incorrect.

Suggested implementation:

```typescript
import { Button } from "~/components/ui/button";
import { Text } from "~/components/ui/text";
import { useToast } from "~/components/ui/use-toast";

```

```typescript
export default function AuthIndex() {
  const router = useRouter();
  const { toast } = useToast();

```

```typescript
  const handleSocialLogin = async (
    provider: "google" | "facebook" | "github" | "apple" | "linkedin"
  ) => {
    try {
      await authClient.signIn.social({
        provider,
        callbackURL: "/",
      });
    } catch (error) {
      // Log for debugging / error tracking
      console.error("Social login failed", error);

      // User-visible feedback
      toast?.({
        variant: "destructive",
        title: "Unable to sign in",
        description:
          "Something went wrong while connecting to the provider. Please try again.",
      });
    }
  };

```

To fully integrate this change, you will likely need to:
1) Ensure `authClient` is imported in this file from wherever it is defined (for example, a module like `"~/lib/auth/client"`), if it is not already imported elsewhere in the file.
2) Confirm the correct import path and API for the toast hook in your codebase. If your project uses a different hook (e.g., `useToast` from `"~/components/ui/toast"` or similar) or a different options shape, adjust the `import { useToast } from "~/components/ui/use-toast";` line and the `toast?.({ ... })` call accordingly.
3) If your UI library does not support the `variant` prop or uses different props for destructive/error toasts, align the toast options with your existing toast/snackbar conventions.
</issue_to_address>

### Comment 5
<location> `apps/mobile/src/app/(public)/auth/index.tsx:25-34` </location>
<code_context>
-            />
-          </CardContent>
-          <CardFooter>
-            <Button
-              onPress={handleResetPassword}
-              className="flex-1 flex-row items-center gap-4"
</code_context>

<issue_to_address>
**suggestion:** Hardcoded black icon color can clash with theming (especially dark mode).

All social buttons render `FontAwesome` icons with `color="black"`, which can become nearly invisible on dark backgrounds and inconsistent with the rest of the theme-aware UI.

Please update these icons to use theme-aware colors (e.g., your design system’s foreground/text color) or the `Icon` component / NativeWind classes so they inherit the surrounding text/foreground color and remain legible in both light and dark mode.
</issue_to_address>

### Comment 6
<location> `apps/api/src/lib/email/config.ts:11-35` </location>
<code_context>
	const config = {
		provider: env.EMAIL_PROVIDER as EmailConfig["provider"],
		from: env.EMAIL_FROM,
		fromName: env.EMAIL_FROM_NAME,

		// Resend
		resendApiKey: env.RESEND_API_KEY,

		// Plunk
		plunkApiKey: env.PLUNK_API_KEY,

		// SMTP
		smtpHost: env.SMTP_HOST,
		smtpPort: env.SMTP_PORT ?? 587,
		smtpSecure: env.SMTP_SECURE ?? false,
		smtpUser: env.SMTP_USER,
		smtpPass: env.SMTP_PASS,

		// Nodemailer
		nodemailerService: env.NODEMAILER_SERVICE,
		nodemailerUser: env.NODEMAILER_USER,
		nodemailerPass: env.NODEMAILER_PASS,
	};

	return config;

</code_context>

<issue_to_address>
**suggestion (code-quality):** Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/TypeScript/Default-Rules/inline-immediately-returned-variable))

```suggestion
	return {
 		provider: env.EMAIL_PROVIDER as EmailConfig["provider"],
 		from: env.EMAIL_FROM,
 		fromName: env.EMAIL_FROM_NAME,

 		// Resend
 		resendApiKey: env.RESEND_API_KEY,

 		// Plunk
 		plunkApiKey: env.PLUNK_API_KEY,

 		// SMTP
 		smtpHost: env.SMTP_HOST,
 		smtpPort: env.SMTP_PORT ?? 587,
 		smtpSecure: env.SMTP_SECURE ?? false,
 		smtpUser: env.SMTP_USER,
 		smtpPass: env.SMTP_PASS,

 		// Nodemailer
 		nodemailerService: env.NODEMAILER_SERVICE,
 		nodemailerUser: env.NODEMAILER_USER,
 		nodemailerPass: env.NODEMAILER_PASS,
 	};

```

<br/><details><summary>Explanation</summary>Something that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.
</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +91 to +92
type ButtonProps = React.ComponentProps<typeof Pressable> &
React.RefAttributes<typeof Pressable> &

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): ButtonProps typing around ref is misleading and not actually supported by the component implementation.

Because Button is a plain function component (not using forwardRef), adding React.RefAttributes<typeof Pressable> makes TypeScript accept a ref that will be treated as a normal prop and never attached to the underlying Pressable. It’s also not the right shape for a Pressable instance ref.

To fix this:

  • If refs aren’t supported, drop React.RefAttributes<typeof Pressable> and use React.ComponentPropsWithoutRef<typeof Pressable>.
  • If refs should be supported, wrap Button in React.forwardRef<Pressable, ButtonProps> (or similar) and handle the ref properly.

This keeps the type definition aligned with the actual behavior and avoids confusing consumers relying on refs.

Suggested implementation:

type ButtonProps = React.ComponentPropsWithoutRef<typeof Pressable> &
  VariantProps<typeof buttonVariants>;
  • No further code changes are strictly required if the Button component does not currently support refs (i.e., it is not wrapped with React.forwardRef and does not handle a ref prop).
  • If there are any usages of Button that currently pass a ref expecting it to be attached to the underlying Pressable, those call sites should be updated to not rely on refs, or the component should be refactored to use React.forwardRef if ref support is desired in the future.

Comment thread apps/api/src/lib/auth.ts Outdated
Comment on lines +21 to +26
...(env.NODE_ENV === "development" ? [
"exp://*/*", // Trust all Expo development URLs
"exp://10.0.0.*:*/*", // Trust 10.0.0.x IP range
"exp://192.168.*.*:*/*", // Trust 192.168.x.x IP range
"exp://172.*.*.*:*/*", // Trust 172.x.x.x IP range
"exp://localhost:*/*" // Trust localhost

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 question (security): The wildcard-heavy trustedOrigins entries may not be interpreted as intended and could broaden trust too much.

These dev trustedOrigins entries use very broad patterns (e.g., "exp://*/*", "exp://10.0.0.*:*/*"). Depending on how betterAuth parses them, they may either be unsupported and silently ignored or match more origins than intended, which can hide misconfigurations and make origin issues harder to diagnose.

Please:

  • Verify whether betterAuth supports this wildcard syntax.
  • If not, replace with explicit Expo dev origins (e.g., the concrete exp:// URLs you use).
  • If it does, tighten the patterns to only the specific hosts/ports you actually need in development.

Comment thread apps/mobile/src/app/(public)/auth/reset-password.tsx Outdated
Comment thread apps/mobile/src/app/(public)/auth/index.tsx Outdated
Comment on lines 11 to 35
const config = {
provider: process.env.EMAIL_PROVIDER as EmailConfig["provider"],
from: process.env.EMAIL_FROM || "onboarding@resend.dev",
fromName: process.env.EMAIL_FROM_NAME || "Universal Expo App",
provider: env.EMAIL_PROVIDER as EmailConfig["provider"],
from: env.EMAIL_FROM,
fromName: env.EMAIL_FROM_NAME,

// Resend
resendApiKey: process.env.RESEND_API_KEY,
resendApiKey: env.RESEND_API_KEY,

// Plunk
plunkApiKey: process.env.PLUNK_API_KEY,
plunkApiKey: env.PLUNK_API_KEY,

// SMTP
smtpHost: process.env.SMTP_HOST,
smtpPort: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587,
smtpSecure: process.env.SMTP_SECURE === "true",
smtpUser: process.env.SMTP_USER,
smtpPass: process.env.SMTP_PASS,
smtpHost: env.SMTP_HOST,
smtpPort: env.SMTP_PORT ?? 587,
smtpSecure: env.SMTP_SECURE ?? false,
smtpUser: env.SMTP_USER,
smtpPass: env.SMTP_PASS,

// Nodemailer
nodemailerService: process.env.NODEMAILER_SERVICE,
nodemailerUser: process.env.NODEMAILER_USER,
nodemailerPass: process.env.NODEMAILER_PASS,
nodemailerService: env.NODEMAILER_SERVICE,
nodemailerUser: env.NODEMAILER_USER,
nodemailerPass: env.NODEMAILER_PASS,
};

return config;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const config = {
provider: process.env.EMAIL_PROVIDER as EmailConfig["provider"],
from: process.env.EMAIL_FROM || "onboarding@resend.dev",
fromName: process.env.EMAIL_FROM_NAME || "Universal Expo App",
provider: env.EMAIL_PROVIDER as EmailConfig["provider"],
from: env.EMAIL_FROM,
fromName: env.EMAIL_FROM_NAME,
// Resend
resendApiKey: process.env.RESEND_API_KEY,
resendApiKey: env.RESEND_API_KEY,
// Plunk
plunkApiKey: process.env.PLUNK_API_KEY,
plunkApiKey: env.PLUNK_API_KEY,
// SMTP
smtpHost: process.env.SMTP_HOST,
smtpPort: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587,
smtpSecure: process.env.SMTP_SECURE === "true",
smtpUser: process.env.SMTP_USER,
smtpPass: process.env.SMTP_PASS,
smtpHost: env.SMTP_HOST,
smtpPort: env.SMTP_PORT ?? 587,
smtpSecure: env.SMTP_SECURE ?? false,
smtpUser: env.SMTP_USER,
smtpPass: env.SMTP_PASS,
// Nodemailer
nodemailerService: process.env.NODEMAILER_SERVICE,
nodemailerUser: process.env.NODEMAILER_USER,
nodemailerPass: process.env.NODEMAILER_PASS,
nodemailerService: env.NODEMAILER_SERVICE,
nodemailerUser: env.NODEMAILER_USER,
nodemailerPass: env.NODEMAILER_PASS,
};
return config;
return {
provider: env.EMAIL_PROVIDER as EmailConfig["provider"],
from: env.EMAIL_FROM,
fromName: env.EMAIL_FROM_NAME,
// Resend
resendApiKey: env.RESEND_API_KEY,
// Plunk
plunkApiKey: env.PLUNK_API_KEY,
// SMTP
smtpHost: env.SMTP_HOST,
smtpPort: env.SMTP_PORT ?? 587,
smtpSecure: env.SMTP_SECURE ?? false,
smtpUser: env.SMTP_USER,
smtpPass: env.SMTP_PASS,
// Nodemailer
nodemailerService: env.NODEMAILER_SERVICE,
nodemailerUser: env.NODEMAILER_USER,
nodemailerPass: env.NODEMAILER_PASS,
};


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.

@qodo-code-review

qodo-code-review Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Remove testing framework dependencies

The PR removes testing-related dependencies like vitest, esbuild, and rollup
from pnpm-lock.yaml without any explanation in the description. This dismantles
the testing infrastructure and should be reverted.

Examples:

pnpm-lock.yaml [3288-3323]
    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
    engines: {node: '>=8'}

  boolbase@1.0.0:
    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}

  bplist-creator@0.1.0:
    resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==}

  bplist-parser@0.3.1:

 ... (clipped 26 lines)

Solution Walkthrough:

Before:

# pnpm-lock.yaml (conceptual)

packages:
  # ...
  # '@vitest/...' packages are missing
  # 'chai' is missing
  # 'esbuild' is missing
  # 'rollup' is missing
  # ...

snapshots:
  # ...
  # Snapshots for testing packages are missing

After:

# pnpm-lock.yaml (conceptual)

packages:
  # ...
  '@vitest/expect@3.2.4':
    ...
  'chai@5.3.3':
    ...
  'esbuild@0.25.12':
    ...
  'rollup@4.53.3':
    ...
  # Other testing packages are restored

snapshots:
  # ...
  # Snapshots for testing packages are restored
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies the critical and undocumented removal of the entire testing framework, which is a major regression for the project's quality assurance.

High
Possible issue
✅ Conditionally configure social login providers
Suggestion Impact:The commit changed socialProviders construction to conditionally spread in each provider (including Google) only if both credentials exist, removing empty-string fallbacks.

code diff:

   socialProviders: {
-    google: {
-      prompt: "select_account", 
-      clientId: env.GOOGLE_CLIENT_ID,
-      clientSecret: env.GOOGLE_CLIENT_SECRET,
-      // redirectURI: `${env.BETTER_AUTH_URL}/api/auth/callback/google`,
-    },
-    facebook: {
-      clientId: env.FACEBOOK_CLIENT_ID || "",
-      clientSecret: env.FACEBOOK_CLIENT_SECRET || "",
-    },
-    github: {
-      clientId: env.GITHUB_CLIENT_ID || "",
-      clientSecret: env.GITHUB_CLIENT_SECRET || "",
-    },
-    apple: {
-      clientId: env.APPLE_CLIENT_ID || "",
-      clientSecret: env.APPLE_CLIENT_SECRET || "",
-    },
-    linkedin: {
-      clientId: env.LINKEDIN_CLIENT_ID || "",
-      clientSecret: env.LINKEDIN_CLIENT_SECRET || "",
-    },
+    ...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
+      ? {
+          google: {
+            prompt: "select_account",
+            clientId: env.GOOGLE_CLIENT_ID,
+            clientSecret: env.GOOGLE_CLIENT_SECRET,
+          },
+        }
+      : {}),
+    ...(env.FACEBOOK_CLIENT_ID && env.FACEBOOK_CLIENT_SECRET
+      ? {
+          facebook: {
+            clientId: env.FACEBOOK_CLIENT_ID,
+            clientSecret: env.FACEBOOK_CLIENT_SECRET,
+          },
+        }
+      : {}),
+    ...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET
+      ? {
+          github: {
+            clientId: env.GITHUB_CLIENT_ID,
+            clientSecret: env.GITHUB_CLIENT_SECRET,
+          },
+        }
+      : {}),
+    ...(env.APPLE_CLIENT_ID && env.APPLE_CLIENT_SECRET
+      ? {
+          apple: {
+            clientId: env.APPLE_CLIENT_ID,
+            clientSecret: env.APPLE_CLIENT_SECRET,
+          },
+        }
+      : {}),
+    ...(env.LINKEDIN_CLIENT_ID && env.LINKEDIN_CLIENT_SECRET
+      ? {
+          linkedin: {
+            clientId: env.LINKEDIN_CLIENT_ID,
+            clientSecret: env.LINKEDIN_CLIENT_SECRET,
+          },
+        }
+      : {}),
   },

Conditionally construct the socialProviders object to only include a provider if
both its client ID and client secret are available, avoiding misconfiguration
with empty credentials.

apps/api/src/lib/auth.ts [32-55]

 socialProviders: {
   google: {
-    prompt: "select_account", 
+    prompt: "select_account",
     clientId: env.GOOGLE_CLIENT_ID,
     clientSecret: env.GOOGLE_CLIENT_SECRET,
     // redirectURI: `${env.BETTER_AUTH_URL}/api/auth/callback/google`,
   },
-  facebook: {
-    clientId: env.FACEBOOK_CLIENT_ID || "",
-    clientSecret: env.FACEBOOK_CLIENT_SECRET || "",
-  },
-  github: {
-    clientId: env.GITHUB_CLIENT_ID || "",
-    clientSecret: env.GITHUB_CLIENT_SECRET || "",
-  },
-  apple: {
-    clientId: env.APPLE_CLIENT_ID || "",
-    clientSecret: env.APPLE_CLIENT_SECRET || "",
-  },
-  linkedin: {
-    clientId: env.LINKEDIN_CLIENT_ID || "",
-    clientSecret: env.LINKEDIN_CLIENT_SECRET || "",
-  },
+  ...(env.FACEBOOK_CLIENT_ID && env.FACEBOOK_CLIENT_SECRET && {
+    facebook: {
+      clientId: env.FACEBOOK_CLIENT_ID,
+      clientSecret: env.FACEBOOK_CLIENT_SECRET,
+    },
+  }),
+  ...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET && {
+    github: {
+      clientId: env.GITHUB_CLIENT_ID,
+      clientSecret: env.GITHUB_CLIENT_SECRET,
+    },
+  }),
+  ...(env.APPLE_CLIENT_ID && env.APPLE_CLIENT_SECRET && {
+    apple: {
+      clientId: env.APPLE_CLIENT_ID,
+      clientSecret: env.APPLE_CLIENT_SECRET,
+    },
+  }),
+  ...(env.LINKEDIN_CLIENT_ID && env.LINKEDIN_CLIENT_SECRET && {
+    linkedin: {
+      clientId: env.LINKEDIN_CLIENT_ID,
+      clientSecret: env.LINKEDIN_CLIENT_SECRET,
+    },
+  }),
 },

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that falling back to empty strings for social provider credentials is risky and proposes to conditionally include providers only when all required credentials are present, preventing potential runtime errors.

Medium
✅ Add error handling for social login
Suggestion Impact:The commit added error handling to handleSocialLogin by checking the sign-in result for errors and showing a toast, plus redirecting on success. While not a try/catch, it implements the intention of handling potential errors.

code diff:

@@ -13,87 +14,96 @@
   const handleSocialLogin = async (
     provider: "google" | "facebook" | "github" | "apple" | "linkedin"
   ) => {
-    await authClient.signIn.social({
+    const result = await authClient.signIn.social({
       provider,
       callbackURL: "/",
     });
+    if (result.error) {
+      toast.error("Failed to login", {
+        description: result.error.message,
+      });
+      return;
+    }
+    router.replace("/home");
   };

Add a try...catch block to the handleSocialLogin function to handle potential
errors during the social sign-in process.

apps/mobile/src/app/(public)/auth/index.tsx [13-20]

 const handleSocialLogin = async (
   provider: "google" | "facebook" | "github" | "apple" | "linkedin"
 ) => {
-  await authClient.signIn.social({
-    provider,
-    callbackURL: "/",
-  });
+  try {
+    await authClient.signIn.social({
+      provider,
+      callbackURL: "/",
+    });
+  } catch (error) {
+    console.error(`Social login with ${provider} failed:`, error);
+    // Optionally, show a toast or alert to the user
+  }
 };

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the handleSocialLogin function lacks error handling for an async call, which could lead to unhandled promise rejections and application instability.

Medium
✅ Enforce all-or-nothing environment variables
Suggestion Impact:The commit added a schema-level validation (.superRefine) that checks each social provider and raises an issue if only one of client ID or secret is provided, implementing the all-or-nothing rule (including Google made optional). Although it used superRefine instead of multiple refine calls, it fulfills the suggestion’s intent.

code diff:

+    NODE_ENV: z
+      .enum(["development", "test", "production"])
+      .default("development"),
+  })
+  .superRefine((data, ctx) => {
+    const providers = [
+      {
+        name: "Google",
+        id: data.GOOGLE_CLIENT_ID,
+        secret: data.GOOGLE_CLIENT_SECRET,
+        idKey: "GOOGLE_CLIENT_ID",
+        secretKey: "GOOGLE_CLIENT_SECRET",
+      },
+      {
+        name: "Facebook",
+        id: data.FACEBOOK_CLIENT_ID,
+        secret: data.FACEBOOK_CLIENT_SECRET,
+        idKey: "FACEBOOK_CLIENT_ID",
+        secretKey: "FACEBOOK_CLIENT_SECRET",
+      },
+      {
+        name: "GitHub",
+        id: data.GITHUB_CLIENT_ID,
+        secret: data.GITHUB_CLIENT_SECRET,
+        idKey: "GITHUB_CLIENT_ID",
+        secretKey: "GITHUB_CLIENT_SECRET",
+      },
+      {
+        name: "Apple",
+        id: data.APPLE_CLIENT_ID,
+        secret: data.APPLE_CLIENT_SECRET,
+        idKey: "APPLE_CLIENT_ID",
+        secretKey: "APPLE_CLIENT_SECRET",
+      },
+      {
+        name: "LinkedIn",
+        id: data.LINKEDIN_CLIENT_ID,
+        secret: data.LINKEDIN_CLIENT_SECRET,
+        idKey: "LINKEDIN_CLIENT_ID",
+        secretKey: "LINKEDIN_CLIENT_SECRET",
+      },
+    ];
+
+    for (const provider of providers) {
+      if (
+        (provider.id && !provider.secret) ||
+        (!provider.id && provider.secret)
+      ) {
+        ctx.addIssue({
+          code: "custom",
+          message: `${provider.name} requires both Client ID and Client Secret to be set, or neither.`,
+          path: [provider.id ? provider.secretKey : provider.idKey],
+        });
+      }
+    }
+  });

Use Zod's refine method on envSchema to ensure that for each optional social
provider, both the client ID and secret are provided together, or not at all.

apps/api/src/env.ts [4-60]

 const envSchema = z.object({
   // Database
   DATABASE_URL: z.string().min(1),
 
   // Auth
   BETTER_AUTH_SECRET: z.string().min(1),
   BETTER_AUTH_URL: z.url(),
   GOOGLE_CLIENT_ID: z.string().min(1),
   GOOGLE_CLIENT_SECRET: z.string().min(1),
   FACEBOOK_CLIENT_ID: z.string().min(1).optional(),
   FACEBOOK_CLIENT_SECRET: z.string().min(1).optional(),
   GITHUB_CLIENT_ID: z.string().min(1).optional(),
   GITHUB_CLIENT_SECRET: z.string().min(1).optional(),
   APPLE_CLIENT_ID: z.string().min(1).optional(),
   APPLE_CLIENT_SECRET: z.string().min(1).optional(),
   LINKEDIN_CLIENT_ID: z.string().min(1).optional(),
   LINKEDIN_CLIENT_SECRET: z.string().min(1).optional(),
 ...
-});
+}).refine(
+    (data) => (data.FACEBOOK_CLIENT_ID && data.FACEBOOK_CLIENT_SECRET) || (!data.FACEBOOK_CLIENT_ID && !data.FACEBOOK_CLIENT_SECRET),
+    { message: "Both FACEBOOK_CLIENT_ID and FACEBOOK_CLIENT_SECRET must be provided or neither.", path: ["FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET"] }
+).refine(
+    (data) => (data.GITHUB_CLIENT_ID && data.GITHUB_CLIENT_SECRET) || (!data.GITHUB_CLIENT_ID && !data.GITHUB_CLIENT_SECRET),
+    { message: "Both GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET must be provided or neither.", path: ["GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET"] }
+).refine(
+    (data) => (data.APPLE_CLIENT_ID && data.APPLE_CLIENT_SECRET) || (!data.APPLE_CLIENT_ID && !data.APPLE_CLIENT_SECRET),
+    { message: "Both APPLE_CLIENT_ID and APPLE_CLIENT_SECRET must be provided or neither.", path: ["APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET"] }
+).refine(
+    (data) => (data.LINKEDIN_CLIENT_ID && data.LINKEDIN_CLIENT_SECRET) || (!data.LINKEDIN_CLIENT_ID && !data.LINKEDIN_CLIENT_SECRET),
+    { message: "Both LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET must be provided or neither.", path: ["LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET"] }
+);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential misconfiguration where social provider credentials can be partially set and proposes using refine to enforce that either both or neither are provided, improving configuration robustness and preventing runtime errors.

Medium
Security
Remove overly permissive trusted origin

Remove the overly permissive exp:/// trusted origin from the development
configuration to enhance security.

apps/api/src/lib/auth.ts [20-27]

 // Development mode - Expo's exp:// scheme with local IP ranges
 ...(env.NODE_ENV === "development" ? [
-    "exp://*/*",                 // Trust all Expo development URLs
     "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
     "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
     "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
     "exp://localhost:*/*"        // Trust localhost
 ] : [])

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies an overly permissive trusted origin exp://*/* and recommends removing it to improve security by adhering to the principle of least privilege, even in a development environment.

Low
General
✅ Fix absolute positioning of back button
Suggestion Impact:The commit removed the entire back button block (including the redundant View wrapper) and replaced it with a Header component, effectively addressing the redundancy though not by the exact suggested change.

code diff:

-      <View className="flex-row">
-        {router.canGoBack() && (
-          <Button
-            variant="ghost"
-            className="absolute top-0 left-0 z-10"
-            onPress={() => router.back()}
-          >
-            <ChevronLeft size={24} className="text-foreground" />
-          </Button>
-        )}
-      </View>
+      <Header />

Remove the redundant View wrapper around the absolutely positioned back button
to simplify the component structure.

apps/mobile/src/app/(public)/auth/sign-up.tsx [12-22]

-<View className="flex-row">
-  {router.canGoBack() && (
-    <Button
-      variant="ghost"
-      className="absolute top-0 left-0 z-10"
-      onPress={() => router.back()}
-    >
-      <ChevronLeft size={24} className="text-foreground" />
-    </Button>
-  )}
-</View>
+{router.canGoBack() && (
+  <Button
+    variant="ghost"
+    className="absolute top-4 left-4 z-10"
+    onPress={() => router.back()}
+  >
+    <ChevronLeft size={24} className="text-foreground" />
+  </Button>
+)}

[Suggestion processed]

Suggestion importance[1-10]: 3

__

Why: The suggestion correctly identifies a redundant View wrapper around an absolutely positioned button, but the proposed fix is a minor code cleanup with low impact on functionality or layout.

Low
  • Update

<Stack.Screen name="sign-in" />
<Stack.Screen name="sign-up" />
<Stack.Screen name="forgot-password" />
</Stack>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The layout now only registers the four listed screens with the stack, so the reset-password.tsx page never gets a Stack.Screen entry and navigating to the password reset page will throw because that route is not defined anymore. [logic error]

Severity Level: Minor ⚠️

Suggested change
</Stack>
<Stack.Screen name="reset-password" />
Why it matters? ⭐

There is a reset-password.tsx screen under this layout, so the stack should register that route; without the entry the screen never gets a defined path and navigation from the forgot-password flow will fail. Adding the missing <Stack.Screen name="reset-password" /> fixes a real navigation bug introduced by removing the route.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/mobile/src/app/(public)/auth/_layout.tsx
**Line:** 21:21
**Comment:**
	*Logic Error: The layout now only registers the four listed screens with the stack, so the `reset-password.tsx` page never gets a `Stack.Screen` entry and navigating to the password reset page will throw because that route is not defined anymore.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

</View>

<Button
onPress={() => router.push("/(public)/auth/sign-in")}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Pushing to "/(public)/auth/sign-in" tries to navigate to a literal path that includes the route group segment, but expo-router omits (public) from the actual URL, so this navigation target never exists and the screen will not load. [logic error]

Severity Level: Minor ⚠️

Suggested change
onPress={() => router.push("/(public)/auth/sign-in")}
onPress={() => router.push("/auth/sign-in")}
Why it matters? ⭐

In expo-router the (public) route group is never part of the actual URL, so pushing to "/(public)/auth/sign-in" will never resolve to a screen. Changing it to "/auth/sign-in" points at the real route and makes the button work.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/mobile/src/app/(public)/auth/index.tsx
**Line:** 89:89
**Comment:**
	*Logic Error: Pushing to `"/(public)/auth/sign-in"` tries to navigate to a literal path that includes the route group segment, but expo-router omits `(public)` from the actual URL, so this navigation target never exists and the screen will not load.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

// For web, also check window location search params
if (!resetToken && typeof window !== "undefined") {
resetToken = new URLSearchParams(window.location.search).get("token");
resetToken = new URLSearchParams(window.location.search).get("token") || "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Guard against missing window.location before reading search: on React Native window exists but window.location is undefined, so calling window.location.search when the URL token is absent will crash the app instead of showing the reset form. [null pointer]

Severity Level: Minor ⚠️

Suggested change
resetToken = new URLSearchParams(window.location.search).get("token") || "";
const searchParams = window.location?.search;
if (searchParams) {
resetToken = new URLSearchParams(searchParams).get("token") || "";
}
Why it matters? ⭐

On native runtimes window exists but window.location is undefined, so the current code would throw whenever the token isn't already in params. The guard prevents a runtime crash before showing the reset form, so it fixes a real platform bug.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/mobile/src/app/(public)/auth/reset-password.tsx
**Line:** 27:27
**Comment:**
	*Null Pointer: Guard against missing `window.location` before reading `search`: on React Native `window` exists but `window.location` is undefined, so calling `window.location.search` when the URL token is absent will crash the app instead of showing the reset form.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

size="sm"
onPress={() => {
push("/auth/sign-in", { withAnchor: true });
push("/auth", { withAnchor: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The desktop login button now navigates to /auth, but the actual login screen lives at /auth/sign-in (see the mobile flow), so tapping this button will push to a route that has no page and results in a runtime navigation error/blank screen; it should navigate to /auth/sign-in instead. [logic error]

Severity Level: Minor ⚠️

Suggested change
push("/auth", { withAnchor: true });
push("/auth/sign-in", { withAnchor: true });
Why it matters? ⭐

The mobile flow already pushes to /auth/sign-in, and there is no evidence that /auth is a valid screen, so keeping the desktop button on /auth almost certainly produces a blank/errored navigation. Aligning the desktop handler with the actual login route fixes a real runtime navigation bug introduced in this PR.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/mobile/src/containers/core/HeaderUser.tsx
**Line:** 135:135
**Comment:**
	*Logic Error: The desktop login button now navigates to `/auth`, but the actual login screen lives at `/auth/sign-in` (see the mobile flow), so tapping this button will push to a route that has no page and results in a runtime navigation error/blank screen; it should navigate to `/auth/sign-in` instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment thread apps/api/src/env.ts Outdated

if (!parsed.success) {
logger.error("❌ Invalid environment variables:");
logger.error(JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: z.flattenError is not a function exported by Zod, so whenever the schema validation fails this line throws TypeError: z.flattenError is not a function before your logging or process.exit(1) can run; call parsed.error.flatten() instead so you actually log the validation errors and exit cleanly. [logic error]

Severity Level: Minor ⚠️

Suggested change
logger.error(JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2));
logger.error(JSON.stringify(parsed.error.flatten().fieldErrors, null, 2));
Why it matters? ⭐

z.flattenError is not part of Zod's API, so this line throws TypeError: z.flattenError is not a function the moment validation fails and nothing is logged before the process exits. Using parsed.error.flatten() is the documented way to access the field errors from a ZodError, so the suggested change fixes a real runtime crash during env validation.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/api/src/env.ts
**Line:** 66:66
**Comment:**
	*Logic Error: `z.flattenError` is not a function exported by Zod, so whenever the schema validation fails this line throws `TypeError: z.flattenError is not a function` before your logging or `process.exit(1)` can run; call `parsed.error.flatten()` instead so you actually log the validation errors and exit cleanly.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai

codeant-ai Bot commented Nov 25, 2025

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
apps/mobile/src/containers/core/HeaderUser.tsx (1)

143-143: Replace HTML <div> with React Native <View> primitive.

Line 143 uses an HTML <div> element instead of the React Native <View> primitive, which violates the coding guidelines and will break on native iOS/Android platforms.

Apply this diff to fix the issue:

-				<div className="flex md:hidden">
+				<View className="flex md:hidden">

Also update the closing tag on line 162:

-				</div>
+				</View>

As per coding guidelines, React Native primitives must be used instead of HTML elements.

apps/mobile/src/app/index.tsx (1)

5-27: Perfect! I now have all the necessary information to verify the review comment. Let me analyze the findings:

Key Verification Results:

  1. Session State Check Issue (CONFIRMED):

    • Line 9: const session = authClient.useSession(); returns an object
    • Line 19: if (!session) will never be true because session is always an object (even when loading)
    • The AdminLayout at line 6 shows the correct pattern: const { isPending, data } = authClient.useSession();
    • AdminLayout then properly checks if (isPending) (line 7), not the session object itself
  2. Redirect Inconsistency (CONFIRMED):

    • index.tsx line 36: return <Redirect href="/(public)/auth" />;
    • AdminLayout line 13: return <Redirect href="/auth" />;
    • These are indeed different redirect targets
  3. The review's suggested fix is valid - the pattern should follow AdminLayout's destructuring approach

The review comment is substantially correct. The original analysis properly identified that the if (!session) condition is unreachable and that redirects are inconsistent. The suggested refactor aligns with the existing pattern used elsewhere in the codebase.


Fix the session state condition to match the destructuring pattern used in AdminLayout, and standardize redirect routes

The if (!session) check on line 19 will never execute because authClient.useSession() returns an object with properties like { isPending, data }, not a falsy value. This makes the loading UI unreachable.

The AdminLayout (at apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx) demonstrates the correct pattern:

const { isPending, data } = authClient.useSession();
if (isPending) {
  return <LoadingScreen />;
}

Additionally, redirect routes differ across files:

  • index.tsx (line 36): "/(public)/auth"
  • AdminLayout (line 13): "/auth"

Update index.tsx to destructure isPending and data, check isPending for the loading state, and align redirects with AdminLayout or establish a consistent route naming convention across the app.

apps/mobile/src/components/ui/button.tsx (1)

91-105: Forward ref prop to Pressable.

ButtonProps includes React.RefAttributes<typeof Pressable> which indicates ref support, but the Button component doesn't forward the ref to the underlying Pressable. In React 19, ref is a regular prop and should be passed through.

Apply this diff to forward the ref:

-function Button({ className, variant, size, ...props }: ButtonProps) {
+function Button({ className, variant, size, ref, ...props }: ButtonProps) {
   return (
     <TextClassContext.Provider value={buttonTextVariants({ variant, size })}>
       <Pressable
         className={cn(props.disabled && 'opacity-50', buttonVariants({ variant, size }), className)}
         role="button"
+        ref={ref}
         {...props}
       />
     </TextClassContext.Provider>
   );
 }
apps/mobile/src/app/(public)/auth/sign-in.tsx (1)

52-53: Remove console.error from committed code.

Line 52 uses console.error, which violates the coding guideline: "Don't use console or debugger in committed code." The same issue occurs at line 80.

Remove or replace with a proper logging solution:

    } catch (error) {
-      console.error("Login error:", error);
      toast.error("An unexpected error occurred", {
        position: "bottom-center",
      });

Apply the same fix to line 80:

    } catch (error) {
-      console.error("Verification email error:", error);
      toast.error("Failed to send verification email", {
        position: "bottom-center",
      });
apps/mobile/src/app/(public)/auth/forgot-password.tsx (1)

53-57: Remove console.error from committed code.

Line 54 uses console.error, which violates the coding guideline: "Don't use console or debugger in committed code."

Remove the console.error statement:

    } catch (error) {
-      console.error("Password reset error:", error);
      toast.error("Failed to send reset instructions. Please try again.", {
        position: "bottom-center",
      });
🧹 Nitpick comments (9)
apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx (1)

12-14: Align admin unauthenticated redirect path with the rest of the app

Redirecting to /auth makes sense, but other places (e.g., apps/mobile/src/app/index.tsx) use "/(public)/auth" for the same target. For consistency with expo-router’s typed routes and to avoid subtle mismatches, consider standardizing on one form across the app (probably the grouped path).

apps/mobile/src/components/auth/sign-up-success-card.tsx (1)

30-75: Refactored success card layout looks clean and preserves behavior

The move to a View/Text-based layout keeps all the previous behaviors (continue CTA, resend with loading/feedback states) while simplifying structure and matching the rest of the mobile UI primitives. No issues from a logic or typing standpoint.

If you later standardize spacing/typography across auth screens, this component already aligns well with that direction.

apps/mobile/src/app/(public)/auth/reset-password.tsx (3)

26-28: Empty string fallback is redundant.

Line 27 provides an empty string fallback for a missing token, but the subsequent validation at lines 30-36 will immediately catch this case and redirect to sign-in. The fallback doesn't add value since !resetToken checks for both null/undefined and empty strings.

Simplify by removing the fallback:

-      resetToken = new URLSearchParams(window.location.search).get("token") || "";
+      resetToken = new URLSearchParams(window.location.search).get("token");

106-114: Back button positioning inconsistency.

The back button uses top-0 (line 109) while other auth screens use top-4 (e.g., index.tsx line 27, forgot-password.tsx). This creates visual inconsistency.

Apply this diff for consistent spacing:

-          className="absolute top-0 left-0 z-10"
+          className="absolute top-4 left-4 z-10"

94-102: Loading state lacks Screen wrapper.

The loading state (lines 96-100) doesn't use the Screen component, while the main render (line 105) does. This creates an inconsistent experience if users see the loading state.

Wrap the loading state in Screen for consistency:

  if (!token) {
    return (
-      <View className="flex-1 justify-center items-center">
+      <Screen className="flex-1 justify-center items-center">
        <View className="w-full max-w-sm p-6">
          <Text className="text-center text-muted-foreground">Loading...</Text>
        </View>
-      </View>
+      </Screen>
    );
  }
apps/mobile/src/components/screen.tsx (4)

11-12: Consider making keyboard offset configurable.

The hardcoded 20px offset for iOS may not suit all screen layouts. Consider accepting an optional keyboardOffset prop to allow consumers to customize this value when needed.

Example:

-const isIos = Platform.OS === "ios";
-const keyboardOffset = isIos ? 20 : 0;
-
 export const Screen = ({
   children,
   className,
   SystemBarsProps,
   KeyboardAvoidingViewProps,
+  keyboardOffset,
 }: {
   children: React.ReactNode;
   className?: string;
   SystemBarsProps?: SystemBarsProps;
   KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
+  keyboardOffset?: number;
 }) => {
+  const isIos = Platform.OS === "ios";
+  const offset = keyboardOffset ?? (isIos ? 20 : 0);
   const { isDarkColorScheme } = useColorScheme();
   return (
     <SafeAreaView className={cn("flex-1", className)}>
       <SystemBars
         style={isDarkColorScheme ? "dark" : "light"}
         {...SystemBarsProps}
       />
       <KeyboardAvoidingView
         behavior={isIos ? "padding" : "height"}
-        keyboardVerticalOffset={keyboardOffset}
+        keyboardVerticalOffset={offset}
         {...KeyboardAvoidingViewProps}
         style={[{ flex: 1 }, KeyboardAvoidingViewProps?.style]}
       >
         {children}
       </KeyboardAvoidingView>
     </SafeAreaView>
   );
 };

17-18: Use camelCase for prop names.

The prop names SystemBarsProps and KeyboardAvoidingViewProps use PascalCase, which deviates from standard JavaScript and React naming conventions. Props should be camelCase for consistency.

 export const Screen = ({
   children,
   className,
-  SystemBarsProps,
-  KeyboardAvoidingViewProps,
+  systemBarsProps,
+  keyboardAvoidingViewProps,
 }: {
   children: React.ReactNode;
   className?: string;
-  SystemBarsProps?: SystemBarsProps;
-  KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
+  systemBarsProps?: SystemBarsProps;
+  keyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
 }) => {
   const { isDarkColorScheme } = useColorScheme();
   return (
     <SafeAreaView className={cn("flex-1", className)}>
       <SystemBars
         style={isDarkColorScheme ? "dark" : "light"}
-        {...SystemBarsProps}
+        {...systemBarsProps}
       />
       <KeyboardAvoidingView
         behavior={isIos ? "padding" : "height"}
         keyboardVerticalOffset={keyboardOffset}
-        {...KeyboardAvoidingViewProps}
-        style={[{ flex: 1 }, KeyboardAvoidingViewProps?.style]}
+        {...keyboardAvoidingViewProps}
+        style={[{ flex: 1 }, keyboardAvoidingViewProps?.style]}
       >
         {children}
       </KeyboardAvoidingView>
     </SafeAreaView>
   );
 };

14-24: Extract props type to a separate interface.

As per coding guidelines, "define shapes with TypeScript interfaces" when using object destructuring for parameters. Extract the inline type definition to improve reusability and type composition.

+export interface ScreenProps {
+  children: React.ReactNode;
+  className?: string;
+  systemBarsProps?: SystemBarsProps;
+  keyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
+}
+
-export const Screen = ({
-  children,
-  className,
-  SystemBarsProps,
-  KeyboardAvoidingViewProps,
-}: {
-  children: React.ReactNode;
-  className?: string;
-  SystemBarsProps?: SystemBarsProps;
-  KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
-}) => {
+export const Screen = ({
+  children,
+  className,
+  systemBarsProps,
+  keyboardAvoidingViewProps,
+}: ScreenProps) => {

27-27: Consider making SafeAreaView edges configurable.

The SafeAreaView uses default edges (all sides). For some screen layouts, you may want to control which edges receive safe area insets. Consider accepting an optional edges prop.

 export const Screen = ({
   children,
   className,
   SystemBarsProps,
   KeyboardAvoidingViewProps,
+  safeAreaEdges,
 }: {
   children: React.ReactNode;
   className?: string;
   SystemBarsProps?: SystemBarsProps;
   KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
+  safeAreaEdges?: ('top' | 'right' | 'bottom' | 'left')[];
 }) => {
   const { isDarkColorScheme } = useColorScheme();
   return (
-    <SafeAreaView className={cn("flex-1", className)}>
+    <SafeAreaView className={cn("flex-1", className)} edges={safeAreaEdges}>
       <SystemBars
         style={isDarkColorScheme ? "dark" : "light"}
         {...SystemBarsProps}
       />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42cdc9d and 720300a.

⛔ Files ignored due to path filters (5)
  • apps/mobile/assets/images/partial-react-logo.png is excluded by !**/*.png
  • apps/mobile/assets/images/react-logo.png is excluded by !**/*.png
  • apps/mobile/assets/images/react-logo@2x.png is excluded by !**/*.png
  • apps/mobile/assets/images/react-logo@3x.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • .vscode/settings.json (1 hunks)
  • apps/api/.env.example (1 hunks)
  • apps/api/src/env.ts (1 hunks)
  • apps/api/src/index.ts (1 hunks)
  • apps/api/src/lib/auth.ts (1 hunks)
  • apps/api/src/lib/email/config.ts (1 hunks)
  • apps/mobile/components.json (1 hunks)
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx (1 hunks)
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/_layout.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx (2 hunks)
  • apps/mobile/src/app/(public)/auth/index.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/reset-password.tsx (3 hunks)
  • apps/mobile/src/app/(public)/auth/sign-in.tsx (2 hunks)
  • apps/mobile/src/app/(public)/auth/sign-up.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/social-sign-in.tsx (0 hunks)
  • apps/mobile/src/app/index.tsx (3 hunks)
  • apps/mobile/src/components/auth/sign-up-form.tsx (4 hunks)
  • apps/mobile/src/components/auth/sign-up-success-card.tsx (4 hunks)
  • apps/mobile/src/components/screen.tsx (1 hunks)
  • apps/mobile/src/components/ui/button.tsx (3 hunks)
  • apps/mobile/src/components/ui/icon.tsx (1 hunks)
  • apps/mobile/src/components/ui/text.tsx (1 hunks)
  • apps/mobile/src/containers/auth/sign-up.container.tsx (1 hunks)
  • apps/mobile/src/containers/core/HeaderUser.tsx (1 hunks)
  • apps/mobile/src/lib/auth-client.ts (0 hunks)
  • biome.jsonc (1 hunks)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • turbo.json (3 hunks)
💤 Files with no reviewable changes (2)
  • apps/mobile/src/app/(public)/auth/social-sign-in.tsx
  • apps/mobile/src/lib/auth-client.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use object destructuring for method parameters (wrap multiple parameters in an object; define shapes with TypeScript interfaces)
Use authClient.useSession() for session state
Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)
Import UI components via path aliases (e.g., @/src/components/ui/)
Prefer union string/number literals over TypeScript enums
Use export type and import type for types
Don't use non-null assertions (!) or implicit any
Avoid TypeScript namespaces; prefer ESM modules

Files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/lib/auth.ts
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/api/src/lib/email/config.ts
  • apps/api/src/index.ts
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Platform.OS and process.env.EXPO_OS for conditional platform logic

Files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/lib/auth.ts
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/api/src/lib/email/config.ts
  • apps/api/src/index.ts
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use React Native primitives (View, Text, etc.) instead of HTML elements
Use Tailwind responsive prefixes (sm:, md:, lg:) and NativeWind responsive utilities
Use web: prefix for web-only styles; use conditional rendering for native-only components
Use IconSymbol to map SF Symbols on iOS to Material Icons on other platforms
Web a11y: ensure buttons have type, anchors are valid, SVGs/iframes have title, html has lang, inputs have valid autocomplete, ARIA roles/props/values are valid

Files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Don't use the arguments object
Use for...of instead of Array.forEach
Use arrow functions instead of function expressions
Use Date.now() instead of new Date().getTime()
Use .flatMap() instead of map().flat() when possible
Prefer regex literals over RegExp constructor when possible
Use concise optional chaining instead of chained logical expressions
Always provide radix to parseInt()
Don't use var; use const/let and prefer const when single assignment
Use template literals over string concatenation
Use strict equality (===/!==)
Don't use console or debugger in committed code
Prevent import cycles and avoid namespace imports
Don't hardcode secrets (API keys/tokens) in source
Don't have unused vars, params, imports, labels, or private members
Avoid await inside loops; ensure Promise-like statements are handled
Disallow bitwise operators and comma operator
Ensure switch statements are exhaustive; default clause comes last; no fallthrough or duplicate cases
Use object spread over Object.assign when creating new objects
Prefer Array.isArray over instanceof Array; use at() instead of integer index access where appropriate
No unreachable code, duplicate conditions, or assignments in conditionals
Always use radix with parseInt; prefer Number.isNaN/Number.isFinite over globals
Disallow using global eval, with, var, and unsafe negation
Ensure typeof comparisons use valid strings; getters return values; constructors don’t return values

Files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/lib/auth.ts
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/api/src/lib/email/config.ts
  • apps/api/src/index.ts
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{tsx,jsx}: Provide key props in lists; avoid array index as key
Don't define React components inside other components
Don't use event handlers on non-interactive elements; make interactive roles focusable
Don't use both children and dangerouslySetInnerHTML; avoid dangerous JSX props
Use <>...</> instead of ...

Files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
🧠 Learnings (23)
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/lib/auth-client.ts : Initialize Better Auth client in src/lib/auth-client.ts with platform-specific plugins

Applied to files:

  • apps/api/.env.example
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/containers/**/*.container.tsx : Create containers in src/containers/ mirroring Expo Router paths (e.g., admin/add-user.container.tsx)

Applied to files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use NativeWind with className and Tailwind utilities for component styling

Applied to files:

  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/ui/icon.tsx
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use authClient.useSession() for session state

Applied to files:

  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/app/**/_layout.{ts,tsx} : Ensure global.css is imported in the app entry layout for NativeWind setup

Applied to files:

  • apps/mobile/src/app/(public)/(protected)/admin/_layout.tsx
  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/(protected)/_layout.tsx
  • apps/api/src/index.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/components/ui/**/*.{ts,tsx} : Only truly reusable UI components belong in src/components/ui with proper TypeScript interfaces

Applied to files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/components/screen.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use React Native primitives (View, Text, etc.) instead of HTML elements

Applied to files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/auth/sign-up-success-card.tsx
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/app/** : Expo Router: keep all routes under src/app with route groups (e.g., (auth), (tabs))

Applied to files:

  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Follow Expo Router file-based routing conventions for navigation structure

Applied to files:

  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)

Applied to files:

  • apps/mobile/src/app/index.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use web: prefix for web-only styles; use conditional rendering for native-only components

Applied to files:

  • apps/mobile/src/app/index.tsx
  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/components.json
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use IconSymbol to map SF Symbols on iOS to Material Icons on other platforms

Applied to files:

  • apps/mobile/src/components/ui/icon.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Web a11y: ensure buttons have type, anchors are valid, SVGs/iframes have title, html has lang, inputs have valid autocomplete, ARIA roles/props/values are valid

Applied to files:

  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/components/**/*.{ts,tsx} : Implement component variants with class-variance-authority (cva)

Applied to files:

  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ios,android,web}.{ts,tsx,js} : Use platform-specific file extensions (.ios.tsx, .web.ts, etc.) for platform implementations

Applied to files:

  • apps/mobile/src/components/ui/button.tsx
  • apps/mobile/src/components/ui/text.tsx
📚 Learning: 2025-08-28T12:01:24.375Z
Learnt from: chakrihacker
Repo: fyndx/universal-starter PR: 13
File: apps/frontend/tsconfig.json:13-17
Timestamp: 2025-08-28T12:01:24.375Z
Learning: When using TypeScript project references for Eden Treaty type safety, the backend needs both "composite": true and "declaration": true in tsconfig.json to emit .d.ts files that the frontend can consume. The workspace root also needs TypeScript as a devDependency for the build commands to work properly.

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prevent import cycles and avoid namespace imports

Applied to files:

  • apps/api/src/index.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Import UI components via path aliases (e.g., @/src/components/ui/)

Applied to files:

  • apps/api/src/index.ts
  • apps/mobile/components.json
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't hardcode secrets (API keys/tokens) in source

Applied to files:

  • apps/api/src/index.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use export type and import type for types

Applied to files:

  • apps/api/src/index.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to metro.config.js : Metro config must support CSS, package exports, and .mjs files

Applied to files:

  • apps/mobile/components.json
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to babel.config.js : Configure NativeWind Babel/Metro plugins for Tailwind compilation

Applied to files:

  • apps/mobile/components.json
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to tailwind.config.js : Configure themes in Tailwind via CSS custom properties in tailwind.config.js

Applied to files:

  • apps/mobile/components.json
🧬 Code graph analysis (17)
apps/mobile/src/app/(public)/auth/sign-in.tsx (2)
apps/mobile/src/components/screen.tsx (1)
  • Screen (14-42)
apps/frontend/src/app/(public)/auth/sign-in.tsx (3)
  • SignIn (21-180)
  • ctx (41-52)
  • setIsLoading (30-67)
apps/mobile/src/app/(public)/auth/index.tsx (3)
apps/mobile/src/lib/auth-client.ts (1)
  • authClient (21-28)
apps/mobile/src/components/screen.tsx (1)
  • Screen (14-42)
apps/frontend/src/app/(public)/auth/sign-in.tsx (4)
  • SignIn (21-180)
  • setIsLoading (30-67)
  • ctx (41-52)
  • email (69-95)
apps/mobile/src/app/index.tsx (2)
apps/frontend/src/app/(public)/(protected)/home/(tabs)/index.tsx (2)
  • HomeScreen (7-18)
  • router (12-12)
apps/frontend/src/app/(public)/auth/sign-in.tsx (3)
  • SignIn (21-180)
  • setIsLoading (30-67)
  • ctx (41-52)
apps/mobile/src/components/ui/icon.tsx (1)
apps/mobile/src/lib/utils.ts (1)
  • cn (4-6)
apps/mobile/src/components/screen.tsx (2)
apps/mobile/src/hooks/useColorScheme.ts (1)
  • useColorScheme (9-64)
apps/mobile/src/lib/utils.ts (1)
  • cn (4-6)
apps/mobile/src/app/(public)/auth/_layout.tsx (1)
apps/frontend/src/app/(public)/auth/sign-in.tsx (2)
  • SignIn (21-180)
  • ctx (41-52)
apps/mobile/src/app/(public)/auth/sign-up.tsx (2)
apps/mobile/src/components/screen.tsx (1)
  • Screen (14-42)
apps/mobile/src/containers/auth/sign-up.container.tsx (1)
  • SignUpContainer (7-56)
apps/mobile/src/components/auth/sign-up-form.tsx (4)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
apps/frontend/src/app/(public)/auth/sign-in.tsx (1)
  • SignIn (21-180)
apps/mobile/src/containers/core/HeaderUser.tsx (1)
apps/frontend/src/app/(public)/auth/sign-in.tsx (1)
  • SignIn (21-180)
apps/api/src/lib/auth.ts (3)
apps/api/src/env.ts (1)
  • env (70-70)
apps/api/src/infra/db.ts (1)
  • prisma (2-2)
apps/api/src/plugins/auth.ts (1)
  • Elysia (4-16)
apps/mobile/src/components/ui/button.tsx (2)
apps/mobile/src/lib/utils.ts (1)
  • cn (4-6)
apps/mobile/src/components/ui/text.tsx (1)
  • TextClassContext (89-89)
apps/mobile/src/app/(public)/auth/reset-password.tsx (6)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/components/ui/password.tsx (1)
  • PasswordInput (50-50)
apps/mobile/src/models/admin/user-detail.model.ts (1)
  • handleResetPassword (184-189)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
apps/frontend/src/app/(public)/auth/sign-in.tsx (2)
  • SignIn (21-180)
  • ctx (41-52)
apps/mobile/src/app/(public)/(protected)/_layout.tsx (1)
apps/frontend/src/app/(public)/auth/sign-in.tsx (1)
  • SignIn (21-180)
apps/api/src/lib/email/config.ts (2)
apps/api/src/lib/email/types.ts (1)
  • EmailConfig (27-49)
apps/api/src/env.ts (1)
  • env (70-70)
apps/mobile/src/app/(public)/auth/forgot-password.tsx (6)
apps/mobile/src/components/screen.tsx (1)
  • Screen (14-42)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/input.tsx (1)
  • Input (25-25)
apps/mobile/src/models/admin/user-detail.model.ts (1)
  • handleResetPassword (184-189)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
apps/mobile/src/components/auth/sign-up-success-card.tsx (1)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/text.tsx (1)
apps/mobile/src/lib/utils.ts (1)
  • cn (4-6)
🪛 dotenv-linter (4.0.0)
apps/api/.env.example

[warning] 7-7: [UnorderedKey] The FACEBOOK_CLIENT_ID key should go before the GOOGLE_CLIENT_ID key

(UnorderedKey)


[warning] 8-8: [UnorderedKey] The FACEBOOK_CLIENT_SECRET key should go before the GOOGLE_CLIENT_ID key

(UnorderedKey)


[warning] 9-9: [UnorderedKey] The GITHUB_CLIENT_ID key should go before the GOOGLE_CLIENT_ID key

(UnorderedKey)


[warning] 10-10: [UnorderedKey] The GITHUB_CLIENT_SECRET key should go before the GOOGLE_CLIENT_ID key

(UnorderedKey)


[warning] 11-11: [UnorderedKey] The APPLE_CLIENT_ID key should go before the BETTER_AUTH_SECRET key

(UnorderedKey)


[warning] 12-12: [UnorderedKey] The APPLE_CLIENT_SECRET key should go before the BETTER_AUTH_SECRET key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (33)
apps/mobile/components.json (2)

14-17: I'll verify the path alias consistency concerns across the mobile app codebase. Let me start by examining the configuration files and checking if the referenced directories exist.
<function_calls>


#!/bin/bash

Check components.json and tsconfig.json configuration for apps/mobile

echo "=== Checking components.json in apps/mobile ==="
if [ -f "apps/mobile/components.json" ]; then
cat -n "apps/mobile/components.json" | head -30
else
echo "components.json not found"
fi

echo ""
echo "=== Checking tsconfig.json in apps/mobile ==="
if [ -f "apps/mobile/tsconfig.json" ]; then
cat -n "apps/mobile/tsconfig.json"
else
echo "tsconfig.json not found in apps/mobile"
fi

echo ""
echo "=== Checking root tsconfig.json ==="
if [ -f "tsconfig.json" ]; then
cat -n "tsconfig.json" | head -50
else
echo "tsconfig.json not found in root"
fi


</function_calls>

<function_calls>


#!/bin/bash

Check if the aliased directories exist in apps/mobile

echo "=== Checking referenced alias directories ==="

Define directories to check

dirs=(
"apps/mobile/lib/utils"
"apps/mobile/components/ui"
"apps/mobile/lib"
"apps/mobile/hooks"
)

for dir in "${dirs[@]}"; do
if [ -d "$dir" ]; then
echo "✓ Directory exists: $dir"
# List contents if directory exists
echo " Contents:"
ls -la "$dir" | head -10
else
echo "✗ Directory NOT found: $dir"
fi
done


</function_calls>

<function_calls>


#!/bin/bash

Search for import patterns in apps/mobile to check what import style is actually used

echo "=== Searching for import patterns in apps/mobile ==="

echo ""
echo "Searching for @/ style imports..."
rg -n "from ['"]@/" apps/mobile --type=ts --type=tsx -C 2 | head -50

echo ""
echo "Searching for / style imports..."
rg -n "from ['"]
/" apps/mobile --type=ts --type=tsx -C 2 | head -50

echo ""
echo "Searching for relative imports../"
rg -n "from ['"]../" apps/mobile --type=ts --type=tsx -C 2 | head -30


</function_calls>


2-11: The Tailwind and styling files are correctly configured and already exist.

The configuration references to tailwind.config.js and global.css use relative paths that correctly point to apps/mobile/tailwind.config.js and apps/mobile/global.css, which are both present in the codebase. This is the standard monorepo structure where each app maintains its own configuration files at the app root. No additional action is needed.

Likely an incorrect or invalid review comment.

apps/mobile/src/app/(public)/(protected)/_layout.tsx (1)

14-14: Redirect to social login landing page is correctly configured.

The "/auth" route exists and is properly structured as a landing page (apps/mobile/src/app/(public)/auth/index.tsx) that presents multiple social login options (Google, Facebook, GitHub, Apple, LinkedIn). The redirect change from "/auth/sign-in" to "/auth" correctly routes unauthenticated users to this landing page, aligning with the PR's social login feature.

.vscode/settings.json (1)

6-6: Default formatter switch to Biome is consistent with tooling updates

Setting the global editor.defaultFormatter to biomejs.biome aligns with the updated Biome tooling and the language-specific overrides below. This should help keep formatting consistent across JS/TS/JSON files as long as the Biome extension is installed in VS Code.

You may want to confirm everyone on the team has biomejs.biome enabled in their editor.

pnpm-workspace.yaml (1)

9-10: I'll help you verify the workspace-wide Biome and TypeScript version bumps. Let me search for how these tools are referenced across the workspace and check compatibility.
<function_calls>

#!/bin/bash

Search for @biomejs/biome and typescript references across the workspace

echo "=== Searching for @biomejs/biome references ==="
rg -n '"@biomejs/biome"' --type=json --type=yaml

echo ""
echo "=== Searching for typescript references ==="
rg -n 'typescript' --type=json --type=yaml | head -50


</function_calls>

Now let me check the tsconfig files and CI configuration:
<function_calls>

#!/bin/bash

Find and examine tsconfig files

echo "=== Finding tsconfig files ==="
fd -name 'tsconfig*.json' -o -name 'tsconfig*.yaml'

echo ""
echo "=== Examining main tsconfig.json ==="
if [ -f tsconfig.json ]; then
cat -n tsconfig.json | head -40
fi

echo ""
echo "=== Finding CI configuration files ==="
fd -path '.github/workflows/.yml' -o -path '.github/workflows/.yaml'


</function_calls>

Let me also check if there are any package.json files that might override the catalog versions:
<function_calls>

#!/bin/bash

Look for packages that might override biome/typescript versions

echo "=== Checking for package.json files with biome/typescript overrides ==="
fd -name 'package.json' | xargs grep -l '@biomejs/biome|typescript' 2>/dev/null

echo ""
echo "=== Examining pnpm-workspace.yaml ==="
cat -n pnpm-workspace.yaml


</function_calls>

turbo.json (1)

3-3: Turbo configuration options are supported; verify db:reset script is wired in package.json.

The ui: "tui" option and interactive flag are both supported in Turborepo's turbo.json. The pattern you're using—cache: false with interactive: true on the dev task, and cache: false with persistent: true on db:reset—aligns with documented Turbo constraints (interactive tasks require persistent: true and cannot be cached).

However, verify that db:reset is defined as a script in your package.json, since Turbo task definitions must map to actual executable scripts. The repository clone failed, so I cannot confirm this directly. If db:reset isn't found in package.json scripts, Turbo will fail when trying to run that task.

package.json (1)

20-20: Based on my verification:

The ultracite 6.3.6 version is fully compatible with the biome.jsonc extends configuration.

The web search confirms that ultracite v6.3.6 explicitly supports the preset system with "ultracite/core" and "ultracite/react" extends. Your biome.jsonc is correctly configured and follows the official ultracite v6 documentation pattern exactly. No compatibility drift or version misalignment detected.

biome.jsonc (1)

2-3: Biome 2.3.7 configuration is correct and compatible with ultracite 6.3.6

The schema version (2.3.7) matches the installed Biome version, and ultracite 6.3.6 explicitly supports the split presets ["ultracite/core", "ultracite/react"] as the recommended configuration for v6. The extends array syntax is the documented approach for v6 and poses no resolution issues.

apps/mobile/src/components/ui/icon.tsx (2)

1-11: LGTM!

The Icon implementation is clean and follows coding guidelines: uses type imports, path aliases, proper destructuring, and avoids React hooks.


43-43: Verify the default icon size.

The default size of 14 is smaller than typical icon sizes (16, 20, 24). Please confirm this is intentional and aligns with your design system.

apps/mobile/src/components/ui/button.tsx (2)

6-54: LGTM!

The platform-aware variant system is well-implemented, using Platform.select appropriately for web-only hover/focus states and maintaining clean separation of concerns.


56-89: LGTM!

Text variant system correctly complements button variants with appropriate platform-specific styling.

apps/mobile/src/components/ui/text.tsx (3)

7-43: LGTM!

The text variant system provides a comprehensive typography scale with appropriate platform-specific enhancements for web.


49-63: LGTM!

Accessibility mappings correctly provide semantic roles and ARIA attributes for heading hierarchy and special text types.


67-87: Verify role/aria-level behavior with asChild.

When asChild={true}, the role and aria-level props are passed to the child component via Slot.Text. Ensure that these accessibility attributes are correctly handled by all potential child components, as non-text children may not support these props.

Consider conditionally applying these attributes only when asChild={false}:

role={asChild ? undefined : (variant ? ROLE[variant] : undefined)}
aria-level={asChild ? undefined : (variant ? ARIA_LEVEL[variant] : undefined)}
apps/mobile/src/containers/auth/sign-up.container.tsx (1)

35-35: LGTM!

Adding padding improves spacing and aligns with the new Screen-based layout approach used throughout the PR.

apps/api/src/index.ts (1)

1-1: LGTM!

The environment loader import at the top ensures environment variables are validated before any other initialization occurs, which is the correct pattern for startup configuration.

apps/mobile/src/app/(public)/auth/sign-up.tsx (1)

1-10: LGTM!

Imports are well-organized and follow the project's path alias conventions.

apps/mobile/src/app/(public)/auth/_layout.tsx (1)

1-23: LGTM!

The layout simplification and updated redirect path correctly align with the new Screen-based approach and route structure. The explicit screen declarations improve clarity and the removal of the separate social-sign-in screen is consistent with integrating social login into the main auth flow.

apps/api/.env.example (1)

4-14: LGTM!

The social login provider credentials are correctly added as placeholders. The environment variables are well-organized and follow the existing pattern.

Note: The static analysis warnings about key ordering are cosmetic. The current grouping by provider (CLIENT_ID followed by CLIENT_SECRET for each provider) is more intuitive than alphabetical ordering.

apps/api/src/env.ts (2)

34-37: LGTM: Clean boolean transform for SMTP_SECURE.

The string-to-boolean transform handles environment variable parsing correctly.


62-68: LGTM: Proper validation error handling.

The safeParse approach with error logging and process exit ensures the application doesn't start with invalid configuration. Using z.flattenError provides clear field-level error messages.

apps/mobile/src/app/(public)/auth/index.tsx (1)

22-32: LGTM: Clean back navigation pattern.

The conditional back button with router.canGoBack() check follows the established pattern used in other auth screens.

apps/mobile/src/app/(public)/auth/reset-password.tsx (1)

115-159: LGTM: Clean Screen-based refactor.

The transition from Card-based to Screen-based layout is well-executed, maintaining all functionality while improving consistency with other auth screens.

apps/api/src/lib/email/config.ts (1)

7-33: LGTM: Clean refactor to centralized env module.

The migration from process.env to the typed env object improves type safety and centralizes environment validation. The nullish coalescing defaults for smtpPort (587) and smtpSecure (false) are sensible fallbacks.

apps/mobile/src/components/auth/sign-up-form.tsx (1)

70-137: LGTM: Consistent Card-to-View refactor.

The sign-up form successfully transitions to the Screen-based layout pattern used across all auth screens. All functionality is preserved, including password strength calculation and validation.

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

91-173: LGTM: Successful Screen-based refactor.

The transition from Card-based to Screen-based layout is clean and maintains all authentication functionality, including email verification prompts and resend capability. The use of asChild with Link (lines 123, 165) follows React Native best practices.

apps/mobile/src/app/(public)/auth/forgot-password.tsx (1)

64-116: LGTM: Clean Screen-based refactor.

The forgot password screen successfully adopts the Screen-based layout pattern, maintaining all functionality while ensuring visual consistency with other auth screens.

apps/api/src/lib/auth.ts (2)

32-38: I'll verify the Google OAuth configuration concern by examining the Better Auth setup, checking the version in use, and researching the redirect URI handling behavior.
<function_calls>


#!/bin/bash

Find package.json files and check for better-auth dependency

find . -name "package.json" -type f | head -5 | while read f; do
if grep -q "better-auth" "$f"; then
echo "=== Found in: $f ==="
cat "$f" | grep -A 2 -B 2 "better-auth"
fi
done


</function_calls>

<function_calls>

Better Auth redirect URI auto-generated or explicit configuration required OAuth

</function_calls>

I need to gather information about the Better Auth configuration. Let me search the codebase for the context and also check the project dependencies.
<function_calls>


#!/bin/bash

Find and read the auth.ts file to understand full context

find . -path "*/apps/api/src/lib/auth.ts" -type f


</function_calls>


11-11: Based on my analysis of the codebase, I can now provide the verification results:

Auth routes are configured to expect /api/auth prefix, and uncommenting the basePath is necessary.

The routing setup shows:

  • Elysia app created with prefix: "/api" (line 19, apps/api/src/index.ts)
  • Better Auth handler mounted directly without sub-path specification (line 57: .mount(auth.handler))
  • When basePath: "/auth" is commented out, Better Auth uses its default basePath of "/", resulting in auth routes at /api/*
  • Mobile client hardcodes baseURL: "${env.EXPO_PUBLIC_API_URL}/api/auth" (apps/mobile/src/lib/auth-client.ts:22)
  • OpenAPI plugin explicitly defaults to prefix = "/api/auth" (apps/api/src/lib/open-api.ts:8)
  • Google OAuth redirect URI expects /api/auth/callback/google (apps/api/src/lib/auth.ts:37)

This creates a routing mismatch: without the basePath, auth endpoints will be at /api/* instead of /api/auth/*, breaking all mobile authentication calls.


Verify that basePath: "/auth" is uncommented in the Better Auth configuration.

The commented basePath: "/auth" on line 11 must be active to ensure auth routes are accessible at /api/auth/* where the mobile client expects them.

apps/mobile/src/components/screen.tsx (3)

25-25: Verify if a Legend State observable alternative exists for color scheme.

The coding guidelines state: "Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)". The useColorScheme hook is not a Better Auth hook. Please confirm whether a Legend State observable should be used instead for managing color scheme state.

As per coding guidelines.


36-36: Verify that style override behavior is intentional.

The style array places { flex: 1 } first, then spreads KeyboardAvoidingViewProps?.style. This allows consumers to override the flex property, which could break the component's layout if unintended. Confirm whether this flexibility is desired or if flex: 1 should be enforced.

If flex: 1 must always be applied, consider:

-        style={[{ flex: 1 }, KeyboardAvoidingViewProps?.style]}
+        style={[KeyboardAvoidingViewProps?.style, { flex: 1 }]}

1-42: Verify component location aligns with project structure.

The retrieved learnings indicate: "Only truly reusable UI components belong in src/components/ui with proper TypeScript interfaces". This Screen component appears to be a reusable UI primitive used across multiple screens. Should it be located in apps/mobile/src/components/ui/screen.tsx instead?

Based on learnings.

Comment thread apps/api/src/env.ts Outdated
Comment thread apps/api/src/lib/auth.ts Outdated
Comment on lines +21 to +27
...(env.NODE_ENV === "development" ? [
"exp://*/*", // Trust all Expo development URLs
"exp://10.0.0.*:*/*", // Trust 10.0.0.x IP range
"exp://192.168.*.*:*/*", // Trust 192.168.x.x IP range
"exp://172.*.*.*:*/*", // Trust 172.x.x.x IP range
"exp://localhost:*/*" // Trust localhost
] : [])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Overly permissive development trusted origins.

The development-mode trusted origins (lines 21-27) are too broad and create security risks:

  1. exp://*/* trusts all Expo development URLs without restriction
  2. exp://172.*.*.*:*/* includes non-private IP ranges (172.16-31 are private, but 172.0-15 and 172.32-255 are public)
  3. Wildcard patterns bypass origin validation entirely

Restrict to private IP ranges only:

    // Development mode - Expo's exp:// scheme with local IP ranges
    ...(env.NODE_ENV === "development" ? [
-        "exp://*/*",                 // Trust all Expo development URLs
-        "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
-        "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
-        "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
+        "exp://10.*.*.*:*/*",        // Private: 10.0.0.0/8
+        "exp://192.168.*.*:*/*",     // Private: 192.168.0.0/16
+        "exp://172.16.*.*:*/*",      // Private: 172.16.0.0/12 (start)
+        "exp://172.17.*.*:*/*",
+        "exp://172.18.*.*:*/*",
+        "exp://172.19.*.*:*/*",
+        "exp://172.20.*.*:*/*",
+        "exp://172.21.*.*:*/*",
+        "exp://172.22.*.*:*/*",
+        "exp://172.23.*.*:*/*",
+        "exp://172.24.*.*:*/*",
+        "exp://172.25.*.*:*/*",
+        "exp://172.26.*.*:*/*",
+        "exp://172.27.*.*:*/*",
+        "exp://172.28.*.*:*/*",
+        "exp://172.29.*.*:*/*",
+        "exp://172.30.*.*:*/*",
+        "exp://172.31.*.*:*/*",      // Private: 172.16.0.0/12 (end)
        "exp://localhost:*/*"        // Trust localhost
    ] : [])

Alternatively, if you need the broader pattern, add a comment explaining the security trade-off and ensure this is never used in production.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/api/src/lib/auth.ts around lines 21 to 27, the development trusted
origins are overly permissive (exp://*/*, broad 172.* range and other
wildcards); replace the global wildcard and the broad 172.* pattern with
explicit private-range entries only (e.g. exp://10.*/*, exp://10.*.*:*/* or
equivalent for 10.0.0.*, exp://192.168.*.*:*/*, and the private
172.16.0.0–172.31.255.255 range narrowed to explicit patterns such as
exp://172.16.*.*:*/* through exp://172.31.*.*:*/*), remove exp://*/*, and drop
any other wildcard catch-alls; if you intentionally need broader patterns, add a
clear comment warning about the security trade-off and ensure the block is gated
to development-only (never enabled in production).

Comment thread apps/api/src/lib/auth.ts Outdated
Comment thread apps/mobile/src/app/(public)/auth/index.tsx
Comment on lines +46 to +80
<FontAwesome name="google" size={20} color="black" />
<Text>Continue with Google</Text>
</Button>
<Button
variant="outline"
onPress={() => handleSocialLogin("facebook")}
className="flex-row items-center gap-2"
>
<FontAwesome name="facebook" size={20} color="black" />
<Text>Continue with Facebook</Text>
</Button>
<Button
variant="outline"
onPress={() => handleSocialLogin("github")}
className="flex-row items-center gap-2"
>
<FontAwesome name="github" size={20} color="black" />
<Text>Continue with Github</Text>
</Button>
<Button
variant="outline"
onPress={() => handleSocialLogin("apple")}
className="flex-row items-center gap-2"
>
<FontAwesome name="apple" size={20} color="black" />
<Text>Continue with Apple</Text>
</Button>
<Button
variant="outline"
onPress={() => handleSocialLogin("linkedin")}
className="flex-row items-center gap-2"
>
<FontAwesome name="linkedin" size={20} color="black" />
<Text>Continue with Linkedin</Text>
</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded icon colors break dark mode.

All FontAwesome icons use color="black" (lines 46, 54, 62, 70, 78), which will be invisible or have poor contrast in dark mode. The text color adapts via Tailwind classes, but icons remain black.

Use the theme-aware text-foreground color or extract the current color from the theme:

-<FontAwesome name="google" size={20} color="black" />
+<FontAwesome name="google" size={20} className="text-foreground" />

Note: If FontAwesome doesn't support className, you may need to use useColorScheme() to conditionally set the color:

import { useColorScheme } from "~/lib/useColorScheme";

const { isDarkColorScheme } = useColorScheme();
const iconColor = isDarkColorScheme ? "white" : "black";

// Then use:
<FontAwesome name="google" size={20} color={iconColor} />
🤖 Prompt for AI Agents
In apps/mobile/src/app/(public)/auth/index.tsx around lines 46 to 80 the
FontAwesome icons are hardcoded with color="black", which breaks dark mode;
update the component to use a theme-aware color instead — either apply the
shared text color (e.g., Tailwind's text-foreground) if the icon supports
className, or import and use useColorScheme to derive a boolean
isDarkColorScheme and set iconColor = isDarkColorScheme ? "white" : "black" (or
pull the appropriate foreground colors from your theme), then replace the
hardcoded "black" values with the theme-derived iconColor so icons match
light/dark modes.

Comment on lines +11 to +25
<Screen>
<View className="flex-row">
{router.canGoBack() && (
<Button
variant="ghost"
className="absolute top-0 left-0 z-10"
onPress={() => router.back()}
>
<ChevronLeft size={24} className="text-foreground" />
</Button>
)}
</View>
<SignUpContainer />
</Screen>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use Icon wrapper for className support.

Line 19 applies className directly to ChevronLeft, but Lucide icons don't natively support NativeWind's className. The new Icon component introduced in this PR (apps/mobile/src/components/ui/icon.tsx) provides this functionality via cssInterop.

Apply this diff to use the Icon wrapper:

+import { Icon } from "~/components/ui/icon";
 import { useRouter } from "expo-router";
 import { ChevronLeft } from "lucide-react-native";
 import { View } from "react-native";
 import { Screen } from "~/components/screen";
 import { Button } from "~/components/ui/button";
 import { SignUpContainer } from "~/containers/auth/sign-up.container";

 export default function SignUp() {
   const router = useRouter();
   return (
     <Screen>
       <View className="flex-row">
         {router.canGoBack() && (
           <Button
             variant="ghost"
             className="absolute top-0 left-0 z-10"
             onPress={() => router.back()}
           >
-            <ChevronLeft size={24} className="text-foreground" />
+            <Icon as={ChevronLeft} size={24} className="text-foreground" />
           </Button>
         )}
       </View>
       <SignUpContainer />
     </Screen>
   );
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/mobile/src/app/(public)/auth/sign-up.tsx around lines 11 to 25, the
Lucide ChevronLeft icon is receiving a NativeWind `className` directly; import
the new Icon wrapper from apps/mobile/src/components/ui/icon.tsx and replace the
direct ChevronLeft usage with the Icon wrapper (e.g., use Icon with the
ChevronLeft component passed as the icon prop and move
`className="text-foreground"` onto Icon while preserving the size prop), and
update the imports accordingly so the back button renders with
NativeWind-compatible styling.

Comment thread apps/mobile/src/containers/core/HeaderUser.tsx Outdated
…ation flows and add Buoy SDK dependencies.
@codeant-ai

codeant-ai Bot commented Nov 26, 2025

Copy link
Copy Markdown

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Nov 26, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
apps/mobile/src/app/(public)/auth/_layout.tsx (1)

5-13: Session gating and redirect logic look solid; consider a non-null loading state.

Using authClient.useSession() to block rendering until auth state is known and redirect authenticated users straight into /(public)/(protected)/home/(tabs) is clean and avoids a flash of unauthenticated UI. For UX, you might later swap the return null while isPending for a lightweight loading indicator or branded splash, but this is non-blocking.

Please confirm that the current authClient.useSession() API on mobile indeed exposes { isPending, data } with the same semantics you rely on here, given any Better Auth version changes.

apps/mobile/src/components/ui/password.tsx (1)

30-30: Simplify className template string.

The fallback || "" is unnecessary since undefined in a template string coerces to an empty string.

Apply this diff:

-        className={`pr-10 ${className || ""}`}
+        className={`pr-10 ${className ?? ""}`}

Or even simpler:

-        className={`pr-10 ${className || ""}`}
+        className={["pr-10", className].filter(Boolean).join(" ")}
apps/mobile/src/components/screen.tsx (1)

14-24: Consider extracting the props type for reusability.

The inline type definition works but extracting it would improve type reusability and documentation.

Apply this diff to extract the type:

+type ScreenProps = {
+  children: React.ReactNode;
+  className?: string;
+  SystemBarsProps?: SystemBarsProps;
+  KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
+};
+
-export const Screen = ({
-  children,
-  className,
-  SystemBarsProps: systemBarsProps,
-  KeyboardAvoidingViewProps: keyboardAvoidingViewProps,
-}: {
-  children: React.ReactNode;
-  className?: string;
-  SystemBarsProps?: SystemBarsProps;
-  KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps;
-}) => {
+export const Screen = ({
+  children,
+  className,
+  SystemBarsProps: systemBarsProps,
+  KeyboardAvoidingViewProps: keyboardAvoidingViewProps,
+}: ScreenProps) => {
apps/mobile/src/app/_layout.tsx (1)

77-79: Prefer arrow function for consistency.

The coding guidelines recommend using arrow functions over function declarations.

Apply this diff:

-function noop() {
-  // do nothing
-}
+const noop = () => {
+  // do nothing
+};
apps/mobile/src/components/dev-tools/tools.tsx (1)

2-2: Consider using styled UI components for consistency.

The coding guidelines recommend importing UI components via path aliases (e.g., @/src/components/ui/). The custom Button and Text components from ~/components/ui/ provide consistent styling and better integration with the app's design system.

Apply this diff to use the styled components:

-import { Button, Text, View } from "react-native";
+import { View } from "react-native";
+import { Button } from "~/components/ui/button";
+import { Text } from "~/components/ui/text";

Then update the Button usage on Line 15:

-      <Button onPress={onClose} title="Close" />
+      <Button onPress={onClose}>
+        <Text>Close</Text>
+      </Button>

As per coding guidelines, Import UI components via path aliases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 720300a and 1a5f887.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • apps/mobile/package.json (2 hunks)
  • apps/mobile/src/app/(public)/auth/_layout.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx (2 hunks)
  • apps/mobile/src/app/(public)/auth/index.tsx (1 hunks)
  • apps/mobile/src/app/(public)/auth/reset-password.tsx (3 hunks)
  • apps/mobile/src/app/(public)/auth/sign-in.tsx (2 hunks)
  • apps/mobile/src/app/(public)/auth/sign-up.tsx (1 hunks)
  • apps/mobile/src/app/_layout.tsx (1 hunks)
  • apps/mobile/src/components/ThemeToggle.tsx (1 hunks)
  • apps/mobile/src/components/auth/sign-up-form.tsx (1 hunks)
  • apps/mobile/src/components/dev-tools/tools.tsx (1 hunks)
  • apps/mobile/src/components/header.tsx (1 hunks)
  • apps/mobile/src/components/screen.tsx (1 hunks)
  • apps/mobile/src/components/ui/password.tsx (1 hunks)
  • apps/mobile/src/containers/auth/sign-up.container.tsx (1 hunks)
  • apps/mobile/src/containers/core/HeaderUser.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/mobile/src/components/ThemeToggle.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/mobile/src/app/(public)/auth/sign-up.tsx
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/mobile/src/app/(public)/auth/index.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use object destructuring for method parameters (wrap multiple parameters in an object; define shapes with TypeScript interfaces)
Use authClient.useSession() for session state
Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)
Import UI components via path aliases (e.g., @/src/components/ui/)
Prefer union string/number literals over TypeScript enums
Use export type and import type for types
Don't use non-null assertions (!) or implicit any
Avoid TypeScript namespaces; prefer ESM modules

Files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/dev-tools/tools.tsx
  • apps/mobile/src/components/header.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/password.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Platform.OS and process.env.EXPO_OS for conditional platform logic

Files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/dev-tools/tools.tsx
  • apps/mobile/src/components/header.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/password.tsx
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use React Native primitives (View, Text, etc.) instead of HTML elements
Use Tailwind responsive prefixes (sm:, md:, lg:) and NativeWind responsive utilities
Use web: prefix for web-only styles; use conditional rendering for native-only components
Use IconSymbol to map SF Symbols on iOS to Material Icons on other platforms
Web a11y: ensure buttons have type, anchors are valid, SVGs/iframes have title, html has lang, inputs have valid autocomplete, ARIA roles/props/values are valid

Files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/dev-tools/tools.tsx
  • apps/mobile/src/components/header.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/password.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Don't use the arguments object
Use for...of instead of Array.forEach
Use arrow functions instead of function expressions
Use Date.now() instead of new Date().getTime()
Use .flatMap() instead of map().flat() when possible
Prefer regex literals over RegExp constructor when possible
Use concise optional chaining instead of chained logical expressions
Always provide radix to parseInt()
Don't use var; use const/let and prefer const when single assignment
Use template literals over string concatenation
Use strict equality (===/!==)
Don't use console or debugger in committed code
Prevent import cycles and avoid namespace imports
Don't hardcode secrets (API keys/tokens) in source
Don't have unused vars, params, imports, labels, or private members
Avoid await inside loops; ensure Promise-like statements are handled
Disallow bitwise operators and comma operator
Ensure switch statements are exhaustive; default clause comes last; no fallthrough or duplicate cases
Use object spread over Object.assign when creating new objects
Prefer Array.isArray over instanceof Array; use at() instead of integer index access where appropriate
No unreachable code, duplicate conditions, or assignments in conditionals
Always use radix with parseInt; prefer Number.isNaN/Number.isFinite over globals
Disallow using global eval, with, var, and unsafe negation
Ensure typeof comparisons use valid strings; getters return values; constructors don’t return values

Files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/dev-tools/tools.tsx
  • apps/mobile/src/components/header.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/password.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{tsx,jsx}: Provide key props in lists; avoid array index as key
Don't define React components inside other components
Don't use event handlers on non-interactive elements; make interactive roles focusable
Don't use both children and dangerouslySetInnerHTML; avoid dangerous JSX props
Use <>...</> instead of ...

Files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/containers/auth/sign-up.container.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
  • apps/mobile/src/components/dev-tools/tools.tsx
  • apps/mobile/src/components/header.tsx
  • apps/mobile/src/components/auth/sign-up-form.tsx
  • apps/mobile/src/components/ui/password.tsx
🧠 Learnings (11)
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use React Native primitives (View, Text, etc.) instead of HTML elements

Applied to files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/components/screen.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/components/ui/password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/containers/**/*.container.tsx : Create containers in src/containers/ mirroring Expo Router paths (e.g., admin/add-user.container.tsx)

Applied to files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use authClient.useSession() for session state

Applied to files:

  • apps/mobile/src/app/(public)/auth/sign-in.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/app/**/_layout.{ts,tsx} : Ensure global.css is imported in the app entry layout for NativeWind setup

Applied to files:

  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
  • apps/mobile/src/app/_layout.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/app/** : Expo Router: keep all routes under src/app with route groups (e.g., (auth), (tabs))

Applied to files:

  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/reset-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Follow Expo Router file-based routing conventions for navigation structure

Applied to files:

  • apps/mobile/src/app/(public)/auth/_layout.tsx
  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{tsx,ts} : Use Platform.OS and process.env.EXPO_OS for conditional platform logic

Applied to files:

  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{tsx,jsx} : Don't define React components inside other components

Applied to files:

  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Import UI components via path aliases (e.g., @/src/components/ui/)

Applied to files:

  • apps/mobile/src/app/(public)/auth/forgot-password.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use web: prefix for web-only styles; use conditional rendering for native-only components

Applied to files:

  • apps/mobile/src/app/_layout.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)

Applied to files:

  • apps/mobile/src/components/ui/password.tsx
🧬 Code graph analysis (7)
apps/mobile/src/app/(public)/auth/_layout.tsx (3)
apps/mobile/src/lib/auth-client.ts (1)
  • authClient (21-28)
apps/frontend/src/app/(public)/(protected)/home/(tabs)/index.tsx (2)
  • HomeScreen (7-18)
  • router (12-12)
apps/frontend/src/app/(public)/auth/sign-in.tsx (1)
  • SignIn (21-180)
apps/mobile/src/containers/auth/sign-up.container.tsx (2)
apps/mobile/src/components/auth/sign-up-success-card.tsx (1)
  • SignUpSuccessCard (16-78)
apps/mobile/src/models/admin/user-detail.model.ts (1)
  • handleResendVerification (152-157)
apps/mobile/src/components/screen.tsx (2)
apps/mobile/src/hooks/useColorScheme.ts (1)
  • useColorScheme (9-64)
apps/mobile/src/lib/utils.ts (1)
  • cn (4-6)
apps/mobile/src/app/(public)/auth/forgot-password.tsx (7)
apps/mobile/src/components/screen.tsx (1)
  • Screen (14-42)
apps/mobile/src/components/header.tsx (1)
  • Header (7-19)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/input.tsx (1)
  • Input (25-25)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/models/admin/user-detail.model.ts (1)
  • handleResetPassword (184-189)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
apps/mobile/src/components/dev-tools/tools.tsx (4)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ThemeToggle.tsx (1)
  • ThemeToggle (7-31)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/lib/icons/MoonStar.tsx (1)
  • MoonStar (5-5)
apps/mobile/src/components/header.tsx (2)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/components/ui/icon.tsx (1)
  • Icon (54-54)
apps/mobile/src/components/auth/sign-up-form.tsx (6)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/input.tsx (1)
  • Input (25-25)
apps/mobile/src/components/ui/password.tsx (1)
  • PasswordInput (51-51)
apps/mobile/src/components/ui/progress.tsx (1)
  • Progress (32-32)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (21)
apps/mobile/src/app/(public)/auth/_layout.tsx (1)

15-23: Auth stack now correctly registers all relevant routes, including reset-password.

Registering index, sign-in, sign-up, forgot-password, and reset-password ensures each auth screen has a defined route and fixes the earlier reset-password navigation gap; headerShown: false also matches a pattern where screens own their headers.

Please double-check that each screen file under this layout (index.tsx, sign-in.tsx, sign-up.tsx, forgot-password.tsx, reset-password.tsx) matches these route names so Expo Router resolves them as expected. Based on learnings, this aligns with the Expo Router file-based routing conventions.

apps/mobile/package.json (1)

28-30: All dependencies verified as legitimate with no known security vulnerabilities or React 19 incompatibilities.

The npm registry confirms all four packages are legitimate:

  • @react-buoy/core, @react-buoy/env, @react-buoy/network (all ^1.5.18): Published 7 hours ago by lovesworking; maintained at https://github.com/LovesWorking/react-native-buoy with Proprietary licenses. No known CVEs. These are React Native dev tools with no explicit React 19 peerDependency, but React Native 0.81.5 is compatible with React 19.1.0.

  • expo-clipboard (~8.0.7): MIT licensed, maintained by Expo team members. Well-established project (142 versions). React Native 0.81.5 is compatible (Expo SDK 54 targets RN 0.81). No known CVEs.

Version constraints (^ for @react-buoy, ~ for expo-clipboard) reflect standard npm practices—both are reasonable choices.

The PR's "Possible security concern" label does not appear substantiated by package analysis.

apps/mobile/src/components/header.tsx (1)

7-19: LGTM! Clean and focused header component.

The Header component is well-structured with appropriate conditional rendering based on navigation history. The use of router.canGoBack() ensures the back button only appears when navigation history exists.

apps/mobile/src/containers/auth/sign-up.container.tsx (2)

35-35: LGTM! Layout adjustment adds consistent padding.

The addition of p-6 padding and class reordering improves visual consistency with other auth screens.


42-43: LGTM! Prop reordering is cosmetic.

Reordering props doesn't affect functionality since JavaScript object properties are unordered. The component interface remains unchanged.

apps/mobile/src/components/screen.tsx (1)

32-37: Verify intentional flex override behavior.

The style array [{ flex: 1 }, keyboardAvoidingViewProps?.style] allows consumers to override flex: 1 by passing style props. This could break the Screen's layout contract if consumers accidentally override the flex behavior.

If this is intentional (to allow full style control), consider documenting this behavior. Otherwise, apply this diff to prevent flex override:

       <KeyboardAvoidingView
         behavior={isIos ? "padding" : "height"}
         keyboardVerticalOffset={keyboardOffset}
         {...keyboardAvoidingViewProps}
-        style={[{ flex: 1 }, keyboardAvoidingViewProps?.style]}
+        style={{ flex: 1, ...keyboardAvoidingViewProps?.style }}
       >

Note: Using object spread { flex: 1, ...style } still allows override but makes the precedence more explicit. To prevent override entirely, you'd need to reverse the order or use StyleSheet.flatten.

apps/mobile/src/components/auth/sign-up-form.tsx (2)

53-68: LGTM! Type alias follows coding guidelines.

Converting from interface to type aligns with the coding guideline to use export type for type definitions.


78-145: LGTM! Clean refactor from Card to View-based layout.

The migration from Card components to View/Text primitives maintains all functionality while following React Native best practices. The form fields, password strength indicator, and navigation links are properly preserved.

apps/mobile/src/app/(public)/auth/forgot-password.tsx (2)

64-107: LGTM! Screen and Header integration is clean.

The refactor from Card-based UI to Screen/Header layout is well-executed. The form maintains all functionality (email input, loading states, navigation) while gaining consistent safe area handling and keyboard avoidance.


4-4: Inconsistent path alias for authClient import.

Line 4 uses @/src/lib/auth-client while other imports (lines 6-7, 13) use the ~/ alias pattern (~/components/, ~/lib/). Mixing path aliases reduces consistency and can confuse developers about the canonical import style.

Apply this diff to use consistent path aliasing:

-import { authClient } from "@/src/lib/auth-client";
+import { authClient } from "~/lib/auth-client";

Verify this path resolves correctly, as other files in the PR use the same pattern.

⛔ Skipped due to learnings
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Import UI components via path aliases (e.g., @/src/components/ui/)
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use authClient.useSession() for session state
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/lib/auth-client.ts : Initialize Better Auth client in src/lib/auth-client.ts with platform-specific plugins
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to tsconfig.json : Configure TypeScript path aliases for @/* from project root
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)
apps/mobile/src/app/(public)/auth/sign-in.tsx (1)

91-161: LGTM! Screen integration and verification flow are well-implemented.

The Screen/Header refactor maintains all sign-in functionality while adding consistent layout and safe area handling. The conditional verification prompt (lines 126-145) provides good UX for unverified users.

apps/mobile/src/app/(public)/auth/reset-password.tsx (4)

28-29: LGTM! Optional chaining prevents native runtime crash.

The use of window.location?.search with optional chaining correctly handles React Native environments where window.location is undefined, preventing the runtime crash mentioned in past review comments.


98-103: LGTM! Loading state uses Screen wrapper.

The loading state is now wrapped in Screen with proper layout, addressing the past review comment about consistent Screen usage across auth flows.


107-154: LGTM! Screen/Header integration and password reset flow are solid.

The refactor to Screen/Header layout maintains all password reset functionality (token validation, password matching, length validation) while providing consistent safe area handling and navigation.


4-4: Inconsistent path alias for authClient import.

Line 4 uses @/src/lib/auth-client while other imports (lines 5-6, 11) use the ~/ alias pattern. This is the same inconsistency seen in sign-in.tsx and forgot-password.tsx.

Apply this diff to use consistent path aliasing:

-import { authClient } from "@/src/lib/auth-client";
+import { authClient } from "~/lib/auth-client";
⛔ Skipped due to learnings
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Import UI components via path aliases (e.g., @/src/components/ui/)
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use authClient.useSession() for session state
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/lib/auth-client.ts : Initialize Better Auth client in src/lib/auth-client.ts with platform-specific plugins
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to tsconfig.json : Configure TypeScript path aliases for @/* from project root
apps/mobile/src/app/_layout.tsx (4)

4-4: LGTM!

The import additions and path updates are appropriate for the new dev tools functionality.

Also applies to: 12-14


19-23: LGTM!

The biome-ignore is appropriately justified for the required Expo Router ErrorBoundary re-export pattern.


25-29: LGTM!

The Platform.select pattern for platform-specific setup is clean and follows the coding guidelines for conditional platform logic.


64-69: LGTM!

The web-specific background class setup is correctly implemented using isomorphic layout effect.

apps/mobile/src/components/dev-tools/tools.tsx (2)

7-18: LGTM!

The ThemeToggleTool component is well-structured with proper object destructuring, safe area handling, and NativeWind styling.


20-30: LGTM!

The customTools export is correctly typed and structured for the @react-buoy/core dev tools API.

Comment thread apps/mobile/src/app/_layout.tsx
import { Link, useRouter } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
import { authClient } from "@/src/lib/auth-client";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Inconsistent path alias for authClient import.

Line 4 uses @/src/lib/auth-client while other imports (lines 5-6, 12) use the ~/ alias pattern. This inconsistency matches the same issue in forgot-password.tsx.

Apply this diff to use consistent path aliasing:

-import { authClient } from "@/src/lib/auth-client";
+import { authClient } from "~/lib/auth-client";
📝 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 { authClient } from "@/src/lib/auth-client";
import { authClient } from "~/lib/auth-client";
🤖 Prompt for AI Agents
In apps/mobile/src/app/(public)/auth/sign-in.tsx around line 4, the import uses
the inconsistent alias "@/src/lib/auth-client"; change it to use the project's
consistent "~/" alias (e.g. "~/lib/auth-client") to match the other imports in
this file (and align with forgot-password.tsx). Update that single import path
so all imports in the file use the same alias pattern.

@@ -1,50 +1,51 @@
import { Eye, EyeOff } from "lucide-react-native";
import * as React from "react";
import { type RefObject, useState } from "react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Replace useState with Legend State observable.

The coding guidelines explicitly state to avoid React hooks and use Legend State observables directly (exception: Better Auth hooks). The useState hook on line 21 violates this guideline.

Based on learnings, the codebase standard is to use Legend State for component state management.

Apply this diff to replace useState with Legend State:

-import { type RefObject, useState } from "react";
+import { type RefObject } from "react";
+import { observable } from "@legendapp/state";
+import { Reactive } from "@legendapp/state/react";

And update the component:

 function PasswordInput({
   className,
   placeholderClassName,
   ...props
 }: PasswordInputProps) {
-  const [showPassword, setShowPassword] = useState(false);
+  const showPassword$ = observable(false);

   const togglePasswordVisibility = () => {
-    setShowPassword(!showPassword);
+    showPassword$.set(!showPassword$.get());
   };

   return (
-    <View className="relative">
+    <Reactive.View className="relative">
       <Input
         className={`pr-10 ${className || ""}`}
         placeholderClassName={placeholderClassName}
-        secureTextEntry={!showPassword}
+        secureTextEntry={() => !showPassword$.get()}
         {...props}
       />
       <Pressable
-        accessibilityLabel={showPassword ? "Hide password" : "Show password"}
+        accessibilityLabel={() => showPassword$.get() ? "Hide password" : "Show password"}
         accessibilityRole="button"
         className="absolute top-0 right-3 flex h-10 native:h-12 web:cursor-pointer items-center justify-center"
         onPress={togglePasswordVisibility}
       >
-        {showPassword ? (
+        {() => showPassword$.get() ? (
           <Icon as={EyeOff} className="h-4 w-4 text-muted-foreground" />
         ) : (
           <Icon as={Eye} className="h-4 w-4 text-muted-foreground" />
-        )}
+        )()}
       </Pressable>
-    </View>
+    </Reactive.View>
   );
 }

Also applies to: 21-21

🤖 Prompt for AI Agents
In apps/mobile/src/components/ui/password.tsx (imports at line 2 and state usage
at line 21), replace the React useState usage with a Legend State observable:
remove useState from the import, add the appropriate Legend State observable
import, replace the useState declaration on line 21 with creation of a Legend
State observable for that piece of state, and update all reads/writes to use the
Legend State observable API (get/set/subscribe or the project-specific
accessors) instead of setState; ensure imports and any cleanup/subscription
follow the project's Legend State patterns.

Comment on lines 12 to 14
interface PasswordInputProps extends Omit<TextInputProps, "secureTextEntry"> {
ref?: React.RefObject<TextInput>;
ref?: RefObject<TextInput>;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use forwardRef for proper ref forwarding.

The ref prop is defined as a regular prop in the interface, but React requires refs to be forwarded using React.forwardRef. The current pattern won't allow parent components to properly access the underlying TextInput instance.

Apply this diff to properly implement ref forwarding:

+import { type RefObject, forwardRef } from "react";

 interface PasswordInputProps extends Omit<TextInputProps, "secureTextEntry"> {
-  ref?: RefObject<TextInput>;
 }

-function PasswordInput({
+const PasswordInput = forwardRef<TextInput, PasswordInputProps>(({
   className,
   placeholderClassName,
   ...props
-}: PasswordInputProps) {
+}, ref) => {
   // ... component body ...
   return (
     <View className="relative">
       <Input
+        ref={ref}
         className={`pr-10 ${className || ""}`}
         placeholderClassName={placeholderClassName}
         secureTextEntry={!showPassword}
         {...props}
       />
       {/* ... rest of component ... */}
     </View>
   );
-}
+});
+
+PasswordInput.displayName = "PasswordInput";

Also applies to: 16-20

🤖 Prompt for AI Agents
In apps/mobile/src/components/ui/password.tsx around lines 12-14 and 16-20, the
component currently declares a ref prop on the props interface which prevents
proper React ref forwarding; update the component to use React.forwardRef
instead: remove ref from the props interface (use Omit to exclude
"secureTextEntry" only), change the component to a forwardRef functional
component with the ref typed as Ref<TextInput> (or RefObject<TextInput> as
appropriate), accept (props, ref) in the forwardRef callback, pass that ref
through to the underlying TextInput element, and export the forwarded component
so parent components can access the TextInput instance.

Comment on lines +144 to +160
{!(isPending || data) && (
<Button
onPress={() => bottomSheetModalRef.current?.present()}
variant={"ghost"}
>
<Menu className="text-base text-foreground" />
</Button>
)}
{data && !isPending && (
<Pressable onPress={() => bottomSheetModalRef.current?.present()}>
<Avatar alt={data?.user.name ?? "User"}>
<AvatarImage source={{ uri: data?.user?.image ?? undefined }} />
<AvatarFallback>
<Text>{data?.user?.name.charAt(0) ?? "U"}</Text>
</AvatarFallback>
</Avatar>
</Pressable>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Wrapping the mobile view in a raw <div> crashes React Native because <div> is not a valid native component; the UI will fail to render on devices where the web DOM is unavailable. [possible bug]

Severity Level: Critical 🚨

Suggested change
{!(isPending || data) && (
<Button
onPress={() => bottomSheetModalRef.current?.present()}
variant={"ghost"}
>
<Menu className="text-base text-foreground" />
</Button>
)}
{data && !isPending && (
<Pressable onPress={() => bottomSheetModalRef.current?.present()}>
<Avatar alt={data?.user.name ?? "User"}>
<AvatarImage source={{ uri: data?.user?.image ?? undefined }} />
<AvatarFallback>
<Text>{data?.user?.name.charAt(0) ?? "U"}</Text>
</AvatarFallback>
</Avatar>
</Pressable>
<View className="flex md:hidden">
{!(isPending || data) && (
<Button
onPress={() => bottomSheetModalRef.current?.present()}
variant={"ghost"}
>
<Menu className="text-base text-foreground" />
</Button>
)}
{data && !isPending && (
<Pressable onPress={() => bottomSheetModalRef.current?.present()}>
<Avatar alt={data?.user.name ?? "User"}>
<AvatarImage source={{ uri: data?.user?.image ?? undefined }} />
<AvatarFallback>
<Text>{data?.user?.name.charAt(0) ?? "U"}</Text>
</AvatarFallback>
</Avatar>
</Pressable>
)}
</View>
Why it matters? ⭐

The PR is strictly for a React Native screen; <div> is not a supported native component and would crash on device builds. Swapping it for <View> like the rest of the file keeps the layout functional.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/mobile/src/containers/core/HeaderUser.tsx
**Line:** 144:160
**Comment:**
	*Possible Bug: Wrapping the mobile view in a raw `<div>` crashes React Native because `<div>` is not a valid native component; the UI will fail to render on devices where the web DOM is unavailable.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai

codeant-ai Bot commented Nov 26, 2025

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

…tagging, and refactor mobile header UI with FontAwesome prop remapping.
@coderabbitai coderabbitai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Nov 26, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
apps/api/src/lib/auth.ts (1)

21-29: Overly permissive development trusted origins remain unaddressed.

The wildcard patterns (especially exp://*/* and exp://172.*.*.*:*/*) are too broad. The 172.* pattern includes public IP ranges (172.0-15 and 172.32-255 are not private). This was flagged in previous reviews.

Consider restricting to explicit private ranges or verify that Better Auth supports this wildcard syntax as intended.

apps/api/src/env.ts (1)

119-124: z.flattenError is not a valid Zod API - use parsed.error.flatten() instead.

This will throw TypeError: z.flattenError is not a function when validation fails, preventing the formatted error output and clean exit.

Apply this diff:

 if (!parsed.success) {
   logger.error("❌ Invalid environment variables:");
-  logger.error(
-    JSON.stringify(z.flattenError(parsed.error).fieldErrors, null, 2)
-  );
+  logger.error(
+    JSON.stringify(parsed.error.flatten().fieldErrors, null, 2)
+  );
   process.exit(1);
 }
apps/mobile/src/containers/core/HeaderUser.tsx (1)

142-161: Mobile header UI is completely disabled.

The entire mobile view section is commented out, which means mobile users have no visible header controls—no login button, no user avatar, and no way to access the menu that triggers the BottomSheetModal. This breaks the mobile user experience.

If this is work-in-progress code that should not be in this PR, remove it. If the mobile view should be active, uncomment and fix the <div> element on line 142 to use <View> instead (React Native primitive per coding guidelines).

If you intend to enable the mobile view, apply this fix:

-        {/* <div className="flex md:hidden">
+        <View className="flex md:hidden">
           {!(isPending || data) && (
             <Button
               onPress={() => bottomSheetModalRef.current?.present()}
               variant={"ghost"}
             >
               <Menu className="text-base text-foreground" />
             </Button>
           )}
           {data && !isPending && (
             <Pressable onPress={() => bottomSheetModalRef.current?.present()}>
               <Avatar alt={data?.user.name ?? "User"}>
                 <AvatarImage source={{ uri: data?.user?.image ?? undefined }} />
                 <AvatarFallback>
                   <Text>{data?.user?.name.charAt(0) ?? "U"}</Text>
                 </AvatarFallback>
               </Avatar>
             </Pressable>
           )}
-        </div> */}
+        </View>
🧹 Nitpick comments (1)
apps/api/src/env.ts (1)

35-38: SMTP_SECURE transform returns undefined for non-"true" values when the field is present.

The transform (s) => s === "true" returns false for any non-"true" string, but when .optional() is chained after .transform(), an empty string or other falsy value still gets transformed rather than treated as absent. This is likely fine for boolean semantics, but consider using .default(false) if you want explicit false when the env var is set but not "true".

     SMTP_SECURE: z
       .string()
       .transform((s) => s === "true")
-      .optional(),
+      .default("false")
+      .transform((s) => s === "true"),

Or keep as-is if the current behavior is intentional.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a5f887 and 8dca14b.

📒 Files selected for processing (8)
  • .github/workflows/docker-build.yml (1 hunks)
  • apps/api/src/env.test.ts (1 hunks)
  • apps/api/src/env.ts (1 hunks)
  • apps/api/src/lib/auth.ts (2 hunks)
  • apps/mobile/src/app/(public)/auth/index.tsx (1 hunks)
  • apps/mobile/src/app/_layout.tsx (1 hunks)
  • apps/mobile/src/containers/core/HeaderUser.tsx (1 hunks)
  • apps/mobile/src/utils/remap-props.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/mobile/src/app/(public)/auth/index.tsx
  • apps/mobile/src/app/_layout.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use object destructuring for method parameters (wrap multiple parameters in an object; define shapes with TypeScript interfaces)
Use authClient.useSession() for session state
Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)
Import UI components via path aliases (e.g., @/src/components/ui/)
Prefer union string/number literals over TypeScript enums
Use export type and import type for types
Don't use non-null assertions (!) or implicit any
Avoid TypeScript namespaces; prefer ESM modules

Files:

  • apps/api/src/lib/auth.ts
  • apps/api/src/env.test.ts
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/utils/remap-props.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Platform.OS and process.env.EXPO_OS for conditional platform logic

Files:

  • apps/api/src/lib/auth.ts
  • apps/api/src/env.test.ts
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/utils/remap-props.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Don't use the arguments object
Use for...of instead of Array.forEach
Use arrow functions instead of function expressions
Use Date.now() instead of new Date().getTime()
Use .flatMap() instead of map().flat() when possible
Prefer regex literals over RegExp constructor when possible
Use concise optional chaining instead of chained logical expressions
Always provide radix to parseInt()
Don't use var; use const/let and prefer const when single assignment
Use template literals over string concatenation
Use strict equality (===/!==)
Don't use console or debugger in committed code
Prevent import cycles and avoid namespace imports
Don't hardcode secrets (API keys/tokens) in source
Don't have unused vars, params, imports, labels, or private members
Avoid await inside loops; ensure Promise-like statements are handled
Disallow bitwise operators and comma operator
Ensure switch statements are exhaustive; default clause comes last; no fallthrough or duplicate cases
Use object spread over Object.assign when creating new objects
Prefer Array.isArray over instanceof Array; use at() instead of integer index access where appropriate
No unreachable code, duplicate conditions, or assignments in conditionals
Always use radix with parseInt; prefer Number.isNaN/Number.isFinite over globals
Disallow using global eval, with, var, and unsafe negation
Ensure typeof comparisons use valid strings; getters return values; constructors don’t return values

Files:

  • apps/api/src/lib/auth.ts
  • apps/api/src/env.test.ts
  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/api/src/env.ts
  • apps/mobile/src/utils/remap-props.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Don't nest describe() blocks too deeply; don't use focused/disabled tests; ensure expect is inside it()

Files:

  • apps/api/src/env.test.ts
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use React Native primitives (View, Text, etc.) instead of HTML elements
Use Tailwind responsive prefixes (sm:, md:, lg:) and NativeWind responsive utilities
Use web: prefix for web-only styles; use conditional rendering for native-only components
Use IconSymbol to map SF Symbols on iOS to Material Icons on other platforms
Web a11y: ensure buttons have type, anchors are valid, SVGs/iframes have title, html has lang, inputs have valid autocomplete, ARIA roles/props/values are valid

Files:

  • apps/mobile/src/containers/core/HeaderUser.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{tsx,jsx}: Provide key props in lists; avoid array index as key
Don't define React components inside other components
Don't use event handlers on non-interactive elements; make interactive roles focusable
Don't use both children and dangerouslySetInnerHTML; avoid dangerous JSX props
Use <>...</> instead of ...

Files:

  • apps/mobile/src/containers/core/HeaderUser.tsx
🧠 Learnings (9)
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/lib/auth-client.ts : Initialize Better Auth client in src/lib/auth-client.ts with platform-specific plugins

Applied to files:

  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Use authClient.useSession() for session state

Applied to files:

  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't hardcode secrets (API keys/tokens) in source

Applied to files:

  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{tsx,ts} : Use Platform.OS and process.env.EXPO_OS for conditional platform logic

Applied to files:

  • apps/api/src/lib/auth.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use React Native primitives (View, Text, etc.) instead of HTML elements

Applied to files:

  • apps/mobile/src/containers/core/HeaderUser.tsx
  • apps/mobile/src/utils/remap-props.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{tsx,jsx} : Use <>...</> instead of <Fragment>...</Fragment>

Applied to files:

  • apps/mobile/src/containers/core/HeaderUser.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.{ts,tsx} : Avoid React hooks; use Legend State observables directly (exception: Better Auth hooks)

Applied to files:

  • apps/mobile/src/containers/core/HeaderUser.tsx
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use NativeWind with className and Tailwind utilities for component styling

Applied to files:

  • apps/mobile/src/utils/remap-props.ts
📚 Learning: 2025-09-03T13:07:10.649Z
Learnt from: CR
Repo: fyndx/universal-starter PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T13:07:10.649Z
Learning: Applies to **/*.tsx : Use IconSymbol to map SF Symbols on iOS to Material Icons on other platforms

Applied to files:

  • apps/mobile/src/utils/remap-props.ts
🧬 Code graph analysis (5)
apps/api/src/lib/auth.ts (2)
apps/api/src/env.ts (1)
  • env (127-127)
apps/api/src/infra/db.ts (1)
  • prisma (2-2)
apps/api/src/env.test.ts (1)
apps/api/src/env.ts (1)
  • envSchema (4-115)
apps/mobile/src/containers/core/HeaderUser.tsx (9)
apps/mobile/src/lib/icons/Shield.tsx (1)
  • Shield (5-5)
apps/mobile/src/lib/auth-client.ts (1)
  • authClient (21-28)
apps/mobile/src/components/ui/bottom-sheet/index.tsx (2)
  • BottomSheetModal (46-46)
  • BottomSheetView (50-50)
apps/mobile/src/models/core/impersonation.model.ts (1)
  • impersonationModel$ (80-80)
apps/mobile/src/components/ui/activity-indicator.tsx (1)
  • ActivityIndicator (12-12)
apps/mobile/src/components/ui/dropdown-menu.tsx (6)
  • DropdownMenu (259-259)
  • DropdownMenuTrigger (273-273)
  • DropdownMenuContent (261-261)
  • DropdownMenuLabel (264-264)
  • DropdownMenuSeparator (268-268)
  • DropdownMenuItem (263-263)
apps/mobile/src/components/ui/avatar.tsx (3)
  • Avatar (47-47)
  • AvatarImage (47-47)
  • AvatarFallback (47-47)
apps/mobile/src/components/ui/text.tsx (1)
  • Text (89-89)
apps/mobile/src/components/ui/button.tsx (1)
  • Button (107-107)
apps/api/src/env.ts (1)
packages/logger/src/index.ts (1)
  • logger (26-26)
apps/mobile/src/utils/remap-props.ts (1)
apps/mobile/src/components/ui/bottom-sheet/index.tsx (1)
  • BottomSheetModal (46-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (9)
apps/api/src/lib/auth.ts (1)

34-76: Good implementation of conditional social provider configuration.

The conditional spread pattern correctly omits providers when credentials are missing, preventing Better Auth from registering providers with invalid empty credentials. This addresses the previous review concern and follows best practices.

apps/api/src/env.test.ts (1)

4-86: Comprehensive test coverage for environment schema validation.

The tests appropriately cover:

  • Base configuration without social providers
  • Fully configured single provider
  • Incomplete provider configurations (both missing ID and missing Secret)
  • Multiple providers with full and partial configurations

The assertions correctly verify both success/failure states and error messages.

.github/workflows/docker-build.yml (2)

76-78: Verify intent: latest tag generation is now disabled.

The type=raw,value=latest,enable={{is_default_branch}} line is commented out, which means pushes to main will no longer produce a latest tag. If this is intentional to prevent accidental deployments, consider adding a comment explaining why. Otherwise, this may break deployment workflows that rely on pulling the latest tag.


116-117: Same concern for Workers image: latest tag disabled.

Consistent with the API image changes, but ensure downstream deployments don't depend on the latest tag for the workers image.

apps/api/src/env.ts (1)

64-115: Well-designed cross-field validation for social provider credentials.

The superRefine implementation correctly enforces that each OAuth provider must have both CLIENT_ID and CLIENT_SECRET set together, or neither. The error messages are clear and point to the specific missing field. This addresses the previous review concern about partial provider configurations passing validation.

apps/mobile/src/utils/remap-props.ts (2)

1-1: LGTM!

The FontAwesome import is properly structured and aligns with the PR's goal of adding icon support for social login UI.


5-9: LGTM!

The BottomSheetModal remapProps configuration is correct, and the formatting updates improve consistency with the newly added FontAwesome block.

apps/mobile/src/containers/core/HeaderUser.tsx (2)

163-224: Bottom sheet implementation looks correct.

The bottom sheet modal for mobile correctly uses React Native primitives and maintains consistent navigation to /auth (line 217), aligning with the desktop flow (line 132) and the new social login landing page introduced in this PR.


39-43: This review comment is incorrect. The useEffect cannot be replaced with Legend State reactive patterns.

The useEffect (lines 39-43) is necessary and appropriate for syncing external Better Auth session data into the Legend State observable. Legend State in this codebase provides only basic observable() state containers and observer components—it does not expose computed(), watch(), or reactive binding mechanisms.

The useEffect with [data] dependency is the correct pattern for:

  1. Reacting to external data changes (Better Auth session updates)
  2. Syncing that external state into internal Legend State
  3. Tracking dependency changes over component lifecycle

Removing this effect would break impersonation state synchronization when the session changes. The useMount hook used elsewhere triggers initial fetches, not external data syncing—these serve different lifecycle purposes.

Comment on lines +11 to 14
remapProps(FontAwesome, {
className: "style",
color: "color",
});

Copy link
Copy Markdown

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
# First, let's read the remap-props.ts file to understand context
cat -n apps/mobile/src/utils/remap-props.ts

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

     1	import { FontAwesome } from "@expo/vector-icons";
     2	import { remapProps } from "nativewind";
     3	import { BottomSheetModal } from "~/components/ui/bottom-sheet";
     4	
     5	remapProps(BottomSheetModal, {
     6	  className: "style",
     7	  handleClassName: "handleStyle",
     8	  containerClassName: "containerStyle",
     9	});
    10	
    11	remapProps(FontAwesome, {
    12	  className: "style",
    13	  color: "color",
    14	});

</shell_result>


Script executed:

#!/bin/bash
# Search for remapProps function definition and usage
rg -n "remapProps" -A 3 -B 3 --type ts --type tsx

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 2

stderr:

unrecognized file type: tsx

</shell_result>


Script executed:

#!/bin/bash
# Search for FontAwesome imports and usage patterns
rg -n "FontAwesome" -A 2 -B 2 --type ts --type tsx

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 2

stderr:

unrecognized file type: tsx

</shell_result>


Script executed:

#!/bin/bash
# Search for FontAwesome usage in TypeScript files (without tsx flag issue)
rg -n "FontAwesome" -A 2 -B 2 --type ts

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/utils/remap-props.ts:1:import { FontAwesome } from "@expo/vector-icons";
apps/mobile/src/utils/remap-props.ts-2-import { remapProps } from "nativewind";
apps/mobile/src/utils/remap-props.ts-3-import { BottomSheetModal } from "~/components/ui/bottom-sheet";
--
apps/mobile/src/utils/remap-props.ts-9-});
apps/mobile/src/utils/remap-props.ts-10-
apps/mobile/src/utils/remap-props.ts:11:remapProps(FontAwesome, {
apps/mobile/src/utils/remap-props.ts-12-  className: "style",
apps/mobile/src/utils/remap-props.ts-13-  color: "color",
--
apps/mobile/src/app/(public)/auth/index.tsx:1:import { FontAwesome } from "@expo/vector-icons";
apps/mobile/src/app/(public)/auth/index.tsx-2-import { useRouter } from "expo-router";
apps/mobile/src/app/(public)/auth/index.tsx-3-import { View } from "react-native";
--
apps/mobile/src/app/(public)/auth/index.tsx-45-          >
apps/mobile/src/app/(public)/auth/index.tsx-46-            <Text>
apps/mobile/src/app/(public)/auth/index.tsx:47:              <FontAwesome name="google" size={20} />
apps/mobile/src/app/(public)/auth/index.tsx-48-            </Text>
apps/mobile/src/app/(public)/auth/index.tsx-49-            <Text>Continue with Google</Text>
--
apps/mobile/src/app/(public)/auth/index.tsx-55-          >
apps/mobile/src/app/(public)/auth/index.tsx-56-            <Text>
apps/mobile/src/app/(public)/auth/index.tsx:57:              <FontAwesome name="facebook" size={20} />
apps/mobile/src/app/(public)/auth/index.tsx-58-            </Text>
apps/mobile/src/app/(public)/auth/index.tsx-59-            <Text>Continue with Facebook</Text>
--
apps/mobile/src/app/(public)/auth/index.tsx-65-          >
apps/mobile/src/app/(public)/auth/index.tsx-66-            <Text>
apps/mobile/src/app/(public)/auth/index.tsx:67:              <FontAwesome name="github" size={20} />
apps/mobile/src/app/(public)/auth/index.tsx-68-            </Text>
apps/mobile/src/app/(public)/auth/index.tsx-69-            <Text>Continue with Github</Text>
--
apps/mobile/src/app/(public)/auth/index.tsx-75-          >
apps/mobile/src/app/(public)/auth/index.tsx-76-            <Text>
apps/mobile/src/app/(public)/auth/index.tsx:77:              <FontAwesome name="apple" size={20} />
apps/mobile/src/app/(public)/auth/index.tsx-78-            </Text>
apps/mobile/src/app/(public)/auth/index.tsx-79-            <Text>Continue with Apple</Text>
--
apps/mobile/src/app/(public)/auth/index.tsx-85-          >
apps/mobile/src/app/(public)/auth/index.tsx-86-            <Text>
apps/mobile/src/app/(public)/auth/index.tsx:87:              <FontAwesome name="linkedin" size={20} />
apps/mobile/src/app/(public)/auth/index.tsx-88-            </Text>
apps/mobile/src/app/(public)/auth/index.tsx-89-            <Text>Continue with Linkedin</Text>

</shell_result>


Script executed:

#!/bin/bash
# Search for remapProps patterns
rg -n "remapProps" -A 3 -B 1 --type ts

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/utils/remap-props.ts-1-import { FontAwesome } from "@expo/vector-icons";
apps/mobile/src/utils/remap-props.ts:2:import { remapProps } from "nativewind";
apps/mobile/src/utils/remap-props.ts-3-import { BottomSheetModal } from "~/components/ui/bottom-sheet";
apps/mobile/src/utils/remap-props.ts-4-
apps/mobile/src/utils/remap-props.ts:5:remapProps(BottomSheetModal, {
apps/mobile/src/utils/remap-props.ts-6-  className: "style",
apps/mobile/src/utils/remap-props.ts-7-  handleClassName: "handleStyle",
apps/mobile/src/utils/remap-props.ts-8-  containerClassName: "containerStyle",
--
apps/mobile/src/utils/remap-props.ts-10-
apps/mobile/src/utils/remap-props.ts:11:remapProps(FontAwesome, {
apps/mobile/src/utils/remap-props.ts-12-  className: "style",
apps/mobile/src/utils/remap-props.ts-13-  color: "color",
apps/mobile/src/utils/remap-props.ts-14-});

</shell_result>


Script executed:

#!/bin/bash
# Search for color prop usage on FontAwesome or similar icon components
rg -n "color\s*=" -A 1 -B 1 --type ts | head -50

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx-26-					tabBarIcon: ({ color }) => (
apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx:27:						<IconSymbol size={28} name="house.fill" color={color} />
apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx-28-					),
--
apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx-35-					tabBarIcon: ({ color }) => (
apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx:36:						<IconSymbol size={28} name="paperplane.fill" color={color} />
apps/mobile/src/app/(public)/(protected)/home/(tabs)/_layout.tsx-37-					),
--
apps/mobile/src/components/layouts/default/Footer.tsx-35-								<View className="w-8 h-8 bg-muted rounded-lg items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:36:									<AntDesign name="twitter" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-37-								</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-40-								<View className="w-8 h-8 bg-muted rounded-lg items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:41:									<AntDesign name="github" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-42-								</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-45-								<View className="w-8 h-8 bg-muted rounded-lg items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:46:									<AntDesign name="linkedin-square" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-47-								</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-50-								<View className="w-8 h-8 bg-muted rounded-lg items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:51:									<Feather name="globe" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-52-								</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-128-							<View className="w-6 h-6 items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:129:								<Feather name="globe" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-130-							</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-133-							<View className="w-6 h-6 items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:134:								<MaterialIcons name="email" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-135-							</View>
--
apps/mobile/src/components/layouts/default/Footer.tsx-138-							<View className="w-6 h-6 items-center justify-center">
apps/mobile/src/components/layouts/default/Footer.tsx:139:								<MaterialIcons name="help-outline" size={16} color="#6B7280" />
apps/mobile/src/components/layouts/default/Footer.tsx-140-							</View>
--
apps/mobile/src/components/ui/IconSymbol.tsx-39-}) {
apps/mobile/src/components/ui/IconSymbol.tsx:40:  return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
apps/mobile/src/components/ui/IconSymbol.tsx-41-}
--
apps/mobile/src/components/ui/activity-indicator.tsx-8-	const { colorScheme } = useColorScheme();
apps/mobile/src/components/ui/activity-indicator.tsx:9:	return <RNActivityIndicator color={NAV_THEME[colorScheme].text} {...props} />;
apps/mobile/src/components/ui/activity-indicator.tsx-10-}

</shell_result>


Script executed:

#!/bin/bash
# Check if there are any comments or documentation about remapProps usage
rg -n "remapProps|NativeWind" -B 2 -A 5 --type ts | head -60

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/utils/remap-props.ts-1-import { FontAwesome } from "@expo/vector-icons";
apps/mobile/src/utils/remap-props.ts:2:import { remapProps } from "nativewind";
apps/mobile/src/utils/remap-props.ts-3-import { BottomSheetModal } from "~/components/ui/bottom-sheet";
apps/mobile/src/utils/remap-props.ts-4-
apps/mobile/src/utils/remap-props.ts:5:remapProps(BottomSheetModal, {
apps/mobile/src/utils/remap-props.ts-6-  className: "style",
apps/mobile/src/utils/remap-props.ts-7-  handleClassName: "handleStyle",
apps/mobile/src/utils/remap-props.ts-8-  containerClassName: "containerStyle",
apps/mobile/src/utils/remap-props.ts-9-});
apps/mobile/src/utils/remap-props.ts-10-
apps/mobile/src/utils/remap-props.ts:11:remapProps(FontAwesome, {
apps/mobile/src/utils/remap-props.ts-12-  className: "style",
apps/mobile/src/utils/remap-props.ts-13-  color: "color",
apps/mobile/src/utils/remap-props.ts-14-});
--
apps/mobile/src/components/landing/landing-page.tsx-180-			title: "Lightning Fast Development",
apps/mobile/src/components/landing/landing-page.tsx-181-			description:
apps/mobile/src/components/landing/landing-page.tsx:182:				"Built with Expo Router, Legend State, and NativeWind for rapid development and optimal performance.",
apps/mobile/src/components/landing/landing-page.tsx-183-		},
apps/mobile/src/components/landing/landing-page.tsx-184-		{
apps/mobile/src/components/landing/landing-page.tsx-185-			id: "production-ready",
apps/mobile/src/components/landing/landing-page.tsx-186-			icon: Shield,
apps/mobile/src/components/landing/landing-page.tsx-187-			title: "Production Ready",
--
apps/mobile/src/components/landing/landing-page.tsx-388-			avatar: "👩‍🎨",
apps/mobile/src/components/landing/landing-page.tsx-389-			quote:
apps/mobile/src/components/landing/landing-page.tsx:390:				"The integration of Legend State and NativeWind makes state management and styling a breeze. Highly recommended!",
apps/mobile/src/components/landing/landing-page.tsx-391-			rating: 5,
apps/mobile/src/components/landing/landing-page.tsx-392-		},
apps/mobile/src/components/landing/landing-page.tsx-393-	];
apps/mobile/src/components/landing/landing-page.tsx-394-
apps/mobile/src/components/landing/landing-page.tsx-395-	return (
--
apps/mobile/src/components/landing/landing-page.tsx-480-			question: "Can I customize the UI components and styling?",
apps/mobile/src/components/landing/landing-page.tsx-481-			answer:
apps/mobile/src/components/landing/landing-page.tsx:482:				"Absolutely! The UI is built with NativeWind (Tailwind for React Native) and react-native-reusables, making it easy to customize colors, spacing, and components to match your brand.",
apps/mobile/src/components/landing/landing-page.tsx-483-		},
apps/mobile/src/components/landing/landing-page.tsx-484-		{
apps/mobile/src/components/landing/landing-page.tsx-485-			id: "support",
apps/mobile/src/components/landing/landing-page.tsx-486-			question: "Is there ongoing support and updates?",
apps/mobile/src/components/landing/landing-page.tsx-487-			answer:

</shell_result>


Script executed:

#!/bin/bash
# Check if color prop is ever passed with className pattern
rg -n "color\s*=\s*['\"].*(?:text-|bg-)" --type ts

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 1

</shell_result>


Script executed:

#!/bin/bash
# Look for any NativeWind or remapProps related comments/documentation
rg -n "NativeWind|remap" --type ts --type js -B 2 -A 2

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/utils/remap-props.ts-1-import { FontAwesome } from "@expo/vector-icons";
apps/mobile/src/utils/remap-props.ts:2:import { remapProps } from "nativewind";
apps/mobile/src/utils/remap-props.ts-3-import { BottomSheetModal } from "~/components/ui/bottom-sheet";
apps/mobile/src/utils/remap-props.ts-4-
apps/mobile/src/utils/remap-props.ts:5:remapProps(BottomSheetModal, {
apps/mobile/src/utils/remap-props.ts-6-  className: "style",
apps/mobile/src/utils/remap-props.ts-7-  handleClassName: "handleStyle",
--
apps/mobile/src/utils/remap-props.ts-9-});
apps/mobile/src/utils/remap-props.ts-10-
apps/mobile/src/utils/remap-props.ts:11:remapProps(FontAwesome, {
apps/mobile/src/utils/remap-props.ts-12-  className: "style",
apps/mobile/src/utils/remap-props.ts-13-  color: "color",
--
apps/mobile/src/components/landing/landing-page.tsx-180-			title: "Lightning Fast Development",
apps/mobile/src/components/landing/landing-page.tsx-181-			description:
apps/mobile/src/components/landing/landing-page.tsx:182:				"Built with Expo Router, Legend State, and NativeWind for rapid development and optimal performance.",
apps/mobile/src/components/landing/landing-page.tsx-183-		},
apps/mobile/src/components/landing/landing-page.tsx-184-		{
--
apps/mobile/src/components/landing/landing-page.tsx-388-			avatar: "👩‍🎨",
apps/mobile/src/components/landing/landing-page.tsx-389-			quote:
apps/mobile/src/components/landing/landing-page.tsx:390:				"The integration of Legend State and NativeWind makes state management and styling a breeze. Highly recommended!",
apps/mobile/src/components/landing/landing-page.tsx-391-			rating: 5,
apps/mobile/src/components/landing/landing-page.tsx-392-		},
--
apps/mobile/src/components/landing/landing-page.tsx-480-			question: "Can I customize the UI components and styling?",
apps/mobile/src/components/landing/landing-page.tsx-481-			answer:
apps/mobile/src/components/landing/landing-page.tsx:482:				"Absolutely! The UI is built with NativeWind (Tailwind for React Native) and react-native-reusables, making it easy to customize colors, spacing, and components to match your brand.",
apps/mobile/src/components/landing/landing-page.tsx-483-		},
apps/mobile/src/components/landing/landing-page.tsx-484-		{
--
apps/mobile/index.ts-1-import "expo-router/entry";
apps/mobile/index.ts-2-
apps/mobile/index.ts:3:import "@/src/utils/remap-props";
apps/mobile/index.ts-4-import "./global.css";
--
apps/mobile/metro.config.js-1-// Learn more https://docs.expo.io/guides/customizing-metro
apps/mobile/metro.config.js-2-const { getDefaultConfig } = require("expo/metro-config");
apps/mobile/metro.config.js:3:const { withNativeWind } = require("nativewind/metro");
apps/mobile/metro.config.js-4-
apps/mobile/metro.config.js-5-/** @type {import('expo/metro-config').MetroConfig} */
apps/mobile/metro.config.js-6-const config = getDefaultConfig(__dirname);
apps/mobile/metro.config.js-7-
apps/mobile/metro.config.js:8:module.exports = withNativeWind(config, { input: "./global.css", inlineRem: 16 });

</shell_result>


Script executed:

#!/bin/bash
# Check package.json for NativeWind version and any related info
cat package.json | head -50

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

{
  "name": "universal-starter",
  "private": true,
  "workspaces": {
    "packages": [
      "apps/*",
      "packages/*"
    ]
  },
  "scripts": {
    "dev": "turbo run dev",
    "build": "turbo run build"
  },
  "devDependencies": {
    "@biomejs/biome": "catalog:devDeps",
    "@types/bun": "latest",
    "lefthook": "^1.12.3",
    "turbo": "^2.6.1",
    "typescript": "catalog:devDeps",
    "ultracite": "6.3.6"
  },
  "engines": {
    "bun": ">=1.0.0"
  },
  "packageManager": "pnpm@10.23.0"
}

</shell_result>


Script executed:

#!/bin/bash
# Search for any other remapProps calls in the codebase
fd -name "*.ts" -o -name "*.js" | xargs grep -l "remapProps" 2>/dev/null

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 123

stderr:

error: Found argument '-n' which wasn't expected, or isn't valid in this context

  If you tried to supply '-n' as a value rather than a flag, use '-- -n'

Usage: fd [OPTIONS] [pattern] [path]...

For more information try '--help'

</shell_result>


Script executed:

#!/bin/bash
# Find where remapProps is defined or used in node_modules check
fd "remap-props" --type f

Repository: fyndx/universal-starter


<shell_result>
Repository: fyndx/universal-starter
Exit code: 0

stdout:

apps/mobile/src/utils/remap-props.ts

</shell_result>


Remove the redundant color: "color" mapping. FontAwesome's color prop accepts direct color values (hex codes, named colors), not Tailwind class strings, so it doesn't need to be included in the remapProps configuration.

 remapProps(FontAwesome, {
   className: "style",
-  color: "color",
 });
📝 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
remapProps(FontAwesome, {
className: "style",
color: "color",
});
remapProps(FontAwesome, {
className: "style",
});
🤖 Prompt for AI Agents
In apps/mobile/src/utils/remap-props.ts around lines 11 to 14, the remapProps
mapping includes a redundant entry mapping color: "color" for FontAwesome;
remove the color key from the remapProps configuration so only className maps to
"style" remains, and ensure the remapProps call is updated accordingly (no other
changes to FontAwesome usage required).

@chakrihacker
chakrihacker merged commit 5714781 into main Nov 26, 2025
5 checks passed
@chakrihacker
chakrihacker deleted the feat/social-login branch November 26, 2025 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Possible security concern Review effort 3/5 size:XL This PR changes 500-999 lines, ignoring generated files size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant