Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ next-env.d.ts
# expo
.expo/
expo-env.d.ts
apps/expo/.gitignore
apps/expo/ios
apps/expo/android
apps/mobile/.gitignore
apps/mobile/ios
apps/mobile/android

# production
build
Expand Down
30 changes: 29 additions & 1 deletion api/chat/src/trpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { auth } from "@vendor/clerk/server";
import { auth, verifyToken } from "@vendor/clerk/server";
Copy link

Choose a reason for hiding this comment

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

The verifyToken function is imported from @vendor/clerk/server but this function doesn't exist in the Clerk vendor package, which will cause a runtime import error.

View Details
📝 Patch Details
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 69639ff6..d7a24247 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -371,7 +371,7 @@ importers:
         version: 8.2.0
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -558,7 +558,7 @@ importers:
         version: 5.1.5
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       next-themes:
         specifier: ^0.4.6
         version: 0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -703,7 +703,7 @@ importers:
         version: 5.1.5
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -794,7 +794,7 @@ importers:
         version: 0.451.0(react@19.0.0)
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -948,7 +948,7 @@ importers:
         version: 5.1.5
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       pino:
         specifier: ^9.7.0
         version: 9.9.0
@@ -1271,7 +1271,7 @@ importers:
         version: 8.2.0
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -1398,7 +1398,7 @@ importers:
         version: 8.2.0
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -2979,6 +2979,9 @@ importers:
 
   vendor/clerk:
     dependencies:
