feat: integrate ORPC client and replace Confetti component with SkiaL…#10
Conversation
Reviewer's GuideThis PR integrates an ORPC client for remote procedure calls and refactors the Confetti UI implementation by replacing the previous Skia-based component with react-native-fast-confetti and a dynamic SkiaLoader, while also updating dependencies (expo-video and ORPC packages) and adjusting the auth-client plugin setup. Sequence diagram for dynamic loading of Confetti with SkiaLoadersequenceDiagram
participant User as actor User
participant SignUpSuccessCard
participant SkiaLoader
participant WithSkiaWeb
participant ConfettiBase
User->>SignUpSuccessCard: Triggers sign-up success
SignUpSuccessCard->>SkiaLoader: Requests Confetti component
SkiaLoader->>WithSkiaWeb: Loads Skia web environment
WithSkiaWeb->>ConfettiBase: Dynamically imports Confetti
ConfettiBase-->>WithSkiaWeb: Renders Confetti
WithSkiaWeb-->>SkiaLoader: Returns rendered component
SkiaLoader-->>SignUpSuccessCard: Displays Confetti
Entity relationship diagram for ORPC client and OpenAPI specerDiagram
ORPC_CLIENT ||--o| OPENAPI_LINK: uses
OPENAPI_LINK {
string url
object specJSON
}
ORPC_CLIENT {
OPENAPI_LINK link
}
Class diagram for new ORPC client integrationclassDiagram
class OpenAPILink {
+specJSON: object
+url: string
}
class ORPCClient {
+link: OpenAPILink
}
OpenAPILink <|-- ORPCClient: link
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthroughIntroduces ORPC client initialization using EXPO_PUBLIC_API_URL, updates environment and Expo config, adds ORPC and expo-video dependencies, refactors confetti UI to lazy-load via SkiaLoader and removes confetti-base, adjusts auth client plugin ordering, and adds an Expo plugin entry. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant Env as EXPO_PUBLIC_API_URL
participant Server as API Server (/orpc)
participant ORPC as ORPC Client
App->>Env: Read EXPO_PUBLIC_API_URL (default http://localhost:3000)
App->>Server: GET {apiUrl}/orpc/spec.json
Server-->>App: OpenAPI spec
App->>ORPC: createORPCClient(OpenAPILink(spec, base={apiUrl}/orpc/))
ORPC-->>App: orpc instance ready
sequenceDiagram
participant UI as SignUpSuccessCard
participant Loader as SkiaLoader
participant Module as ~/components/ui/confetti
UI->>Loader: component={() => import(".../confetti")}
Loader-->>UI: WithSkiaWeb wrapper (locateFile '/canvaskit.wasm')
Loader->>Module: Dynamic import
Module-->>Loader: default Confetti component
Loader-->>UI: Render Confetti
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
There was a problem hiding this comment.
Hey @chakrihacker - I've reviewed your changes - here's some feedback:
Blocking issues:
- Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)
- Generic Secret detected (link)
General comments:
- The new SkiaLoader wraps the Confetti component, but confetti.tsx no longer provides a Skia-based implementation—verify web vs. native rendering and consider renaming or refactoring the loader to match the updated export.
- Initializing the ORPC client with a top-level await in src/orpc/index.ts may cause issues in some bundlers—consider fetching the spec and creating the client inside an async function or initialization hook instead of at module load.
- expo-video was added to package.json and app.json but isn’t used in the code—if it’s not needed, removing it will keep dependencies lean.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new SkiaLoader wraps the Confetti component, but confetti.tsx no longer provides a Skia-based implementation—verify web vs. native rendering and consider renaming or refactoring the loader to match the updated export.
- Initializing the ORPC client with a top-level await in src/orpc/index.ts may cause issues in some bundlers—consider fetching the spec and creating the client inside an async function or initialization hook instead of at module load.
- expo-video was added to package.json and app.json but isn’t used in the code—if it’s not needed, removing it will keep dependencies lean.
## Individual Comments
### Comment 1
<location> `src/orpc/index.ts:6` </location>
<code_context>
+
+const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000";
+
+const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json());
+
+const link = new OpenAPILink(specJSON, {
</code_context>
<issue_to_address>
Using top-level await may cause issues in environments that do not support it.
Top-level await is only supported in ES2022 modules and select environments. Refactor to use an async initialization function, or confirm all consumers support top-level await to avoid runtime errors.
</issue_to_address>
### Comment 2
<location> `src/orpc/index.ts:4` </location>
<code_context>
+import { createORPCClient } from "@orpc/client";
+import { OpenAPILink } from "@orpc/openapi-client/fetch";
+
+const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000";
+
+const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json());
</code_context>
<issue_to_address>
Defaulting to localhost for API URL may cause issues in production.
Defaulting to localhost may break production usage. Instead, consider throwing an error or using a safer fallback if EXPO_PUBLIC_API_URL is missing.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000";
=======
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
if (!apiUrl) {
throw new Error(
"EXPO_PUBLIC_API_URL is not set. Please configure the API URL in your environment variables."
);
}
>>>>>>> REPLACE
</suggested_fix>
## Security Issues
### Issue 1
<location> `.env:2` </location>
<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
*Source: gitleaks*
</issue_to_address>
### Issue 2
<location> `.env:2` </location>
<issue_to_address>
**security (generic.secrets.security.detected-generic-secret):** Generic Secret detected
*Source: opengrep*
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; | ||
|
|
||
| const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json()); |
There was a problem hiding this comment.
issue (bug_risk): Using top-level await may cause issues in environments that do not support it.
Top-level await is only supported in ES2022 modules and select environments. Refactor to use an async initialization function, or confirm all consumers support top-level await to avoid runtime errors.
| import { createORPCClient } from "@orpc/client"; | ||
| import { OpenAPILink } from "@orpc/openapi-client/fetch"; | ||
|
|
||
| const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; |
There was a problem hiding this comment.
suggestion (bug_risk): Defaulting to localhost for API URL may cause issues in production.
Defaulting to localhost may break production usage. Instead, consider throwing an error or using a safer fallback if EXPO_PUBLIC_API_URL is missing.
| const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; | |
| const apiUrl = process.env.EXPO_PUBLIC_API_URL; | |
| if (!apiUrl) { | |
| throw new Error( | |
| "EXPO_PUBLIC_API_URL is not set. Please configure the API URL in your environment variables." | |
| ); | |
| } |
| DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcGlfa2V5IjoiMDFKWjdKSlpQRkQzNzROREE2RzA1MUtXRUIiLCJ0ZW5hbnRfaWQiOiJhZDMzYmZkYWM2NzhmNTAxMmE1NzgwODAwYzFmMDZhYmM1OGM3OTM4MjY0MDBiZTBhYzFjYWI0ZGRiYjAyOTg1IiwiaW50ZXJuYWxfc2VjcmV0IjoiYzRmNGQ1MWYtOTcyMy00MWNiLTllMWQtOWMwMTI5YmI4MWY1In0.BwHw4GrvmaD5UXmrQuN_tnC7o2_OwtG_2lhMg2nQEXY" | ||
| BETTER_AUTH_SECRET=EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6 | ||
| BETTER_AUTH_URL=http://localhost:3000 #Base URL of your app No newline at end of file | ||
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" |
There was a problem hiding this comment.
security (generic-api-key): Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
Source: gitleaks
| DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcGlfa2V5IjoiMDFKWjdKSlpQRkQzNzROREE2RzA1MUtXRUIiLCJ0ZW5hbnRfaWQiOiJhZDMzYmZkYWM2NzhmNTAxMmE1NzgwODAwYzFmMDZhYmM1OGM3OTM4MjY0MDBiZTBhYzFjYWI0ZGRiYjAyOTg1IiwiaW50ZXJuYWxfc2VjcmV0IjoiYzRmNGQ1MWYtOTcyMy00MWNiLTllMWQtOWMwMTI5YmI4MWY1In0.BwHw4GrvmaD5UXmrQuN_tnC7o2_OwtG_2lhMg2nQEXY" | ||
| BETTER_AUTH_SECRET=EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6 | ||
| BETTER_AUTH_URL=http://localhost:3000 #Base URL of your app No newline at end of file | ||
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" |
There was a problem hiding this comment.
security (generic.secrets.security.detected-generic-secret): Generic Secret detected
Source: opengrep
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||||
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
.env (2)
2-3: Consider removing quotes from environment variable values.The static analysis tool correctly identifies that quotes are not needed around environment variable values in .env files. The quotes will be included as part of the actual values.
-BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" -BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app +BETTER_AUTH_SECRET=EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6 +BETTER_AUTH_URL=http://localhost:3000 #Base URL of your app
4-4: Add missing trailing newline.The file should end with a blank line as indicated by the static analysis tool.
EXPO_PUBLIC_API_URL="https://ship-server.onrender.com" +src/components/ui/confetti.tsx (1)
3-3: Consider export consistency with component naming.The component is named
ConfettiBasebut exported as default. Consider either naming itConfettito match the previous API or using a named export for consistency.-export default function ConfettiBase() { +export default function Confetti() {or
-export default function ConfettiBase() { +export function ConfettiBase() {src/components/ui/skia-loader.tsx (1)
3-5: Enhance type safety with generic constraints.The component type definition could be more specific to ensure the imported component is actually renderable.
interface SkiaCanvasProps { - component: () => Promise<{ default: React.ComponentType }>; + component: () => Promise<{ default: React.ComponentType<any> }>; }src/components/auth/sign-up-success-card.tsx (1)
36-36: Consider adding loading state for better UX.While the lazy loading is implemented correctly, users may experience a blank space while the confetti component loads. Consider adding a loading indicator or fallback.
- <SkiaLoader component={() => import("~/components/ui/confetti")} /> + <React.Suspense fallback={<View className="h-32 w-full" />}> + <SkiaLoader component={() => import("~/components/ui/confetti")} /> + </React.Suspense>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
.env(1 hunks)app.json(1 hunks)package.json(2 hunks)src/components/auth/sign-up-success-card.tsx(2 hunks)src/components/ui/confetti-base.tsx(0 hunks)src/components/ui/confetti.tsx(1 hunks)src/components/ui/skia-loader.tsx(1 hunks)src/lib/auth-client.ts(2 hunks)src/orpc/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/components/ui/confetti-base.tsx
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/**/*.{ts,tsx}: Use Platform.OS and process.env.EXPO_OS for conditional logic
Consider all platforms (iOS/Android/Web) when using native APIs
Files:
src/components/auth/sign-up-success-card.tsxsrc/orpc/index.tssrc/components/ui/skia-loader.tsxsrc/lib/auth-client.tssrc/components/ui/confetti.tsx
src/**/*.tsx
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
Import UI components from @/src/components/ui/ using path aliases
Files:
src/components/auth/sign-up-success-card.tsxsrc/components/ui/skia-loader.tsxsrc/components/ui/confetti.tsx
src/components/ui/**/*.tsx
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/components/ui/**/*.tsx: UI components use NativeWind's className prop with Tailwind utility classes
Use cva (class-variance-authority) for creating variant-based component APIs
Access theme values through CSS custom properties and Tailwind utilities
UI components in src/components/ui/ follow React.forwardRef pattern with displayName
Use Tailwind's responsive prefixes (sm:, md:, lg:) and NativeWind's responsive utilities
Use web: prefix for web-only styles, and conditional rendering for native-specific components
Place UI components in src/components/ui/ with proper TypeScript interfaces
Always use NativeWind's className prop with Tailwind utility classes
Define variants using cva (class-variance-authority) for type-safe component APIs
Use Tailwind's spacing utilities (p-4, m-2, gap-3, etc.)
Use Tailwind's typography utilities (text-lg, font-semibold, etc.)
Toggle between light/dark themes using className conditionals and CSS variables
Use platform-specific prefixes (web:, ios:, android:) for conditional styles
Files:
src/components/ui/skia-loader.tsxsrc/components/ui/confetti.tsx
src/lib/auth-client.ts
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/lib/auth-client.ts: Auth client in src/lib/auth-client.ts with platform-specific plugins
Better Auth requires platform-specific plugin setup for native vs web storage
Files:
src/lib/auth-client.ts
{src/lib/auth-client.ts,src/app/(auth)/**/*.tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
{src/lib/auth-client.ts,src/app/(auth)/**/*.tsx}: Use authClient.useSession() hook for session state
Use authClient hooks for auth state management
Files:
src/lib/auth-client.ts
🧠 Learnings (3)
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Auth client in src/lib/auth-client.ts with platform-specific plugins
Applied to files:
src/lib/auth-client.ts
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Better Auth requires platform-specific plugin setup for native vs web storage
Applied to files:
src/lib/auth-client.ts
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : Use authClient hooks for auth state management
Applied to files:
src/lib/auth-client.ts
🧬 Code Graph Analysis (1)
src/components/auth/sign-up-success-card.tsx (1)
src/components/ui/skia-loader.tsx (1)
SkiaLoader(7-16)
🪛 dotenv-linter (3.3.0)
.env
[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")
[warning] 2-2: [UnorderedKey] The BETTER_AUTH_SECRET key should go before the DATABASE_URL key
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
[warning] 3-3: [UnorderedKey] The BETTER_AUTH_URL key should go before the DATABASE_URL key
🔇 Additional comments (14)
.env (1)
1-1: LGTM! Database connection string is properly configured.The DATABASE_URL appears to be correctly formatted for Prisma Accelerate with the necessary API key and tenant information.
package.json (2)
62-62: LGTM! Expo Video dependency added correctly.The expo-video dependency version ~2.2.2 is properly added and aligns with the plugin configuration in app.json.
20-22: ORPC dependencies are compatible as-isThe caret ranges (^1.7.10, ^1.8.0, ^1.7.11) all allow upgrades up to <2.0.0 and will resolve to the latest 1.8.0 releases. No compatibility issues detected.
Optional: for clarity and consistency, you may bump both
@orpc/clientand@orpc/openapi-clientto^1.8.0, but it isn’t strictly required.app.json (1)
40-41: LGTM! Plugin configuration is correct.The expo-video plugin is properly added to the plugins array and the configuration follows the expected format.
src/orpc/index.ts (3)
4-4: LGTM! Environment variable usage is correct.The fallback to localhost is appropriate for development environments when EXPO_PUBLIC_API_URL is not set.
8-10: LGTM! OpenAPILink configuration is correct.The OpenAPILink is properly configured with the fetched spec and the correct base URL for ORPC endpoints.
12-12: LGTM! Client export is properly configured.The ORPC client is correctly created and exported for use throughout the application.
src/components/ui/confetti.tsx (2)
1-1: LGTM! Direct import from react-native-fast-confetti.The direct import approach simplifies the component structure by removing the previous WASM-based loading complexity.
5-14: LGTM! Confetti configuration is well-defined.The color palette provides good visual variety and
isInfinite={false}ensures the confetti animation has a proper end state.src/lib/auth-client.ts (2)
6-6: Initialization order follows platform requirements.The change from inline plugin initialization to array manipulation allows proper ordering of platform-specific plugins, which aligns with Better Auth's requirements for Expo client configuration.
18-18: Plugin order ensures proper initialization.Moving
adminClient()after the platform-specific conditional ensures it's added last, which is appropriate for admin functionality that may depend on the platform-specific storage configuration.src/components/ui/skia-loader.tsx (1)
1-1: Verify WithSkiaWeb import path and availabilityOur checks against the published package show that:
WithSkiaWebis not exported from the main entry point (lib/index.d.ts).- Attempting to fetch the web-module type definitions at
lib/module/web/index.d.tsvia unpkg returns HTTP 302 (not found).This indicates the deep import
import { WithSkiaWeb } from "@shopify/react-native-skia/lib/module/web";may not resolve correctly across versions. Please:
- Confirm that
node_modules/@shopify/react-native-skia/lib/module/web/index.{js,d.ts}actually exists in your installed version.- If it does exist, consider requesting or contributing a public export for
WithSkiaWebin the package’s main entry point.- Otherwise, update this import to a supported path (or file-relative import) that’s guaranteed to ship with the library.
src/components/auth/sign-up-success-card.tsx (2)
11-11: LGTM! Proper import of the new SkiaLoader component.The import follows the established path alias pattern and correctly imports from the UI components directory.
36-36: Confetti component export verified
Thesrc/components/ui/confetti.tsxfile has a default export (export default function ConfettiBase), which satisfies the SkiaLoader’s requirement. No changes needed—this lazy‐loading usage is correct.
| BETTER_AUTH_URL=http://localhost:3000 #Base URL of your app No newline at end of file | ||
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" | ||
| BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app | ||
| EXPO_PUBLIC_API_URL="https://ship-server.onrender.com" No newline at end of file |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify the production API URL endpoint is accessible.
The EXPO_PUBLIC_API_URL points to a production server. Ensure this endpoint is reliable and accessible for all users.
🏁 Script executed:
#!/bin/bash
# Description: Verify the API endpoint is accessible and returns expected responses
# Test: Check if the API URL is accessible and if the ORPC spec endpoint exists
API_URL="https://ship-server.onrender.com"
echo "Testing main API URL..."
curl -I "$API_URL" --max-time 10
echo "Testing ORPC spec endpoint..."
curl -I "$API_URL/orpc/spec.json" --max-time 10
echo "Testing ORPC base endpoint..."
curl -I "$API_URL/orpc/" --max-time 10Length of output: 3475
Production API endpoints returning 404
Verified the configured EXPO_PUBLIC_API_URL (https://ship-server.onrender.com) and its ORPC endpoints all respond with HTTP 404:
• GET HEAD https://ship-server.onrender.com → 404
• GET HEAD https://ship-server.onrender.com/orpc/ → 404
• GET HEAD https://ship-server.onrender.com/orpc/spec.json → 404
Please ensure the production API is correctly deployed and these endpoints exist, or update the URL in .env to point to a valid, accessible API base.
🤖 Prompt for AI Agents
.env around line 4: the EXPO_PUBLIC_API_URL is set to
"https://ship-server.onrender.com" but the domain's root and /orpc endpoints
return 404; either correct the URL to a valid production API base that serves
the expected ORPC endpoints (e.g., the deployed API hostname that returns
/orpc/spec.json) or redeploy the backend so these endpoints exist at the current
host; update the .env value to the reachable API base and verify GET
/orpc/spec.json returns 200 before merging.
| export function SkiaLoader({ component }: SkiaCanvasProps) { | ||
| return ( | ||
| <WithSkiaWeb | ||
| opts={{ | ||
| locateFile: (file) => `/canvaskit.wasm`, | ||
| }} | ||
| getComponent={component} | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for dynamic imports.
The component doesn't handle cases where the dynamic import fails, which could lead to poor user experience.
export function SkiaLoader({ component }: SkiaCanvasProps) {
+ const [error, setError] = React.useState<Error | null>(null);
+
+ const wrappedComponent = React.useCallback(async () => {
+ try {
+ return await component();
+ } catch (err) {
+ setError(err instanceof Error ? err : new Error('Failed to load component'));
+ throw err;
+ }
+ }, [component]);
+
+ if (error) {
+ return <Text>Failed to load component: {error.message}</Text>;
+ }
+
return (
<WithSkiaWeb
opts={{
locateFile: (file) => `/canvaskit.wasm`,
}}
- getComponent={component}
+ getComponent={wrappedComponent}
/>
);
}📝 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.
| export function SkiaLoader({ component }: SkiaCanvasProps) { | |
| return ( | |
| <WithSkiaWeb | |
| opts={{ | |
| locateFile: (file) => `/canvaskit.wasm`, | |
| }} | |
| getComponent={component} | |
| /> | |
| ); | |
| } | |
| export function SkiaLoader({ component }: SkiaCanvasProps) { | |
| const [error, setError] = React.useState<Error | null>(null); | |
| const wrappedComponent = React.useCallback(async () => { | |
| try { | |
| return await component(); | |
| } catch (err) { | |
| setError(err instanceof Error ? err : new Error('Failed to load component')); | |
| throw err; | |
| } | |
| }, [component]); | |
| if (error) { | |
| return <Text>Failed to load component: {error.message}</Text>; | |
| } | |
| return ( | |
| <WithSkiaWeb | |
| opts={{ | |
| locateFile: (file) => `/canvaskit.wasm`, | |
| }} | |
| getComponent={wrappedComponent} | |
| /> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/components/ui/skia-loader.tsx around lines 7 to 16, the component calls a
dynamic import via getComponent without handling failures; update getComponent
(or wrap WithSkiaWeb usage) to catch import errors, log them, and surface a
fallback UI or error state. Specifically, change getComponent to an async
function that tries the dynamic import in a try/catch, logs the error (or pass
it to an onError handler), and returns a safe fallback component when the import
fails; alternatively wrap SkiaLoader in an error boundary and render a
user-facing fallback if the dynamic load rejects.
| return ( | ||
| <WithSkiaWeb | ||
| opts={{ | ||
| locateFile: (file) => `/canvaskit.wasm`, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Hardcoded CanvasKit path may cause deployment issues.
The hardcoded /canvaskit.wasm path assumes the file is in the public root. This may not work in all deployment environments or build configurations.
Consider making the path configurable:
- opts={{
- locateFile: (file) => `/canvaskit.wasm`,
- }}
+ opts={{
+ locateFile: (file) => process.env.EXPO_PUBLIC_CANVASKIT_PATH || `/canvaskit.wasm`,
+ }}🏁 Script executed:
#!/bin/bash
# Check if canvaskit.wasm is properly configured in public assets
fd "canvaskit.wasm" --type f
rg -i "canvaskit" package.json app.json metro.config.js webpack.config.js || echo "No CanvasKit configuration found"Length of output: 257
Ensure CanvasKit path is configurable for non-root deployments
We do have public/canvaskit.wasm, so the current hard-coded /canvaskit.wasm will work when your app is served from the root. However, if you ever host under a sub-path (e.g. GitHub Pages, a CDN base path, etc.), that leading slash will break.
Please update src/components/ui/skia-loader.tsx to allow overriding via an environment variable (or via your framework’s PUBLIC_URL/BASE_URL):
--- a/src/components/ui/skia-loader.tsx
+++ b/src/components/ui/skia-loader.tsx
@@ -10,7 +10,13 @@ export function SkiaCanvas() {
return (
<CanvasKitWebGL
opts={{
- locateFile: (file) => `/canvaskit.wasm`,
+ locateFile: (file) => {
+ // Use PUBLIC_URL (CRA/Vite) or a custom EXPO_CANVASKIT_PATH if set,
+ // falling back to root-relative path:
+ const base =
+ process.env.PUBLIC_URL ??
+ process.env.EXPO_PUBLIC_CANVASKIT_PATH ??
+ '';
+ return `${base}/canvaskit.wasm`;
+ },
}}
/>
);• This preserves the default behavior for root-mounted sites.
• It lets you inject a custom base path at build time via PUBLIC_URL (create-react-app, Vite) or EXPO_PUBLIC_CANVASKIT_PATH.
📝 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.
| locateFile: (file) => `/canvaskit.wasm`, | |
| export function SkiaCanvas() { | |
| return ( | |
| <CanvasKitWebGL | |
| opts={{ | |
| locateFile: (file) => { | |
| // Use PUBLIC_URL (CRA/Vite) or a custom EXPO_PUBLIC_CANVASKIT_PATH if set, | |
| // falling back to root-relative path: | |
| const base = | |
| process.env.PUBLIC_URL ?? | |
| process.env.EXPO_PUBLIC_CANVASKIT_PATH ?? | |
| ''; | |
| return `${base}/canvaskit.wasm`; | |
| }, | |
| }} | |
| /> | |
| ); | |
| } |
|
|
||
| const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; | ||
|
|
||
| const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json()); |
There was a problem hiding this comment.
Add error handling for the OpenAPI spec fetch.
The top-level await fetch lacks error handling, which could cause the module to fail during initialization if the API is unreachable or returns invalid JSON.
-const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json());
+const specJSON = await fetch(`${apiUrl}/orpc/spec.json`)
+ .then((r) => {
+ if (!r.ok) {
+ throw new Error(`Failed to fetch ORPC spec: ${r.status} ${r.statusText}`);
+ }
+ return r.json();
+ })
+ .catch((error) => {
+ console.error('Failed to initialize ORPC client:', error);
+ throw error;
+ });📝 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.
| const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json()); | |
| const specJSON = await fetch(`${apiUrl}/orpc/spec.json`) | |
| .then((r) => { | |
| if (!r.ok) { | |
| throw new Error(`Failed to fetch ORPC spec: ${r.status} ${r.statusText}`); | |
| } | |
| return r.json(); | |
| }) | |
| .catch((error) => { | |
| console.error('Failed to initialize ORPC client:', error); | |
| throw error; | |
| }); |
🤖 Prompt for AI Agents
In src/orpc/index.ts around line 6, the top-level await that fetches the OpenAPI
spec has no error handling and can crash module initialization if the network
request fails or the response is invalid JSON; wrap the fetch+json parsing in a
try/catch (or use .catch) to handle network and parsing errors, log or rethrow a
clear error with context (including the URL and status), optionally provide a
fallback or export a rejected promise so consumers can handle initialization
failure, and ensure the module doesn't throw unhandled exceptions during import.
There was a problem hiding this comment.
Actionable comments posted: 5
🔭 Outside diff range comments (1)
src/lib/auth-client.ts (1)
21-23: Use EXPO_PUBLIC_API_URL for baseURL to align with environment-driven configPR mentions adding an API URL env var. Hardcoding the Render URL makes switching environments harder.
Apply this diff:
-export const authClient = createAuthClient({ - baseURL: "https://ship-server.onrender.com/api/auth", // Base URL of your Better Auth backend. - // baseURL: "http://localhost:3000/api/auth", // Base URL of your Better Auth backend. +export const authClient = createAuthClient({ + baseURL: `${apiBase}/api/auth`, // Base URL of your Better Auth backend.And add this variable above (outside the selected range):
// Prefer env-driven configuration; EXPO_PUBLIC_* is replaced at build time by Expo. const apiBase = process.env.EXPO_PUBLIC_API_URL ?? "https://ship-server.onrender.com";
♻️ Duplicate comments (2)
src/orpc/index.ts (2)
4-4: Do not silently default API base URL to localhost in productionFalling back to http://localhost:3000 can break production builds silently. Throw a clear error if EXPO_PUBLIC_API_URL is missing.
Apply:
-const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; +const apiUrl = process.env.EXPO_PUBLIC_API_URL; +if (!apiUrl) { + throw new Error("EXPO_PUBLIC_API_URL is not set. Configure it in your environment variables."); +}
1-12: Avoid top-level await and module-scope network calls; add robust init with error handlingTop-level await and a module-scope fetch can fail on some runtimes (Hermes/SSR) and block app startup. Move initialization behind an async initializer with timeout, single-flight, and clear errors. This also aligns better with multi-platform constraints (iOS/Android/Web).
Apply:
-import { createORPCClient } from "@orpc/client"; -import { OpenAPILink } from "@orpc/openapi-client/fetch"; - -const apiUrl = process.env.EXPO_PUBLIC_API_URL || "http://localhost:3000"; - -const specJSON = await fetch(`${apiUrl}/orpc/spec.json`).then((r) => r.json()); - -const link = new OpenAPILink(specJSON, { - url: `${apiUrl}/orpc/`, -}); - -export const orpc = createORPCClient(link); +import { createORPCClient } from "@orpc/client"; +import { OpenAPILink } from "@orpc/openapi-client/fetch"; + +const apiUrl = process.env.EXPO_PUBLIC_API_URL; +if (!apiUrl) { + throw new Error("EXPO_PUBLIC_API_URL is not set. Configure it in your environment variables."); +} + +type ORPCClient = ReturnType<typeof createORPCClient>; +let orpcInstance: ORPCClient | null = null; +let initPromise: Promise<ORPCClient> | null = null; + +/** + * Initializes and returns a singleton ORPC client instance. + * Safe to call multiple times; only the first call performs network I/O. + */ +export async function initORPC(): Promise<ORPCClient> { + if (orpcInstance) return orpcInstance; + if (initPromise) return initPromise; + + initPromise = (async () => { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout + try { + const res = await fetch(`${apiUrl}/orpc/spec.json`, { signal: controller.signal }); + if (!res.ok) { + throw new Error(`Failed to fetch ORPC spec: ${res.status} ${res.statusText}`); + } + const specJSON = await res.json(); + const link = new OpenAPILink(specJSON, { url: `${apiUrl}/orpc/` }); + orpcInstance = createORPCClient(link); + return orpcInstance; + } finally { + clearTimeout(timeout); + } + })(); + + return initPromise; +}Follow-up:
- Replace imports of
orpcwithinitORPCand await at usage sites (e.g., loaders, effects, service layer), not at module top-level.
🧹 Nitpick comments (6)
src/lib/auth-client.ts (2)
18-19: Gate admin plugin behind an env flag to reduce surface area in non-admin buildsUnless you need admin endpoints in all builds, consider enabling this only when explicitly requested.
-plugins.push(adminClient()); +if (process.env.EXPO_PUBLIC_ENABLE_ADMIN === "true") { + plugins.push(adminClient()); +}
4-4: Optional: avoid bundling SecureStore on web by requiring it only on nativeNot critical, but importing
expo-secure-storeat the top can bloat the web bundle. A conditional require avoids pulling native code into web builds.Example approach (requires minor restructuring):
// Remove the top-level import of expo-secure-store // import * as SecureStore from "expo-secure-store"; let nativePlugins: any[] = []; if (process.env.EXPO_OS !== "web") { // eslint-disable-next-line @typescript-eslint/no-var-requires const SecureStore = require("expo-secure-store"); nativePlugins.push( expoClient({ scheme: "universalstarter", storagePrefix: "universal-starter", storage: SecureStore, }) ); } // ...then use: plugins: [...nativePlugins, adminClient()],src/orpc/index.ts (1)
1-12: Consider cross-platform and SSR bootstrap timingIf you need immediate availability in React components, initialize in an app-level provider or bootstrap step, then inject via context. This avoids TLA while keeping consumers synchronous after hydration.
src/components/ui/skia-loader.tsx (2)
3-5: Add a type-only React import for correctness.Avoid depending on global React types; import them explicitly.
+import type { ComponentType } from "react"; interface SkiaCanvasProps { - component: () => Promise<{ default: React.ComponentType }>; + component: () => Promise<{ default: ComponentType<any> }>; }
10-12: Hard-coded wasm path may break behind basePath/CDN; use the provided filename and support basePath.Using
"/canvaskit.wasm"assumes site root. Respect configured base paths/CDNs.- locateFile: (file) => `/canvaskit.wasm`, + locateFile: (file) => `${process.env.EXPO_PUBLIC_BASE_PATH ?? ""}/${file}`,If you keep the hard-coded path, please verify that
/canvaskit.wasmis served from your app root in all web environments (dev, preview, prod).src/components/auth/sign-up-success-card.tsx (1)
36-36: Position confetti as an overlay to avoid layout shifts; add web-only 'fixed'.Currently, the confetti component may take layout space and push the card down. Wrap it in an absolutely positioned, non-interactive container.
- <SkiaLoader component={() => import("~/components/ui/confetti")} /> + <View className="absolute inset-0 pointer-events-none z-10 web:fixed"> + <SkiaLoader component={() => import("~/components/ui/confetti")} /> + </View>This also follows the "use web: prefix for web-only styles" guidance.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
.env(1 hunks)app.json(1 hunks)package.json(2 hunks)src/components/auth/sign-up-success-card.tsx(2 hunks)src/components/ui/confetti-base.tsx(0 hunks)src/components/ui/confetti.tsx(1 hunks)src/components/ui/skia-loader.tsx(1 hunks)src/lib/auth-client.ts(2 hunks)src/orpc/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/components/ui/confetti-base.tsx
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/**/*.{ts,tsx}: Use Platform.OS and process.env.EXPO_OS for conditional logic
Consider all platforms (iOS/Android/Web) when using native APIs
Files:
src/components/auth/sign-up-success-card.tsxsrc/components/ui/skia-loader.tsxsrc/orpc/index.tssrc/components/ui/confetti.tsxsrc/lib/auth-client.ts
src/**/*.tsx
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
Import UI components from @/src/components/ui/ using path aliases
Files:
src/components/auth/sign-up-success-card.tsxsrc/components/ui/skia-loader.tsxsrc/components/ui/confetti.tsx
src/components/ui/**/*.tsx
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/components/ui/**/*.tsx: UI components use NativeWind's className prop with Tailwind utility classes
Use cva (class-variance-authority) for creating variant-based component APIs
Access theme values through CSS custom properties and Tailwind utilities
UI components in src/components/ui/ follow React.forwardRef pattern with displayName
Use Tailwind's responsive prefixes (sm:, md:, lg:) and NativeWind's responsive utilities
Use web: prefix for web-only styles, and conditional rendering for native-specific components
Place UI components in src/components/ui/ with proper TypeScript interfaces
Always use NativeWind's className prop with Tailwind utility classes
Define variants using cva (class-variance-authority) for type-safe component APIs
Use Tailwind's spacing utilities (p-4, m-2, gap-3, etc.)
Use Tailwind's typography utilities (text-lg, font-semibold, etc.)
Toggle between light/dark themes using className conditionals and CSS variables
Use platform-specific prefixes (web:, ios:, android:) for conditional styles
Files:
src/components/ui/skia-loader.tsxsrc/components/ui/confetti.tsx
src/lib/auth-client.ts
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
src/lib/auth-client.ts: Auth client in src/lib/auth-client.ts with platform-specific plugins
Better Auth requires platform-specific plugin setup for native vs web storage
Files:
src/lib/auth-client.ts
{src/lib/auth-client.ts,src/app/(auth)/**/*.tsx}
📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)
{src/lib/auth-client.ts,src/app/(auth)/**/*.tsx}: Use authClient.useSession() hook for session state
Use authClient hooks for auth state management
Files:
src/lib/auth-client.ts
🧠 Learnings (4)
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/components/ui/**/*.tsx : Use web: prefix for web-only styles, and conditional rendering for native-specific components
Applied to files:
src/components/ui/skia-loader.tsx
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Auth client in src/lib/auth-client.ts with platform-specific plugins
Applied to files:
src/lib/auth-client.ts
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to src/lib/auth-client.ts : Better Auth requires platform-specific plugin setup for native vs web storage
Applied to files:
src/lib/auth-client.ts
📚 Learning: 2025-07-28T12:52:53.959Z
Learnt from: CR
PR: fyndx/expo-universal-starter#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-28T12:52:53.959Z
Learning: Applies to {src/lib/auth-client.ts,src/app/(auth)/**/*.tsx} : Use authClient hooks for auth state management
Applied to files:
src/lib/auth-client.ts
🧬 Code Graph Analysis (2)
src/components/auth/sign-up-success-card.tsx (1)
src/components/ui/skia-loader.tsx (1)
SkiaLoader(7-16)
src/lib/auth-client.ts (4)
src/containers/core/HeaderUser.tsx (3)
authClient(34-227)authClient(207-209)authClient(119-121)src/models/admin/user-detail/session.model.ts (1)
ExtendedBetterAuthSession(9-12)src/app/(public)/auth/index.tsx (1)
AuthIndex(6-8)src/app/(public)/auth/_layout.tsx (1)
AuthLayout(6-28)
🪛 dotenv-linter (3.3.0)
.env
[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")
[warning] 2-2: [UnorderedKey] The BETTER_AUTH_SECRET key should go before the DATABASE_URL key
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
[warning] 3-3: [UnorderedKey] The BETTER_AUTH_URL key should go before the DATABASE_URL key
🔇 Additional comments (7)
src/lib/auth-client.ts (2)
18-19: Plugin order looks correct (expoClient before adminClient on native)This ensures storage is registered before admin capabilities. No action needed.
10-15: Deep link scheme matches Expo config
Verified that thescheme: "universalstarter"in src/lib/auth-client.ts matches the value in app.json. No changes needed.package.json (2)
62-62: Confirm expo-video version aligns with Expo SDK 53 and plugin configexpo-video ~2.2.2 should match SDK 53, but double-check build-time config and native module autolinking on all platforms (iOS/Android/Web). If any issues arise, pin the exact version recommended by
npx expo install expo-videofor SDK 53.Run locally:
- npx expo install expo-video
- EAS build previews for iOS and Android to ensure no plugin/config errors at prebuild time.
20-22: Validate ORPC setup & SSR fetch – all core checks passedI confirmed:
- ORPC imports and client instantiation live in
src/orpc/index.ts.- Your lockfile (
package-lock.json) includes@orpc/client,@orpc/contract, and@orpc/openapi-client.- Node v24.3.0 is in use, so a global
fetchis available at runtime.Next steps (manual verification):
- Run production builds on iOS, Android (Metro + Hermes), and Web SSR to ensure the ORPC client bundles correctly.
- Analyze your production bundle (e.g. using source-map-explorer or Metro’s bundle visualizer) to verify tree-shaking is removing unused ORPC code.
Let me know if you hit any issues during those build tests!
app.json (1)
40-42: Verify plugin necessity and build behavior for expo-videoAdding "expo-video" to plugins is fine; some Expo modules work without an explicit plugin. Ensure:
- Prebuild generates expected native changes on iOS/Android.
- No plugin conflict with react-native-edge-to-edge and expo-router.
Locally:
- npx expo prebuild --clean (in a throwaway branch) and inspect ios/ and android/ diffs.
- Run an EAS build preview for both platforms to confirm no config errors.
src/components/ui/confetti.tsx (1)
1-1: Verify and configure Expo Web support for react-native-fast-confetti
- ✅ Dependency check:
react-native-fast-confetti@^1.0.3is already declared in package.json.- ⚙️ Web support caveat: the library relies on
@shopify/react-native-skia, which does not auto-setup CanvasKit for Expo Web.- 📝 Action items:
• Follow the React Native Skia Web setup guide to copy/serve the CanvasKit WASM and include any required polyfills:
https://shopify.github.io/react-native-skia/docs/getting-started/web
• Update your Expo web (webpack) configuration—either viawithSkiaplugin or a customwebpack.config.js—to load Skia’s WASM bundle.
• Run the minimal example on Expo Web (or in Expo Go’s web mode) to confirm confetti renders correctly.src/components/auth/sign-up-success-card.tsx (1)
11-11: Import is fine, but ensure SkiaLoader is platform-safe.Once you split SkiaLoader into
.web.tsx/.native.tsx, this import will be safe across iOS/Android/Web. Without that split, native builds may fail.If you prefer not to split files, gate usage behind
Platform.OS === "web"and render the component directly on native.
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" | ||
| BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app | ||
| EXPO_PUBLIC_API_URL="https://ship-server.onrender.com" No newline at end of file |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Housekeeping: .env hygiene and lint nits
Consider adding .env to .gitignore and committing a .env.example instead. Address dotenv-linter warnings only if desired; quoted values are acceptable and often safer.
🏁 Script executed:
#!/bin/bash
# Check if .env is ignored
if fd -H -a ".gitignore" >/dev/null; then
echo "Found .gitignore:"
cat .gitignore
echo "-----"
echo "Checking if .env is ignored:"
rg -n "^\s*\.env(\.|$|$)" .gitignore || echo "'.env' not ignored"
else
echo "No .gitignore found"
fi
# Search for any accidental secret leaks elsewhere
rg -n --hidden --glob '!.git' '(SECRET|API_KEY|DATABASE_URL|TOKEN)=' -SLength of output: 1230
Ignore local .env and introduce an example file
.envis not currently ignored (only.env*.localis). Add this line to.gitignore:# local env files -.env*.local +.env +.env*.local
- Remove the tracked file and prevent future commits:
git rm --cached .env - Create a committed
.env.examplewith placeholder keys/URLs instead of real secrets:DATABASE_URL="postgres://USER:PASSWORD@HOST:PORT/DB_NAME" BETTER_AUTH_SECRET="your-secret-here" BETTER_AUTH_URL="http://localhost:3000" EXPO_PUBLIC_API_URL="https://your-api.example.com"
- Rotate the exposed
DATABASE_URLandBETTER_AUTH_SECRET—they were leaked in the committed.env.
dotenv-linter warnings about quoted values can be ignored; quotes are often safer.
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")
[warning] 2-2: [UnorderedKey] The BETTER_AUTH_SECRET key should go before the DATABASE_URL key
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
[warning] 3-3: [UnorderedKey] The BETTER_AUTH_URL key should go before the DATABASE_URL key
🤖 Prompt for AI Agents
In .env around lines 2-4: the repo currently contains a committed .env with real
secrets and isn’t ignoring .env; add a line to .gitignore to ignore .env, run
git rm --cached .env to stop tracking the file and commit that change, create
and commit a new .env.example containing placeholder values (e.g. DATABASE_URL,
BETTER_AUTH_SECRET, BETTER_AUTH_URL, EXPO_PUBLIC_API_URL) instead of real
secrets, and rotate any leaked credentials (DATABASE_URL and BETTER_AUTH_SECRET)
immediately; you can ignore dotenv-linter warnings about quoted values.
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" | ||
| BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app |
There was a problem hiding this comment.
Critical: Secrets committed to VCS — rotate immediately and remove from repo
BETTER_AUTH_SECRET (and potentially DATABASE_URL above) are sensitive. Committing real secrets is a high-risk incident.
Immediate actions:
- Revoke/rotate BETTER_AUTH_SECRET and any tokens contained in DATABASE_URL.
- Remove real secrets from the repo history (e.g., git filter-repo) and replace with placeholders or move to CI secrets manager.
- Commit only a sanitized .env.example with placeholders.
Apply placeholders in this file as a stopgap:
-BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6"
-BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app
+BETTER_AUTH_SECRET="__REPLACE_ME__"
+BETTER_AUTH_URL="http://localhost:3000" # Base URL of your appNote: EXPO_PUBLIC_API_URL is fine to be public by design; keep production values in deployment envs and dev values locally.
📝 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.
| BETTER_AUTH_SECRET="EiansbxbE4bzQ02mxu70UV0jJaoz9Lw6" | |
| BETTER_AUTH_URL="http://localhost:3000" #Base URL of your app | |
| BETTER_AUTH_SECRET="__REPLACE_ME__" | |
| BETTER_AUTH_URL="http://localhost:3000" # Base URL of your app |
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")
[warning] 2-2: [UnorderedKey] The BETTER_AUTH_SECRET key should go before the DATABASE_URL key
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
[warning] 3-3: [UnorderedKey] The BETTER_AUTH_URL key should go before the DATABASE_URL key
| export default function ConfettiBase() { | ||
| return ( | ||
| <WithSkiaWeb | ||
| opts={{ | ||
| locateFile: (file) => `/canvaskit.wasm`, | ||
| }} | ||
| getComponent={async () => require("./confetti-base")} | ||
| <Confetti | ||
| colors={[ | ||
| "#FF6B6B", | ||
| "#4ECDC4", | ||
| "#45B7D1", | ||
| "#96CEB4", | ||
| "#FFEAA7", | ||
| "#DDA0DD", | ||
| ]} | ||
| isInfinite={false} | ||
| /> | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Adopt forwardRef + props pass-through and wrap with an overlay container.
Aligns with UI component conventions (forwardRef, optional className) and ensures the confetti overlays content rather than shifting layout.
-import { Confetti } from "react-native-fast-confetti";
+import { Confetti } from "react-native-fast-confetti";
+import { View } from "react-native";
+import type { ComponentProps } from "react";
-export default function ConfettiBase() {
- return (
- <Confetti
- colors={[
- "#FF6B6B",
- "#4ECDC4",
- "#45B7D1",
- "#96CEB4",
- "#FFEAA7",
- "#DDA0DD",
- ]}
- isInfinite={false}
- />
- );
-}
+type RNConfettiProps = ComponentProps<typeof Confetti>;
+export interface ConfettiBaseProps extends RNConfettiProps {
+ // For NativeWind styling of the container
+ className?: string;
+}
+
+function DefaultConfetti(props: RNConfettiProps) {
+ return (
+ <Confetti
+ colors={[
+ "#FF6B6B",
+ "#4ECDC4",
+ "#45B7D1",
+ "#96CEB4",
+ "#FFEAA7",
+ "#DDA0DD",
+ ]}
+ isInfinite={false}
+ {...props}
+ />
+ );
+}
+
+export default function ConfettiBase({
+ className = "absolute inset-0 pointer-events-none",
+ ...props
+}: ConfettiBaseProps) {
+ return (
+ <View className={className}>
+ <DefaultConfetti {...props} />
+ </View>
+ );
+}Committable suggestion skipped: line range outside the PR's diff.
| @@ -0,0 +1,16 @@ | |||
| import { WithSkiaWeb } from "@shopify/react-native-skia/lib/module/web"; | |||
There was a problem hiding this comment.
Web-only import will break native builds — split by platform (.web/.native).
WithSkiaWeb is only available in the web bundle. Importing @shopify/react-native-skia/lib/module/web from a shared file will cause iOS/Android bundling/runtime failures. Create platform-specific files so native never imports the web entry.
Suggested structure (preferred):
// src/components/ui/skia-loader.web.tsx
import type { ComponentType } from "react";
import { WithSkiaWeb } from "@shopify/react-native-skia/lib/module/web";
interface SkiaCanvasProps {
component: () => Promise<{ default: ComponentType<any> }>;
}
export function SkiaLoader({ component }: SkiaCanvasProps) {
return (
<WithSkiaWeb
opts={{
// Honor base paths/CDN if set; fall back to root
locateFile: (file) => `${process.env.EXPO_PUBLIC_BASE_PATH ?? ""}/${file}`,
}}
getComponent={component}
/>
);
}// src/components/ui/skia-loader.native.tsx
import type { ComponentType } from "react";
import * as React from "react";
interface SkiaCanvasProps {
component: () => Promise<{ default: ComponentType<any> }>;
}
export function SkiaLoader({ component }: SkiaCanvasProps) {
const [Comp, setComp] = React.useState<ComponentType<any> | null>(null);
React.useEffect(() => {
let mounted = true;
component().then((mod) => {
if (mounted) setComp(() => mod.default);
});
return () => {
mounted = false;
};
}, [component]);
if (!Comp) return null;
return <Comp />;
}No changes required at call sites after this split.
| import * as SecureStore from "expo-secure-store"; | ||
|
|
||
| const plugins = [adminClient()]; | ||
| const plugins = []; |
There was a problem hiding this comment.
TS typing bug: plugins inferred as never[] — pushes will fail under strict typing
In TypeScript, const plugins = [] infers never[], so plugins.push(...) will error. Add an explicit type to unblock compilation.
Apply this minimal fix:
-const plugins = [];
+const plugins: any[] = [];📝 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.
| const plugins = []; | |
| const plugins: any[] = []; |
🤖 Prompt for AI Agents
In src/lib/auth-client.ts around line 6, the plugins array is declared as const
plugins = [] which TypeScript infers as never[], causing pushes to fail under
strict typing; change the declaration to include an explicit type (e.g., const
plugins: YourPluginType[] = [] or const plugins: Array<Plugin | unknown> = [])
so subsequent plugins.push(...) calls are valid and the file compiles under
strict mode.
User description
…oader
PR Type
Enhancement
Description
Integrate ORPC client for API communication
Replace Confetti component with reusable SkiaLoader
Update authentication client configuration
Add environment variables for API URL
Diagram Walkthrough
File Walkthrough
6 files
Create ORPC client for API communicationAdd reusable SkiaLoader componentSimplify confetti component implementationRemove confetti base component fileReplace Confetti with SkiaLoader usageReorder auth client plugin initialization1 files
Add ORPC dependencies and expo-video2 files
Add API URL environment variableAdd expo-video plugin configurationSummary by CodeRabbit
New Features
Improvements
Chores