Complete ESM conversion and improve typing throughout the codebase#18
Merged
robdimarco-atxp merged 40 commits intomainfrom Sep 10, 2025
Merged
Conversation
**Problem**: Vercel deployment failed with error: "The functions property cannot be used in conjunction with the builds property" **Solution**: - Remove legacy builds property from vercel.json - Keep modern functions property for maxDuration configuration - Maintain routes and installCommand for single-service architecture Vercel now auto-detects the backend/server.ts file and builds it appropriately without needing explicit builds configuration.
**Problem**: Vercel deployment failed with error: "The pattern 'backend/server.ts' defined in functions doesn't match any Serverless Functions inside the api directory" **Root Cause**: Vercel expects functions to be in api/ directory, but we were referencing backend/server.ts in the functions config. **Solution**: Remove the functions config and let Vercel auto-detect the serverless function. Vercel will automatically: - Detect backend/server.ts as a Node.js serverless function - Apply appropriate default configurations - Handle the routing as specified in routes array **Result**: Cleaner config that works with Vercel's auto-detection system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Vercel deployment failed with error: "No Output Directory named 'public' found after the Build completed" **Root Cause**: Vercel expected static files in a public directory, but our single-service architecture serves everything through Express serverless function. **Solution**: - Add explicit builds configuration with @vercel/node - Specify backend/server.ts as the serverless function source - Remove outputDirectory config (not needed for serverless functions) - Keep installCommand to ensure proper build process **Result**: Vercel will treat this as a pure serverless function deployment without looking for static output directories. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: @atxp/client is an ES Module but TypeScript compiles to CommonJS, causing "ERR_REQUIRE_ESM" errors in Vercel serverless functions. **Solution**: - Keep existing CommonJS TypeScript configuration (simpler, more compatible) - Upgrade Vercel to Node.js 18.x runtime which better handles ESM/CommonJS interop - Increase maxLambdaSize to handle larger dependencies - Use builds configuration for explicit control **Changes**: - Reverted ES module experiments (package.json, tsconfig.json, server.ts) - Updated vercel.json with nodejs18.x runtime and larger lambda size - Maintains stable CommonJS build while fixing ES module import issues 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Updated from nodejs18.x to nodejs20.x for better ES module support - Node.js 20 has improved CommonJS/ES module interoperability - Provides better performance and security updates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…e compatibility - Convert static imports of @atxp/client and @atxp/common to dynamic imports in server.ts - Update atxp-utils.ts functions to use async/dynamic imports for ATXPAccount - Make findATXPAccount and validateATXPConnectionString async functions - Change ATXPAccount type annotation to any to avoid static import dependency - This resolves ERR_REQUIRE_ESM errors when deploying to Vercel serverless functions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update atxp-utils.test.ts to use async/await for findATXPAccount and validateATXPConnectionString - Update server.test.ts validation endpoint to handle async validateATXPConnectionString - Remove static ATXPAccount import from test file since it's now dynamically imported - All tests now pass with the new async implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…ire() compilation - Replace await import() with Function constructor approach to bypass TypeScript compilation - This prevents TypeScript from converting dynamic imports to require() calls - Should resolve ERR_REQUIRE_ESM errors in Vercel serverless environment - Tests fail due to Function constructor bypassing vitest mocks, but should work in production - Add type annotations for payment callback parameters 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add "type": "module" to package.json for native ESM support - Update TypeScript config to target ESNext modules instead of CommonJS - Add ESM __dirname polyfill using fileURLToPath and dirname - Remove Function constructor workarounds for dynamic imports - Use clean await import() syntax for @atxp/client and @atxp/common - Update all local imports to use .js extensions for ESM compatibility - Add vitest config for ESM test environment - Update dev script to use ts-node/esm loader This resolves ERR_REQUIRE_ESM errors by natively supporting ES modules while maintaining clean, readable code without workarounds. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update vercel.json to explicitly include frontend/build/** files - Add buildCommand to ensure both backend and frontend are built - Add comprehensive debugging to getStaticPath() function with multiple candidate paths - Include Vercel-specific paths like /var/task and /vercel/path0 - Add detailed logging to help debug Vercel deployment structure This should resolve the "No frontend build directory found" error by: 1. Ensuring frontend is built during Vercel deployment 2. Including frontend build files in the serverless function 3. Checking multiple possible paths where build files might be located 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Update vercel.json buildCommand to copy frontend/build to backend/ after building - Update getStaticPath() to prioritize backend/build directory for Vercel - Add enhanced debugging to check build directory contents - Simplify candidate paths focusing on the copied build location This ensures the frontend build files are included in the backend serverless function deployment, resolving the "No frontend build directory found" error in Vercel. Local testing confirms the server finds build files at the expected location. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace hardcoded localhost CORS origins with dynamic origin handling - In production, allow any origin since frontend and API are served from same domain - In development, maintain strict localhost-only CORS for security - Update SSE endpoint CORS headers to use dynamic origin detection - This resolves CORS errors when deploying to Vercel, Render, or other platforms The CORS error was: "Access-Control-Allow-Origin header has a value 'http://localhost:3000' that is not equal to the supplied origin" because the production frontend was served from Vercel domain, not localhost. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Use relative URL '/api/progress' in production instead of hardcoded localhost - Maintain localhost URL for development where SSE needs direct backend connection - Add environment-based URL detection with logging for debugging - This resolves CORS errors: frontend was connecting to localhost:3001 from Vercel domain The issue was that EventSource was hardcoded to http://localhost:3001/api/progress even in production, causing cross-origin requests from Vercel domain to localhost. Now it uses relative URLs in production since frontend/backend are same domain. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add robust production detection using hostname and protocol checks
- Add debug logging to understand environment detection in Vercel
- Set NODE_ENV=production during build command in vercel.json
- Use multiple fallback methods: NODE_ENV, hostname, and protocol
This should resolve SSE endpoint selection issues by using:
1. NODE_ENV === 'production' (primary)
2. !hostname.includes('localhost') (hostname-based)
3. protocol === 'https:' (protocol-based)
The debug logs will help identify which detection method works in Vercel.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use simple hostname check: localhost = development, anything else = production - Always use relative URLs for same-origin requests (deployed environments) - Only use localhost URLs when specifically running on localhost - Remove complex environment variable detection that wasn't working reliably - Simplify Vercel build command without environment variable overrides This approach is much more reliable: - localhost/127.0.0.1 → http://localhost:3001/api/progress (development) - vercel.app/render.com/etc → /api/progress (production, same-origin) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add cache busting strings to force new deployment - Add version number to SSE connection setup logs - This should help identify if Vercel is using old cached frontend builds - New build hash: main.a7ee31e9.js (was main.1abc448d.js) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Remove builds array from vercel.json to resolve the warning: "Due to builds existing in your configuration file, the Build and Development Settings defined in your Project Settings will not apply" This allows Vercel to properly handle frontend build deployment and use the buildCommand to copy frontend/build to backend/. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Use @vercel/node instead of nodejs20.x for proper function runtime specification. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Use @vercel/node runtime with engines.node specification in package.json instead of invalid nodejs22.x runtime format. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Remove functions configuration that was causing runtime version errors. Use rewrites to route all traffic to the Express server. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Specify backend as output directory to resolve 'No Output Directory named public found' error. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Use relative path /dist/server.js instead of /backend/dist/server.js since outputDirectory is already set to backend. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add api/index.js as Vercel function entry point - Export Express app from server.ts for serverless deployment - Update vercel.json to route all requests to /api serverless function - Remove outputDirectory to let Vercel handle function detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Set outputDirectory to frontend/build to resolve build error. Remove copying frontend build to backend since the serverless function can access both directories during runtime. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Copy frontend/build to backend/public for cleaner organization - Update static file path resolution to check public directory - Set backend as outputDirectory in vercel.json 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Use CommonJS module.exports with dynamic import to handle ES module compatibility in Vercel's serverless runtime environment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Rename api/index.js to api/index.mjs to force ES module treatment - Use proper ES module syntax with top-level await - Remove CommonJS workaround since we've converted everything to ESM 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…s functions with static imports - Change findATXPAccount from Promise<any> to ATXPAccount for better typing - Revert validateATXPConnectionString from async to sync function - Update all callers to use synchronous versions - Fix test mocks to work with static imports using vi.mocked() This reverts the dynamic import approach to test if static imports work with Vercel deployment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Restore expect(ATXPAccount).toHaveBeenCalledWith() assertions to verify that findATXPAccount calls the constructor with correct parameters. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
… detection - Replace hostname-based detection (localhost/127.0.0.1) with NODE_ENV check - Development mode (npm start): uses direct backend URL regardless of hostname - Production mode (npm run build): uses relative URL for same-origin requests - This fixes cases where development server runs on non-localhost hostnames Tested and confirmed working on: - Local development (npm run dev and npm start) - Render deployment - Vercel deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Import ATXPAccount type from @atxp/client - Fix account parameter in pollForTaskCompletion function signature - Fix account variable declaration in /api/texts endpoint - Maintains type safety throughout the codebase 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR completes the ESM (ES Module) conversion of the backend and improves typing throughout the codebase, along with several deployment and development experience improvements.
It was motivated by wanting to get things working on Vercel and Render
Backend Changes
"type": "module"Frontend Changes
NODE_ENVdetectionTesting & Development
vi.mocked()Deployment
Technical Details
ESM Conversion
"type": "module"to package.json for native ESM supportfileURLToPathanddirname.jsextensions for ESM compatibilityawait import()syntaxFrontend Environment Detection
window.location.hostname === 'localhost'(breaks on IP addresses, custom hostnames)process.env.NODE_ENV === 'development'(universal development detection)Benefits
ATXPAccounttypes instead ofanythroughout codebaseTest Plan
npm run devandnpm start)Background
The original async approach with dynamic imports was implemented to fix Vercel deployment issues. After completing the full ESM conversion, testing confirmed that static imports now work properly with Vercel, allowing us to revert to cleaner, properly-typed synchronous functions.
🤖 Generated with Claude Code