+      '@clerk/backend':
+        specifier: ^2.14.0
+        version: 2.14.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       '@clerk/elements':
         specifier: 'catalog:'
         version: 0.23.63(@types/react-dom@19.0.5(@types/react@19.0.13))(@types/react@19.0.13)(next@15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -2990,7 +2993,7 @@ importers:
         version: 0.12.0(typescript@5.9.2)(zod@3.25.76)
       next:
         specifier: 'catalog:'
-        version: 15.4.5(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+        version: 15.4.5(@opentelemetry/api@1.9.0)(@playwright/test@1.54.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
       react:
         specifier: 19.0.0
         version: 19.0.0
@@ -33698,7 +33701,7 @@ snapshots:
       postcss: 8.4.31
       react: 19.0.0
       react-dom: 19.0.0(react@19.0.0)
-      styled-jsx: 5.1.6(@babel/core@7.28.3)(react@19.0.0)
+      styled-jsx: 5.1.6(react@19.0.0)
     optionalDependencies:
       '@next/swc-darwin-arm64': 15.4.5
       '@next/swc-darwin-x64': 15.4.5
@@ -33749,7 +33752,7 @@ snapshots:
       postcss: 8.4.31
       react: 19.0.0
       react-dom: 19.1.1(react@19.0.0)
-      styled-jsx: 5.1.6(@babel/core@7.28.3)(react@19.0.0)
+      styled-jsx: 5.1.6(react@19.0.0)
     optionalDependencies:
       '@next/swc-darwin-arm64': 15.4.5
       '@next/swc-darwin-x64': 15.4.5
@@ -36398,6 +36401,11 @@ snapshots:
     optionalDependencies:
       '@babel/core': 7.28.3
 
+  styled-jsx@5.1.6(react@19.0.0):
+    dependencies:
+      client-only: 0.0.1
+      react: 19.0.0
+
   styled-jsx@5.1.6(react@19.1.1):
     dependencies:
       client-only: 0.0.1
diff --git a/vendor/clerk/package.json b/vendor/clerk/package.json
index 68151c99..dfff741a 100644
--- a/vendor/clerk/package.json
+++ b/vendor/clerk/package.json
@@ -23,6 +23,7 @@
     "typecheck": "tsc --noEmit"
   },
   "dependencies": {
+    "@clerk/backend": "^2.14.0",
     "@clerk/elements": "catalog:",
     "@clerk/nextjs": "catalog:",
     "@t3-oss/env-nextjs": "^0.12.0",
diff --git a/vendor/clerk/src/server.ts b/vendor/clerk/src/server.ts
index d87997f5..2ee0e5cf 100644
--- a/vendor/clerk/src/server.ts
+++ b/vendor/clerk/src/server.ts
@@ -1,3 +1,4 @@
 import "server-only";
 
 export * from "@clerk/nextjs/server";
+export * from "@clerk/backend";

Analysis

Missing verifyToken import causes runtime error in TRPC authentication

What fails: Import of verifyToken from @vendor/clerk/server fails because the function is not available in @clerk/nextjs/server - it exists in @clerk/backend

How to reproduce:

# Try to start the TRPC server or make authenticated API calls
cd api/chat && node -e "import('@vendor/clerk/server').then(m => console.log(m.verifyToken))"

Result: verifyToken is undefined - the function doesn't exist in the re-exported @clerk/nextjs/server package

Expected: verifyToken should be available for Bearer token verification per Clerk Backend SDK documentation which shows verifyToken is exported from @clerk/backend

Fix: Added @clerk/backend dependency to vendor/clerk package and exported it from server.ts to make verifyToken available alongside Next.js server functions

import { db } from "@db/chat/client";

/**
Expand Down Expand Up @@ -55,6 +55,34 @@ export const createTRPCContext = async (opts: {
userId: clerkSession?.userId ?? null,
};

if (!session.userId) {
const authorization = opts.headers.get("authorization") ?? "";
const token = authorization.replace(/^Bearer\s+/i, "").trim();

if (token.length > 0) {
try {
const clerkSecretKey = process.env.CLERK_SECRET_KEY;

if (!clerkSecretKey) {
console.warn("Missing CLERK_SECRET_KEY env var; cannot verify token.");
} else {
const verification = await verifyToken(token, {
secretKey: clerkSecretKey,
});

if ("data" in verification && verification.data) {
const payload = verification.data as { sub?: string };
if (payload.sub) {
session.userId = payload.sub;
}
}
}
} catch (error) {
console.warn("Failed to verify Clerk token", error);
}
}
}

if (session.userId) {
console.info(`>>> tRPC Request from ${source} by ${session.userId}`);
} else {
Expand Down
4 changes: 4 additions & 0 deletions apps/mobile/.expo-shared/assets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
6 changes: 6 additions & 0 deletions apps/mobile/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli

expo-env.d.ts
# @end expo-cli
1 change: 1 addition & 0 deletions apps/mobile/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nativewind-env.d.ts
51 changes: 51 additions & 0 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { ConfigContext, ExpoConfig } from "expo/config";

export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: "Lightfast Chat",
slug: "lightfast-chat",
scheme: "lightfast-chat",
version: "0.1.0",
orientation: "portrait",
icon: "./assets/icon.png",
userInterfaceStyle: "dark",
updates: {
fallbackToCacheTimeout: 0,
},
newArchEnabled: true,
assetBundlePatterns: ["**/*"],
ios: {
bundleIdentifier: "com.lightfast.chat",
supportsTablet: true,
icon: "./assets/icon.png",
},
android: {
package: "com.lightfast.chat",
adaptiveIcon: {
foregroundImage: "./assets/adaptive-icon.png",
backgroundColor: "#1a1a1a",
},
edgeToEdgeEnabled: true,
},
// extra: {
// eas: {
// projectId: "your-eas-project-id",
// },
// },
experiments: {
tsconfigPaths: true,
typedRoutes: true,
},
plugins: [
"expo-router",
"expo-secure-store",
"expo-web-browser",
[
"expo-splash-screen",
{
backgroundColor: "#1a1a1a",
image: "./assets/icon.png",
},
],
],
});
Binary file added apps/mobile/assets/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions apps/mobile/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @type {import("@babel/core").ConfigFunction} */
module.exports = (api) => {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
plugins: [
[
"module-resolver",
{
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
root: ["./src"],
alias: {
"@": "./src",
},
},
],
"react-native-reanimated/plugin",
],
};
};
33 changes: 33 additions & 0 deletions apps/mobile/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"cli": {
"version": ">= 4.1.2",
"appVersionSource": "remote"
},
"build": {
"base": {
"node": "22.12.0",
"pnpm": "9.15.4",
"ios": {
"resourceClass": "m-medium"
}
},
"development": {
"extends": "base",
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"extends": "base",
"distribution": "internal",
"ios": {
"simulator": true
}
},
"production": {
"extends": "base"
}
},
"submit": {
"production": {}
}
}
11 changes: 11 additions & 0 deletions apps/mobile/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import baseConfig from "@repo/eslint-config/base";
import reactConfig from "@repo/eslint-config/react";

