diff --git a/apps/studio/.npmrc b/apps/studio/.npmrc new file mode 100644 index 000000000..769c142af --- /dev/null +++ b/apps/studio/.npmrc @@ -0,0 +1,3 @@ +# Vercel deployment configuration +# Use hoisted node-linker to avoid symlinks that break Vercel serverless functions +node-linker=hoisted diff --git a/apps/studio/package.json b/apps/studio/package.json index 10dca6815..b0a7244f6 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -23,6 +23,7 @@ "@ai-sdk/openai": "^3.0.52", "@ai-sdk/react": "^3.0.160", "@hono/node-server": "^1.19.14", + "@libsql/client": "^0.17.2", "@objectstack/client": "workspace:*", "@objectstack/client-react": "workspace:*", "@objectstack/driver-memory": "workspace:*", @@ -57,6 +58,7 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.8", "ai": "^6.0.158", + "better-sqlite3": "^12.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "hono": "^4.12.12", diff --git a/apps/studio/scripts/build-vercel.sh b/apps/studio/scripts/build-vercel.sh index 10a5507f4..2418c8b68 100755 --- a/apps/studio/scripts/build-vercel.sh +++ b/apps/studio/scripts/build-vercel.sh @@ -28,41 +28,15 @@ cd apps/studio # 2. Bundle API serverless function node scripts/bundle-api.mjs -# 3. Copy native/external modules into local node_modules for Vercel packaging. +# 3. External dependencies are now handled by adding them as direct dependencies +# in apps/studio/package.json. Vercel installs them automatically during deployment. # -# Unlike hotcrm (which uses shamefully-hoist=true), this monorepo uses pnpm's -# default strict node_modules structure. Transitive native dependencies like -# better-sqlite3 only exist in the monorepo root's node_modules/.pnpm/ virtual -# store — they're NOT symlinked into apps/studio/node_modules/. +# Packages marked as external in bundle-api.mjs (@libsql/client, better-sqlite3, +# @ai-sdk/*) are listed as direct dependencies in package.json, so Vercel includes +# them in the serverless function's deployment package via normal pnpm install. # -# The vercel.json includeFiles pattern references node_modules/ relative to -# apps/studio/, so we must copy the actual module files here for Vercel to -# include them in the serverless function's deployment package. -# -# Note: @libsql/client is now bundled by esbuild, so we no longer copy it. -echo "[build-vercel] Copying external native modules to local node_modules..." -for mod in better-sqlite3; do - src="../../node_modules/$mod" - if [ -e "$src" ]; then - dest="node_modules/$mod" - mkdir -p "$(dirname "$dest")" - cp -rL "$src" "$dest" - echo "[build-vercel] ✓ Copied $mod" - else - echo "[build-vercel] ⚠ $mod not found at $src (skipped)" - fi -done -# Copy the @ai-sdk scope (dynamically loaded provider packages) -if [ -d "../../node_modules/@ai-sdk" ]; then - mkdir -p "node_modules/@ai-sdk" - for pkg in ../../node_modules/@ai-sdk/*/; do - pkgname="$(basename "$pkg")" - cp -rL "$pkg" "node_modules/@ai-sdk/$pkgname" - done - echo "[build-vercel] ✓ Copied @ai-sdk/*" -else - echo "[build-vercel] ⚠ @ai-sdk not found (skipped)" -fi +# This avoids the cp -rL infinite recursion issue that occurs when copying packages +# that are already direct dependencies with circular symlinks. # 4. Copy Vite build output to public/ for static file serving rm -rf public diff --git a/apps/studio/scripts/bundle-api.mjs b/apps/studio/scripts/bundle-api.mjs index 9f1d8fc81..4f19101fa 100644 --- a/apps/studio/scripts/bundle-api.mjs +++ b/apps/studio/scripts/bundle-api.mjs @@ -16,9 +16,9 @@ import { build } from 'esbuild'; // Packages that cannot be bundled (native bindings / optional drivers) const EXTERNAL = [ - // @libsql/client is now bundled (pure JS, no native bindings) - // Bundling it solves Vercel deployment issues where external packages - // aren't properly included in the serverless function despite includeFiles config + // @libsql/client has platform-specific native bindings (@libsql/linux-x64-gnu, etc.) + // and must be kept external, then copied to node_modules and included via vercel.json + '@libsql/client', 'better-sqlite3', // AI SDK provider packages — dynamically imported based on env vars '@ai-sdk/anthropic', diff --git a/apps/studio/vercel.json b/apps/studio/vercel.json index 96e8daf72..3365216d1 100644 --- a/apps/studio/vercel.json +++ b/apps/studio/vercel.json @@ -13,7 +13,7 @@ "api/**/*.js": { "memory": 1024, "maxDuration": 300, - "includeFiles": "node_modules/{better-sqlite3,@ai-sdk}/**" + "includeFiles": "node_modules/{better-sqlite3,@libsql,@ai-sdk}/**" } }, "headers": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13a6faaa3..7be7f15ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -145,6 +145,9 @@ importers: '@hono/node-server': specifier: ^1.19.14 version: 1.19.14(hono@4.12.12) + '@libsql/client': + specifier: ^0.17.2 + version: 0.17.2 '@objectstack/client': specifier: workspace:* version: link:../../packages/client @@ -247,6 +250,9 @@ importers: ai: specifier: ^6.0.158 version: 6.0.159(zod@4.3.6) + better-sqlite3: + specifier: ^12.9.0 + version: 12.9.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -4245,8 +4251,8 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - caniuse-lite@1.0.30001787: - resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} + caniuse-lite@1.0.30001788: + resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -9865,7 +9871,7 @@ snapshots: autoprefixer@10.5.0(postcss@8.5.9): dependencies: browserslist: 4.28.2 - caniuse-lite: 1.0.30001787 + caniuse-lite: 1.0.30001788 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.9 @@ -9983,7 +9989,7 @@ snapshots: browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.10.18 - caniuse-lite: 1.0.30001787 + caniuse-lite: 1.0.30001788 electron-to-chromium: 1.5.336 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -10025,7 +10031,7 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - caniuse-lite@1.0.30001787: {} + caniuse-lite@1.0.30001788: {} ccount@2.0.1: {} @@ -12050,7 +12056,7 @@ snapshots: '@next/env': 16.2.3 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.18 - caniuse-lite: 1.0.30001787 + caniuse-lite: 1.0.30001788 postcss: 8.4.31 react: 19.2.5 react-dom: 19.2.5(react@19.2.5)