fix: upgrade client to expo 56

Upgrade the React Native client through Expo SDK 56, align workspace React versions, switch Bun installs to the hoisted linker for Expo compatibility, and fix the Metro proxy to handle localhost/IPv6 loopback after the SDK upgrade.
This commit is contained in:
2026-06-18 16:24:26 +01:00
parent 63e71fb828
commit dc91a048c9
12 changed files with 499 additions and 1617 deletions

View File

@@ -21,8 +21,8 @@
"lucide-react": "^0.577.0", "lucide-react": "^0.577.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"radix-ui": "^1.4.3", "radix-ui": "^1.4.3",
"react": "^19.2.0", "react": "19.2.3",
"react-dom": "^19.2.0", "react-dom": "19.2.3",
"shadcn": "^4.0.8", "shadcn": "^4.0.8",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",

View File

@@ -1,13 +1,12 @@
{ {
"expo": { "expo": {
"name": "Freya", "name": "Freya",
"slug": "freya-client", "slug": "freya",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "icon": "./assets/images/icon.png",
"scheme": "freya", "scheme": "freya",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": { "ios": {
"infoPlist": { "infoPlist": {
"NSAppTransportSecurity": { "NSAppTransportSecurity": {
@@ -24,7 +23,6 @@
"backgroundImage": "./assets/images/android-icon-background.png", "backgroundImage": "./assets/images/android-icon-background.png",
"monochromeImage": "./assets/images/android-icon-monochrome.png" "monochromeImage": "./assets/images/android-icon-monochrome.png"
}, },
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false, "predictiveBackGestureEnabled": false,
"package": "sh.nym.freya" "package": "sh.nym.freya"
}, },
@@ -54,55 +52,82 @@
{ {
"fontFamily": "Inter", "fontFamily": "Inter",
"fontDefinitions": [ "fontDefinitions": [
{ "path": "./assets/fonts/Inter_100Thin.ttf", "weight": 100 }, {
"path": "./assets/fonts/Inter_100Thin.ttf",
"weight": 100
},
{ {
"path": "./assets/fonts/Inter_100Thin_Italic.ttf", "path": "./assets/fonts/Inter_100Thin_Italic.ttf",
"weight": 100, "weight": 100,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_200ExtraLight.ttf", "weight": 200 }, {
"path": "./assets/fonts/Inter_200ExtraLight.ttf",
"weight": 200
},
{ {
"path": "./assets/fonts/Inter_200ExtraLight_Italic.ttf", "path": "./assets/fonts/Inter_200ExtraLight_Italic.ttf",
"weight": 200, "weight": 200,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_300Light.ttf", "weight": 300 }, {
"path": "./assets/fonts/Inter_300Light.ttf",
"weight": 300
},
{ {
"path": "./assets/fonts/Inter_300Light_Italic.ttf", "path": "./assets/fonts/Inter_300Light_Italic.ttf",
"weight": 300, "weight": 300,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_400Regular.ttf", "weight": 400 }, {
"path": "./assets/fonts/Inter_400Regular.ttf",
"weight": 400
},
{ {
"path": "./assets/fonts/Inter_400Regular_Italic.ttf", "path": "./assets/fonts/Inter_400Regular_Italic.ttf",
"weight": 400, "weight": 400,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_500Medium.ttf", "weight": 500 }, {
"path": "./assets/fonts/Inter_500Medium.ttf",
"weight": 500
},
{ {
"path": "./assets/fonts/Inter_500Medium_Italic.ttf", "path": "./assets/fonts/Inter_500Medium_Italic.ttf",
"weight": 500, "weight": 500,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_600SemiBold.ttf", "weight": 600 }, {
"path": "./assets/fonts/Inter_600SemiBold.ttf",
"weight": 600
},
{ {
"path": "./assets/fonts/Inter_600SemiBold_Italic.ttf", "path": "./assets/fonts/Inter_600SemiBold_Italic.ttf",
"weight": 600, "weight": 600,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_700Bold.ttf", "weight": 700 }, {
"path": "./assets/fonts/Inter_700Bold.ttf",
"weight": 700
},
{ {
"path": "./assets/fonts/Inter_700Bold_Italic.ttf", "path": "./assets/fonts/Inter_700Bold_Italic.ttf",
"weight": 700, "weight": 700,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_800ExtraBold.ttf", "weight": 800 }, {
"path": "./assets/fonts/Inter_800ExtraBold.ttf",
"weight": 800
},
{ {
"path": "./assets/fonts/Inter_800ExtraBold_Italic.ttf", "path": "./assets/fonts/Inter_800ExtraBold_Italic.ttf",
"weight": 800, "weight": 800,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/Inter_900Black.ttf", "weight": 900 }, {
"path": "./assets/fonts/Inter_900Black.ttf",
"weight": 900
},
{ {
"path": "./assets/fonts/Inter_900Black_Italic.ttf", "path": "./assets/fonts/Inter_900Black_Italic.ttf",
"weight": 900, "weight": 900,
@@ -113,49 +138,73 @@
{ {
"fontFamily": "Source Serif 4", "fontFamily": "Source Serif 4",
"fontDefinitions": [ "fontDefinitions": [
{ "path": "./assets/fonts/SourceSerif4_200ExtraLight.ttf", "weight": 200 }, {
"path": "./assets/fonts/SourceSerif4_200ExtraLight.ttf",
"weight": 200
},
{ {
"path": "./assets/fonts/SourceSerif4_200ExtraLight_Italic.ttf", "path": "./assets/fonts/SourceSerif4_200ExtraLight_Italic.ttf",
"weight": 200, "weight": 200,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_300Light.ttf", "weight": 300 }, {
"path": "./assets/fonts/SourceSerif4_300Light.ttf",
"weight": 300
},
{ {
"path": "./assets/fonts/SourceSerif4_300Light_Italic.ttf", "path": "./assets/fonts/SourceSerif4_300Light_Italic.ttf",
"weight": 300, "weight": 300,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_400Regular.ttf", "weight": 400 }, {
"path": "./assets/fonts/SourceSerif4_400Regular.ttf",
"weight": 400
},
{ {
"path": "./assets/fonts/SourceSerif4_400Regular_Italic.ttf", "path": "./assets/fonts/SourceSerif4_400Regular_Italic.ttf",
"weight": 400, "weight": 400,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_500Medium.ttf", "weight": 500 }, {
"path": "./assets/fonts/SourceSerif4_500Medium.ttf",
"weight": 500
},
{ {
"path": "./assets/fonts/SourceSerif4_500Medium_Italic.ttf", "path": "./assets/fonts/SourceSerif4_500Medium_Italic.ttf",
"weight": 500, "weight": 500,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_600SemiBold.ttf", "weight": 600 }, {
"path": "./assets/fonts/SourceSerif4_600SemiBold.ttf",
"weight": 600
},
{ {
"path": "./assets/fonts/SourceSerif4_600SemiBold_Italic.ttf", "path": "./assets/fonts/SourceSerif4_600SemiBold_Italic.ttf",
"weight": 600, "weight": 600,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_700Bold.ttf", "weight": 700 }, {
"path": "./assets/fonts/SourceSerif4_700Bold.ttf",
"weight": 700
},
{ {
"path": "./assets/fonts/SourceSerif4_700Bold_Italic.ttf", "path": "./assets/fonts/SourceSerif4_700Bold_Italic.ttf",
"weight": 700, "weight": 700,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_800ExtraBold.ttf", "weight": 800 }, {
"path": "./assets/fonts/SourceSerif4_800ExtraBold.ttf",
"weight": 800
},
{ {
"path": "./assets/fonts/SourceSerif4_800ExtraBold_Italic.ttf", "path": "./assets/fonts/SourceSerif4_800ExtraBold_Italic.ttf",
"weight": 800, "weight": 800,
"style": "italic" "style": "italic"
}, },
{ "path": "./assets/fonts/SourceSerif4_900Black.ttf", "weight": 900 }, {
"path": "./assets/fonts/SourceSerif4_900Black.ttf",
"weight": 900
},
{ {
"path": "./assets/fonts/SourceSerif4_900Black_Italic.ttf", "path": "./assets/fonts/SourceSerif4_900Black_Italic.ttf",
"weight": 900, "weight": 900,
@@ -204,7 +253,9 @@
] ]
} }
} }
] ],
"expo-web-browser",
"expo-image"
], ],
"experiments": { "experiments": {
"typedRoutes": true, "typedRoutes": true,
@@ -213,7 +264,7 @@
"extra": { "extra": {
"router": {}, "router": {},
"eas": { "eas": {
"projectId": "61092d23-36aa-418e-929d-ea40dc912e8f" "projectId": "c54ea4e5-27da-4066-b081-db8005ecf70a"
} }
} }
} }

View File

@@ -10,8 +10,8 @@
"ios": "expo start --ios", "ios": "expo start --ios",
"web": "expo start --web", "web": "expo start --web",
"lint": "expo lint", "lint": "expo lint",
"build:ios": "eas build --profile development --platform ios --non-interactive", "build:ios": "bunx eas-cli build --profile development --platform ios --non-interactive",
"build:ios-simulator": "eas build --profile development-simulator --platform ios --non-interactive", "build:ios-simulator": "bunx eas-cli build --profile development-simulator --platform ios --non-interactive",
"debugger": "bun run scripts/open-debugger.ts" "debugger": "bun run scripts/open-debugger.ts"
}, },
"dependencies": { "dependencies": {
@@ -19,42 +19,38 @@
"@expo-google-fonts/source-serif-4": "^0.4.1", "@expo-google-fonts/source-serif-4": "^0.4.1",
"@expo/vector-icons": "^15.0.3", "@expo/vector-icons": "^15.0.3",
"@json-render/react-native": "^0.13.0", "@json-render/react-native": "^0.13.0",
"@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.8",
"@tanstack/react-query": "^5.90.21", "@tanstack/react-query": "^5.90.21",
"expo": "~54.0.33", "expo": "^56.0.0",
"expo-constants": "~18.0.13", "expo-constants": "~56.0.18",
"expo-dev-client": "~6.0.20", "expo-dev-client": "~56.0.20",
"expo-font": "~14.0.11", "expo-font": "~56.0.7",
"expo-haptics": "~15.0.8", "expo-haptics": "~56.0.3",
"expo-image": "~3.0.11", "expo-image": "~56.0.11",
"expo-linking": "~8.0.11", "expo-linking": "~56.0.14",
"expo-location": "~19.0.8", "expo-location": "~56.0.18",
"expo-router": "~6.0.23", "expo-router": "~56.2.11",
"expo-splash-screen": "~31.0.13", "expo-splash-screen": "~56.0.10",
"expo-status-bar": "~3.0.9", "expo-status-bar": "~56.0.4",
"expo-symbols": "~1.0.8", "expo-symbols": "~56.0.6",
"expo-system-ui": "~6.0.9", "expo-system-ui": "~56.0.5",
"expo-web-browser": "~15.0.10", "expo-web-browser": "~56.0.5",
"react": "19.1.0", "react": "19.2.3",
"react-dom": "19.1.0", "react-dom": "19.2.3",
"react-native": "0.81.5", "react-native": "0.85.3",
"react-native-gesture-handler": "~2.28.0", "react-native-gesture-handler": "~2.31.1",
"react-native-reanimated": "~4.1.1", "react-native-reanimated": "4.3.1",
"react-native-safe-area-context": "~5.6.0", "react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.16.0", "react-native-screens": "4.25.2",
"react-native-svg": "15.12.1", "react-native-svg": "15.15.4",
"react-native-web": "~0.21.0", "react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1", "react-native-worklets": "0.8.3",
"twrnc": "^4.16.0", "twrnc": "^4.16.0",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "~19.1.0", "@types/react": "~19.2.10",
"eas-cli": "^18.0.1",
"eslint": "^9.25.0", "eslint": "^9.25.0",
"eslint-config-expo": "~10.0.0", "eslint-config-expo": "~56.0.4",
"typescript": "^6" "typescript": "~6.0.3"
} }
} }

View File

@@ -8,14 +8,16 @@ import type { ServerWebSocket } from "bun"
const PROXY_PORT = parseInt(process.env.PROXY_PORT || "8080", 10) const PROXY_PORT = parseInt(process.env.PROXY_PORT || "8080", 10)
const PROXY_HOST = process.env.PROXY_HOST || "0.0.0.0" const PROXY_HOST = process.env.PROXY_HOST || "0.0.0.0"
const METRO_HOST = process.env.METRO_HOST || "localhost"
const METRO_PORT = parseInt(process.env.METRO_PORT || "8081", 10) const METRO_PORT = parseInt(process.env.METRO_PORT || "8081", 10)
const METRO_BASE = `http://127.0.0.1:${METRO_PORT}` const METRO_BASE = `http://${METRO_HOST}:${METRO_PORT}`
const METRO_WS_BASE = `ws://${METRO_HOST}:${METRO_PORT}`
function forwardHeaders(headers: Headers): Headers { function forwardHeaders(headers: Headers): Headers {
const result = new Headers(headers) const result = new Headers(headers)
result.delete("origin") result.delete("origin")
result.delete("referer") result.delete("referer")
result.set("host", `127.0.0.1:${METRO_PORT}`) result.set("host", `${METRO_HOST}:${METRO_PORT}`)
return result return result
} }
@@ -40,7 +42,7 @@ Bun.serve<WsData>({
// WebSocket upgrade — bridge to Metro's ws endpoint // WebSocket upgrade — bridge to Metro's ws endpoint
if (req.headers.get("upgrade")?.toLowerCase() === "websocket") { if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
const wsUrl = `ws://127.0.0.1:${METRO_PORT}${url.pathname}${url.search}` const wsUrl = `${METRO_WS_BASE}${url.pathname}${url.search}`
const upstream = new WebSocket(wsUrl) const upstream = new WebSocket(wsUrl)
// Wait for upstream to connect before upgrading the client // Wait for upstream to connect before upgrading the client
@@ -65,12 +67,12 @@ Bun.serve<WsData>({
// HTTP proxy // HTTP proxy
const upstream = `${METRO_BASE}${url.pathname}${url.search}` const upstream = `${METRO_BASE}${url.pathname}${url.search}`
const body = req.body ? await req.arrayBuffer() : undefined const body = req.body ? await req.arrayBuffer() : undefined
const res = await fetch(upstream, { const res = await fetchUpstream(upstream, req.method, forwardHeaders(req.headers), body)
method: req.method, if (res == null) {
headers: forwardHeaders(req.headers), return new Response(`Metro is not reachable on ${METRO_HOST}. Restart the Expo dev server.`, {
body, status: 502,
redirect: "manual", })
}) }
return new Response(res.body, { return new Response(res.body, {
status: res.status, status: res.status,
@@ -121,9 +123,7 @@ async function printDebuggerUrl() {
const target = targets.find((t) => t.reactNative?.capabilities?.prefersFuseboxFrontend) const target = targets.find((t) => t.reactNative?.capabilities?.prefersFuseboxFrontend)
if (!target) return if (!target) return
const wsPath = target.webSocketDebuggerUrl const wsPath = getProxyWebSocketPath(target.webSocketDebuggerUrl)
.replace(/^ws:\/\//, "")
.replace(`127.0.0.1:${METRO_PORT}`, `${tsIp}:${PROXY_PORT}`)
console.log( console.log(
`\n React Native DevTools:\n ${base}/debugger-frontend/rn_fusebox.html?ws=${encodeURIComponent(wsPath)}&sources.hide_add_folder=true&unstable_enableNetworkPanel=true\n`, `\n React Native DevTools:\n ${base}/debugger-frontend/rn_fusebox.html?ws=${encodeURIComponent(wsPath)}&sources.hide_add_folder=true&unstable_enableNetworkPanel=true\n`,
@@ -131,9 +131,28 @@ async function printDebuggerUrl() {
} }
console.log( console.log(
`[proxy] listening on ${PROXY_HOST}:${PROXY_PORT}, forwarding to 127.0.0.1:${METRO_PORT}`, `[proxy] listening on ${PROXY_HOST}:${PROXY_PORT}, forwarding to ${METRO_HOST}:${METRO_PORT}`,
) )
async function fetchUpstream(
upstream: string,
method: string,
headers: Headers,
body: ArrayBuffer | undefined,
) {
try {
return await fetch(upstream, {
method,
headers,
body,
redirect: "manual",
})
} catch {
console.error(`[proxy] ${method} ${upstream} failed; Metro is not reachable`)
return null
}
}
function isDebugTarget(value: unknown): value is DebugTarget { function isDebugTarget(value: unknown): value is DebugTarget {
if (!isRecord(value) || typeof value.webSocketDebuggerUrl !== "string") return false if (!isRecord(value) || typeof value.webSocketDebuggerUrl !== "string") return false
@@ -149,6 +168,11 @@ function isDebugTarget(value: unknown): value is DebugTarget {
return prefersFuseboxFrontend === undefined || typeof prefersFuseboxFrontend === "boolean" return prefersFuseboxFrontend === undefined || typeof prefersFuseboxFrontend === "boolean"
} }
function getProxyWebSocketPath(webSocketDebuggerUrl: string) {
const url = new URL(webSocketDebuggerUrl)
return `${tsIp}:${PROXY_PORT}${url.pathname}${url.search}`
}
function isRecord(value: unknown): value is Record<string, unknown> { function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null return typeof value === "object" && value !== null
} }

View File

@@ -4,7 +4,6 @@
import { $ } from "bun" import { $ } from "bun"
const PROXY_PORT = process.env.PROXY_PORT || "8080" const PROXY_PORT = process.env.PROXY_PORT || "8080"
const METRO_PORT = process.env.METRO_PORT || "8081"
const tsIp = (await $`tailscale ip -4`.text()).trim() const tsIp = (await $`tailscale ip -4`.text()).trim()
const base = `http://${tsIp}:${PROXY_PORT}` const base = `http://${tsIp}:${PROXY_PORT}`
@@ -37,9 +36,7 @@ if (!target) {
process.exit(1) process.exit(1)
} }
const wsUrl = target.webSocketDebuggerUrl const wsUrl = getProxyWebSocketPath(target.webSocketDebuggerUrl)
.replace(/^ws:\/\//, "")
.replace(`127.0.0.1:${METRO_PORT}`, `${tsIp}:${PROXY_PORT}`)
const url = `${base}/debugger-frontend/rn_fusebox.html?ws=${encodeURIComponent(wsUrl)}&sources.hide_add_folder=true&unstable_enableNetworkPanel=true` const url = `${base}/debugger-frontend/rn_fusebox.html?ws=${encodeURIComponent(wsUrl)}&sources.hide_add_folder=true&unstable_enableNetworkPanel=true`
@@ -71,6 +68,11 @@ function isDebugTarget(value: unknown): value is DebugTarget {
return prefersFuseboxFrontend === undefined || typeof prefersFuseboxFrontend === "boolean" return prefersFuseboxFrontend === undefined || typeof prefersFuseboxFrontend === "boolean"
} }
function getProxyWebSocketPath(webSocketDebuggerUrl: string) {
const url = new URL(webSocketDebuggerUrl)
return `${tsIp}:${PROXY_PORT}${url.pathname}${url.search}`
}
function isRecord(value: unknown): value is Record<string, unknown> { function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null return typeof value === "object" && value !== null
} }

View File

@@ -1,14 +1,47 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
PROXY_PORT=8080 PROXY_PORT=${PROXY_PORT:-8080}
METRO_PORT=8081 METRO_HOST=${METRO_HOST:-localhost}
METRO_PORT=${METRO_PORT:-8081}
TS_IP=$(tailscale ip -4) TS_IP=$(tailscale ip -4)
# Start a reverse proxy so Metro sees all requests as loopback. port_is_open() {
# This makes debugger endpoints (/debugger-frontend, /json, /open-debugger) (: >"/dev/tcp/$1/$2") >/dev/null 2>&1
# accessible through the Tailscale IP. }
PROXY_PORT=$PROXY_PORT METRO_PORT=$METRO_PORT bun run scripts/dev-proxy.ts &
ensure_port_available() {
local port=$1
local name=$2
if port_is_open localhost "$port"; then
echo "$name port $port is already in use." >&2
echo "Stop the existing process or set ${name}_PORT to another value." >&2
exit 1
fi
}
wait_for_metro() {
for _ in {1..120}; do
if port_is_open "$METRO_HOST" "$METRO_PORT"; then
return 0
fi
sleep 0.5
done
echo "Metro did not start on ${METRO_HOST}:${METRO_PORT}." >&2
return 1
}
ensure_port_available "$PROXY_PORT" PROXY
ensure_port_available "$METRO_PORT" METRO
# Start the proxy only after Metro is listening. Otherwise an iOS client can hit
# the proxy during Expo startup and get a misleading upstream connection error.
(
wait_for_metro
exec env PROXY_PORT=$PROXY_PORT METRO_HOST=$METRO_HOST METRO_PORT=$METRO_PORT bun run scripts/dev-proxy.ts
) &
PROXY_PID=$! PROXY_PID=$!
trap "kill $PROXY_PID 2>/dev/null" EXIT trap "kill $PROXY_PID 2>/dev/null" EXIT

View File

@@ -1,5 +1,5 @@
import Feather from "@expo/vector-icons/Feather" import Feather from "@expo/vector-icons/Feather"
import { type PressableProps, Pressable, View } from "react-native" import { type PressableProps, Pressable, type StyleProp, View, type ViewStyle } from "react-native"
import tw from "twrnc" import tw from "twrnc"
import { SansSerifText } from "./sans-serif-text" import { SansSerifText } from "./sans-serif-text"
@@ -14,9 +14,10 @@ function ButtonIcon({ name }: ButtonIconProps) {
return <Feather name={name} size={18} color={tw.color("text-stone-100 dark:text-stone-200")} /> return <Feather name={name} size={18} color={tw.color("text-stone-100 dark:text-stone-200")} />
} }
type ButtonProps = Omit<PressableProps, "children"> & { type ButtonProps = Omit<PressableProps, "children" | "style"> & {
label: string label: string
leadingIcon?: React.ReactNode leadingIcon?: React.ReactNode
style?: StyleProp<ViewStyle>
trailingIcon?: React.ReactNode trailingIcon?: React.ReactNode
} }

View File

@@ -16,9 +16,9 @@
"lottie-react": "^2.4.1", "lottie-react": "^2.4.1",
"lucide-react": "^0.577.0", "lucide-react": "^0.577.0",
"motion": "^12.35.0", "motion": "^12.35.0",
"react": "^19.2.4", "react": "19.2.3",
"react-aria-components": "^1.16.0", "react-aria-components": "^1.16.0",
"react-dom": "^19.2.4", "react-dom": "19.2.3",
"react-router": "7.12.0", "react-router": "7.12.0",
"resend": "^6.9.3", "resend": "^6.9.3",
"streamdown": "^2.4.0" "streamdown": "^2.4.0"

1834
bun.lock

File diff suppressed because it is too large Load Diff

2
bunfig.toml Normal file
View File

@@ -0,0 +1,2 @@
[install]
linker = "hoisted"

View File

@@ -45,7 +45,7 @@
# node_modules is content-addressed. If bun.lock or package manifests # node_modules is content-addressed. If bun.lock or package manifests
# change, Nix will report the new hash to put here. # change, Nix will report the new hash to put here.
nodeModulesHashes = { nodeModulesHashes = {
x86_64-linux = "sha256-apVZaFGf9OKpil1WdcQ1CJODsIdjLWlBBZErHg5mjZA="; x86_64-linux = "sha256-8uhlaQAFfCgGdUlrz8sqhtIkC/WfdasbTCi3p/NkU/w=";
}; };
checkSystems = lib.attrNames nodeModulesHashes; checkSystems = lib.attrNames nodeModulesHashes;
@@ -53,7 +53,9 @@
# so source-only edits do not force Bun to reinstall. # so source-only edits do not force Bun to reinstall.
dependencySource = lib.fileset.toSource { dependencySource = lib.fileset.toSource {
root = ./.; root = ./.;
fileset = lib.fileset.fileFilter (file: file.name == "bun.lock" || file.name == "package.json") ./.; fileset = lib.fileset.fileFilter (
file: file.name == "bun.lock" || file.name == "package.json" || file.name == "bunfig.toml"
) ./.;
}; };
# Checks run against a clean source tree, even when using `path:.`. # Checks run against a clean source tree, even when using `path:.`.

View File

@@ -10,6 +10,7 @@
"expo": "cd apps/freya-client && bun run start", "expo": "cd apps/freya-client && bun run start",
"drizzle-studio": "TS_IP=$(tailscale ip -4); echo \"Drizzle Studio: https://local.drizzle.studio/?host=${TS_IP}&port=4983\"; cd apps/freya-backend && bunx drizzle-kit studio --host 0.0.0.0 --port 4983", "drizzle-studio": "TS_IP=$(tailscale ip -4); echo \"Drizzle Studio: https://local.drizzle.studio/?host=${TS_IP}&port=4983\"; cd apps/freya-backend && bunx drizzle-kit studio --host 0.0.0.0 --port 4983",
"freya-backend": "TS_IP=$(tailscale ip -4); echo \"Freya Backend: http://${TS_IP}:3000\"; echo \"\"; echo \"------------------ Bun Debugger ------------------\"; echo \"https://debug.bun.sh/#${TS_IP}:6499\"; echo \"------------------ Bun Debugger ------------------\"; echo \"\"; cd apps/freya-backend && bun run dev", "freya-backend": "TS_IP=$(tailscale ip -4); echo \"Freya Backend: http://${TS_IP}:3000\"; echo \"\"; echo \"------------------ Bun Debugger ------------------\"; echo \"https://debug.bun.sh/#${TS_IP}:6499\"; echo \"------------------ Bun Debugger ------------------\"; echo \"\"; cd apps/freya-backend && bun run dev",
"client": "bun run --elide-lines=0 --filter freya-client start",
"admin-dashboard": "TS_IP=$(tailscale ip -4); echo \"Admin Dashboard: http://${TS_IP}:5174\"; cd apps/admin-dashboard && bun run dev --host 0.0.0.0", "admin-dashboard": "TS_IP=$(tailscale ip -4); echo \"Admin Dashboard: http://${TS_IP}:5174\"; cd apps/admin-dashboard && bun run dev --host 0.0.0.0",
"agent-test-cli": "cd apps/agent-test-cli && bun run start", "agent-test-cli": "cd apps/agent-test-cli && bun run start",
"test": "bun run --filter '*' test", "test": "bun run --filter '*' test",