From 5edfb27ff8c4e613052ca9a85d414cf682908e88 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 18:46:54 +0000 Subject: [PATCH 1/2] fix: remove "type": "module" from backend package.json to fix Vercel ESM error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next.js 16.2 has a known regression (vercel/next.js#91661) where the .next/package.json CJS boundary marker is not included in the Vercel serverless function bundle. This causes Node.js to walk up to the project's package.json, see "type": "module", and treat the compiled server bundles as ESM — breaking the require() calls in the generated route files. The fix (vercel/next.js#93612) is only in canary (16.3.0-canary.17+), not yet in a stable release. Removing "type": "module" is safe here because: - All backend source is TypeScript (no .js files) - next.config is .mjs (explicit ESM) - .eslintrc is .cjs (explicit CJS) - Scripts use tsx (handles ESM regardless) - TypeScript's moduleResolution: bundler is independent of this field Co-Authored-By: Konstantin Wohlwend --- apps/backend/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 4b8c467eba..e1479f108d 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -3,7 +3,6 @@ "version": "2.8.90", "repository": "https://github.com/hexclave/stack-auth", "private": true, - "type": "module", "scripts": { "clean": "rimraf src/generated && rimraf .next && rimraf node_modules", "typecheck": "tsc --noEmit", From e971fd8fcd56ca023e1e6e24dffce001b15bc12a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 19:07:19 +0000 Subject: [PATCH 2/2] fix: patch required-server-files manifest to include CJS boundary marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workaround for vercel/next.js#91661 — Next.js 16.2 omits .next/package.json from the required-server-files manifest, so Vercel's serverless functions don't get the CJS boundary marker. Without it, Node.js resolves the project's "type": "module" and treats the compiled server bundles as ESM, breaking require() calls at runtime. This adds a postbuild script that patches the manifest to include the file, matching the upstream fix in vercel/next.js#93612 (only in canary as of 16.2.6). The previous approach of removing "type": "module" broke the codegen step which uses tsx/esbuild and needs ESM for top-level await support. Co-Authored-By: Konstantin Wohlwend --- apps/backend/package.json | 3 +- .../scripts/patch-required-server-files.mjs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 apps/backend/scripts/patch-required-server-files.mjs diff --git a/apps/backend/package.json b/apps/backend/package.json index e1479f108d..731abb94bd 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -3,6 +3,7 @@ "version": "2.8.90", "repository": "https://github.com/hexclave/stack-auth", "private": true, + "type": "module", "scripts": { "clean": "rimraf src/generated && rimraf .next && rimraf node_modules", "typecheck": "tsc --noEmit", @@ -13,7 +14,7 @@ "dev": "BACKEND_PORT=${STACK_DEV_FALLBACK_BACKEND:+${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}10} && BACKEND_PORT=${BACKEND_PORT:-${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}02} && concurrently -n \"dev,codegen,prisma-studio,email-queue,cron-jobs,bulldozer-studio\" -k \"STACK_DISABLE_REACT_ASYNC_DEBUG_INFO=${STACK_DISABLE_REACT_ASYNC_DEBUG_INFO:-true} next dev --port $BACKEND_PORT ${STACK_BACKEND_DEV_EXTRA_ARGS:-}\" \"pnpm run codegen:watch\" \"pnpm run prisma-studio\" \"pnpm run run-email-queue\" \"pnpm run run-cron-jobs\" \"pnpm run run-bulldozer-studio\"", "dev:inspect": "STACK_BACKEND_DEV_EXTRA_ARGS=\"--inspect\" pnpm run dev", "dev:profile": "STACK_BACKEND_DEV_EXTRA_ARGS=\"--experimental-cpu-prof\" pnpm run dev", - "build": "pnpm run codegen && next build", + "build": "pnpm run codegen && next build && node scripts/patch-required-server-files.mjs", "docker-build": "pnpm run codegen && next build --experimental-build-mode compile", "build-self-host-migration-script": "tsdown --config scripts/db-migrations.tsdown.config.ts", "analyze-bundle": "next experimental-analyze", diff --git a/apps/backend/scripts/patch-required-server-files.mjs b/apps/backend/scripts/patch-required-server-files.mjs new file mode 100644 index 0000000000..cb9aec42ab --- /dev/null +++ b/apps/backend/scripts/patch-required-server-files.mjs @@ -0,0 +1,36 @@ +/** + * Workaround for https://github.com/vercel/next.js/issues/91661 + * + * Next.js 16.2 doesn't include `.next/package.json` (the CJS boundary marker) + * in the `required-server-files.json` manifest. Without it, Vercel's serverless + * functions inherit `"type": "module"` from the project's package.json, causing + * `require is not defined` at runtime. + * + * The upstream fix landed in vercel/next.js#93612 (canary only as of 16.2.6). + * This script patches the manifest after `next build` to include the file. + * + * Remove this script once we upgrade to a stable Next.js release containing the fix. + */ + +import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { join } from 'path'; + +const distDir = '.next'; +const manifestPath = join(distDir, 'required-server-files.json'); + +if (!existsSync(manifestPath)) { + // Not all build modes produce this manifest (e.g. compile-only docker builds) + process.exit(0); +} + +const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); + +const packageJsonEntry = join(distDir, 'package.json'); + +if (!manifest.files.includes(packageJsonEntry)) { + manifest.files.push(packageJsonEntry); + writeFileSync(manifestPath, JSON.stringify(manifest)); + console.log(`Patched ${manifestPath}: added ${packageJsonEntry} to files array`); +} else { + console.log(`${manifestPath} already includes ${packageJsonEntry}, no patch needed`); +}