/** @type {import('typescript-eslint').Config} */
export default [
{
ignores: [".expo/**", "expo-plugins/**"],
},
...baseConfig,
...reactConfig,
];
1 change: 1 addition & 0 deletions apps/mobile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "expo-router/entry";
29 changes: 29 additions & 0 deletions apps/mobile/metro.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Learn more: https://docs.expo.dev/guides/monorepos/
const { getDefaultConfig } = require("expo/metro-config");
const { FileStore } = require("metro-cache");
const { withNativeWind } = require("nativewind/metro");

const path = require("node:path");

const config = withTurborepoManagedCache(
withNativeWind(getDefaultConfig(__dirname), {
input: "./src/styles.css",
configPath: "./tailwind.config.ts",
}),
);
module.exports = config;

/**
* Move the Metro cache to the `.cache/metro` folder.
* If you have any environment variables, you can configure Turborepo to invalidate it when needed.
*
* @see https://turborepo.com/docs/reference/configuration#env
* @param {import('expo/metro-config').MetroConfig} config
* @returns {import('expo/metro-config').MetroConfig}
*/
function withTurborepoManagedCache(config) {
config.cacheStores = [
new FileStore({ root: path.join(__dirname, ".cache/metro") }),
];
return config;
}
3 changes: 3 additions & 0 deletions apps/mobile/nativewind-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference types="nativewind/types" />

// NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind.
71 changes: 71 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@lightfast/mobile",
"version": "0.1.0",
"private": true,
"main": "index.ts",
"scripts": {
"clean": "git clean -xdf .cache .expo .turbo android ios node_modules",
"dev": "expo start",
"dev:android": "expo start --android",
"dev:ios": "expo start --ios",
"android": "expo run:android",
"ios": "expo run:ios",
"format": "prettier --check . --ignore-path ../../.gitignore --ignore-path .prettierignore",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/openai": "^0.0.70",
"@ai-sdk/react": "catalog:",
"@clerk/clerk-expo": "^2.15.0",
"@expo/metro-config": "^0.20.14",
"@legendapp/list": "^2.0.2",
"@repo/chat-ai-types": "workspace:*",
"@stardazed/streams-text-encoding": "^1.0.2",
"@tanstack/react-query": "catalog:",
"@trpc/client": "catalog:",
"@trpc/server": "catalog:",
"@trpc/tanstack-react-query": "catalog:",
"@ungap/structured-clone": "^1.3.0",
"expo": "53.0.9",
"expo-auth-session": "~6.2.1",
"expo-constants": "17.1.6",
"expo-dev-client": "5.1.8",
"expo-linking": "7.1.5",
"expo-router": "5.0.7",
"expo-secure-store": "14.2.3",
"expo-splash-screen": "0.30.8",
"expo-status-bar": "2.2.3",
"expo-system-ui": "~5.0.7",
"expo-web-browser": "14.1.6",
"lucide-react-native": "^0.544.0",
"nativewind": "~4.1.23",
"react": "catalog:react19",
"react-dom": "catalog:react19",
"react-native": "0.79.2",
"react-native-gesture-handler": "~2.25.0",
"react-native-reanimated": "~3.18.0",
"react-native-safe-area-context": "~5.4.1",
"react-native-screens": "~4.11.1",
"react-native-svg": "^15.13.0",
"superjson": "2.2.2",
"zod": "catalog:"
},
"devDependencies": {
"@api/chat": "workspace:*",
"@babel/core": "^7.27.4",
"@babel/preset-env": "^7.27.2",
"@babel/runtime": "^7.27.4",
"@repo/eslint-config": "workspace:*",
"@repo/prettier-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"babel-plugin-module-resolver": "^5.0.0",
"@types/babel__core": "^7.20.5",
"@types/react": "catalog:react19",
"eslint": "catalog:",
"prettier": "catalog:",
"tailwindcss": "^3.4.17",
"typescript": "catalog:"
},
"prettier": "@repo/prettier-config"
}
Binary file added apps/mobile/public/android-chrome-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/android-chrome-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/favicon.ico
Binary file not shown.
Binary file added apps/mobile/public/og-bg-only.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/public/og.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions apps/mobile/src/app/(auth)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Redirect, Stack } from "expo-router";
import { View } from "react-native";

import { useAuth } from "@clerk/clerk-expo";

export default function AuthLayout() {
const { isLoaded, isSignedIn } = useAuth();

if (!isLoaded) {
return <View className="flex-1 bg-background" />;
}

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

return <Stack screenOptions={{ headerShown: false }} />;
}
Loading
Loading