Skip to content

Conversation

@TooTallNate
Copy link
Member

@TooTallNate TooTallNate commented Dec 30, 2025

TL;DR

Enable "use step" functions to be defined outside of the app directory with proper path alias resolution.

What changed?

  • Modified the build process to use a two-pass approach:
    1. First pass creates an intermediate bundle that externalizes non-step code
    2. Second pass bundles the intermediate output with all dependencies
  • Converted pathsAliasHelper to a proper step function called pathsAliasStep that can be imported directly
  • Updated the pathsAliasWorkflow to call the external step function directly instead of using a wrapper step

How to test?

Run the pathsAliasWorkflow test which imports a step function from outside the workbench directory using a TypeScript path alias. The workflow should complete successfully and return "pathsAliasStep".

Why make this change?

This enhancement provides more flexibility in organizing step functions by allowing them to be defined anywhere in the codebase, not just within the app directory. It enables better code organization and reuse of step functions across different parts of the application while maintaining proper path alias resolution.

@changeset-bot
Copy link

changeset-bot bot commented Dec 30, 2025

🦋 Changeset detected

Latest commit: def292b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@workflow/sveltekit Patch
@workflow/builders Patch
@workflow/astro Patch
@workflow/nitro Patch
@workflow/next Patch
workflow Patch
@workflow/cli Patch
@workflow/nuxt Patch
@workflow/ai Patch
@workflow/world-testing Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Dec 30, 2025

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 308 0 11 319
❌ 💻 Local Development 254 28 8 290
✅ 📦 Local Production 282 0 8 290
✅ 🐘 Local Postgres 282 0 8 290
✅ 🪟 Windows 29 0 0 29
❌ 🌍 Community Worlds 116 12 0 128
Total 1271 40 35 1346

❌ Failed Tests

💻 Local Development (28 failed)

nuxt-stable (28 failed):

  • addTenWorkflow
  • addTenWorkflow
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • webhook route with invalid token
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • retryAttemptCounterWorkflow
  • retryableAndFatalErrorWorkflow
  • maxRetriesZeroWorkflow - maxRetries=0 runs once without retrying
  • stepDirectCallWorkflow - calling step functions directly outside workflow context
  • crossFileErrorWorkflow - stack traces work across imported modules
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check endpoint - workflow and step endpoints respond to __health query parameter
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
🌍 Community Worlds (12 failed)

mongodb (1 failed):

  • webhookWorkflow

redis (1 failed):

  • webhookWorkflow

starter (9 failed):

  • addTenWorkflow
  • addTenWorkflow
  • retryAttemptCounterWorkflow
  • crossFileErrorWorkflow - stack traces work across imported modules
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly

turso (1 failed):

  • webhookWorkflow

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 28 0 1
✅ example 28 0 1
✅ express 28 0 1
✅ fastify 28 0 1
✅ hono 28 0 1
✅ nextjs-turbopack 28 0 1
✅ nextjs-webpack 28 0 1
✅ nitro 28 0 1
✅ nuxt 28 0 1
✅ sveltekit 28 0 1
✅ vite 28 0 1
❌ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 28 0 1
✅ express-stable 28 0 1
✅ fastify-stable 28 0 1
✅ hono-stable 28 0 1
✅ nextjs-turbopack-stable 29 0 0
✅ nextjs-webpack-stable 29 0 0
✅ nitro-stable 28 0 1
❌ nuxt-stable 0 28 1
✅ sveltekit-stable 28 0 1
✅ vite-stable 28 0 1
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 28 0 1
✅ express-stable 28 0 1
✅ fastify-stable 28 0 1
✅ hono-stable 28 0 1
✅ nextjs-turbopack-stable 29 0 0
✅ nextjs-webpack-stable 29 0 0
✅ nitro-stable 28 0 1
✅ nuxt-stable 28 0 1
✅ sveltekit-stable 28 0 1
✅ vite-stable 28 0 1
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 28 0 1
✅ express-stable 28 0 1
✅ fastify-stable 28 0 1
✅ hono-stable 28 0 1
✅ nextjs-turbopack-stable 29 0 0
✅ nextjs-webpack-stable 29 0 0
✅ nitro-stable 28 0 1
✅ nuxt-stable 28 0 1
✅ sveltekit-stable 28 0 1
✅ vite-stable 28 0 1
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 29 0 0
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 0
❌ mongodb 28 1 0
✅ redis-dev 3 0 0
❌ redis 28 1 0
✅ starter-dev 3 0 0
❌ starter 20 9 0
✅ turso-dev 3 0 0
❌ turso 28 1 0

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: success
  • Local Dev: failure
  • Local Prod: success
  • Local Postgres: success
  • Windows: success

Check the workflow run for details.

@vercel
Copy link
Contributor

vercel bot commented Dec 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Dec 31, 2025 6:56pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Dec 31, 2025 6:56pm
example-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-astro-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-express-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-fastify-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-hono-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-nitro-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-nuxt-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workbench-vite-workflow Ready Ready Preview, Comment Dec 31, 2025 6:56pm
workflow-docs Ready Ready Preview, Comment Dec 31, 2025 6:56pm

Copy link
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@TooTallNate TooTallNate force-pushed the 12-30-allow_use_step_functions_to_be_defined_outside_of_the_app_directory branch from 094a1be to def292b Compare December 31, 2025 18:54
@github-actions
Copy link
Contributor

github-actions bot commented Dec 31, 2025

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Starter 🥇 Next.js (Turbopack) 0.038s (+2.7%) 1.013s (~) 0.975s 10 1.00x
🌐 Redis Next.js (Turbopack) 0.039s (-5.4% 🟢) 1.019s (~) 0.980s 10 1.02x
💻 Local Next.js (Turbopack) 0.039s (+4.0%) 1.014s (~) 0.975s 10 1.02x
💻 Local Nitro 0.043s (-2.0%) 1.007s (~) 0.963s 10 1.14x
💻 Local Express 0.045s (+5.3% 🔺) 1.008s (~) 0.962s 10 1.20x
🌐 MongoDB Next.js (Turbopack) 0.064s (-36.9% 🟢) 1.015s (~) 0.951s 10 1.67x
🌐 Turso Next.js (Turbopack) 0.103s (+1.4%) 1.015s (~) 0.911s 10 2.72x
🐘 Postgres Next.js (Turbopack) 0.205s (-14.9% 🟢) 1.050s (+2.9%) 0.845s 10 5.39x
🐘 Postgres Nitro 0.312s (~) 1.013s (~) 0.701s 10 8.21x
🐘 Postgres Express 0.315s (-15.8% 🟢) 1.012s (~) 0.697s 10 8.30x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.569s (-5.4% 🟢) 1.465s (-8.5% 🟢) 0.895s 10 1.00x
▲ Vercel Nitro 0.571s (-48.9% 🟢) 1.469s (-26.5% 🟢) 0.898s 10 1.00x
▲ Vercel Express 0.576s (-24.0% 🟢) 1.620s (-11.9% 🟢) 1.044s 10 1.01x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.088s (-1.3%) 2.014s (~) 0.926s 10 1.00x
🌐 Starter Next.js (Turbopack) 1.093s (~) 2.009s (~) 0.916s 10 1.00x
💻 Local Next.js (Turbopack) 1.101s (~) 2.011s (~) 0.910s 10 1.01x
💻 Local Nitro 1.113s (~) 2.007s (~) 0.894s 10 1.02x
💻 Local Express 1.116s (+0.5%) 2.006s (~) 0.891s 10 1.03x
🌐 MongoDB Next.js (Turbopack) 1.315s (+0.6%) 2.016s (~) 0.701s 10 1.21x
🌐 Turso Next.js (Turbopack) 1.315s (+1.1%) 2.013s (~) 0.698s 10 1.21x
🐘 Postgres Next.js (Turbopack) 1.726s (-6.1% 🟢) 2.017s (~) 0.291s 10 1.59x
🐘 Postgres Express 2.161s (-3.2%) 3.015s (~) 0.854s 10 1.99x
🐘 Postgres Nitro 2.177s (+2.6%) 3.014s (~) 0.837s 10 2.00x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.832s (+2.5%) 3.606s (-1.5%) 0.774s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.844s (+4.0%) 3.750s (+2.5%) 0.905s 10 1.00x
▲ Vercel Express 2.859s (-10.7% 🟢) 3.751s (-9.4% 🟢) 0.892s 10 1.01x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 10.582s (-0.8%) 11.023s (~) 0.441s 5 1.00x
🌐 Starter Next.js (Turbopack) 10.582s (~) 11.009s (~) 0.427s 5 1.00x
💻 Local Next.js (Turbopack) 10.647s (~) 11.019s (~) 0.372s 5 1.01x
💻 Local Nitro 10.784s (~) 11.009s (~) 0.225s 5 1.02x
💻 Local Express 10.795s (~) 11.015s (~) 0.220s 5 1.02x
🌐 Turso Next.js (Turbopack) 12.228s (~) 13.024s (~) 0.797s 5 1.16x
🌐 MongoDB Next.js (Turbopack) 12.294s (~) 13.029s (~) 0.735s 5 1.16x
🐘 Postgres Next.js (Turbopack) 14.382s (-3.9%) 14.836s (-5.1% 🟢) 0.454s 5 1.36x
🐘 Postgres Nitro 20.362s (~) 21.039s (~) 0.677s 5 1.92x
🐘 Postgres Express 20.503s (+1.1%) 21.028s (~) 0.526s 5 1.94x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 37.130s (+61.2% 🔺) 38.025s (+61.3% 🔺) 0.895s 5 1.00x
▲ Vercel Next.js (Turbopack) 37.505s (+63.6% 🔺) 38.242s (+60.5% 🔺) 0.737s 5 1.01x
▲ Vercel Nitro 37.617s (+61.5% 🔺) 38.566s (+62.2% 🔺) 0.949s 5 1.01x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Starter 🥇 Next.js (Turbopack) 1.336s (~) 2.008s (~) 0.672s 15 1.00x
🌐 Redis Next.js (Turbopack) 1.340s (-0.8%) 2.012s (~) 0.671s 15 1.00x
💻 Local Next.js (Turbopack) 1.385s (-0.8%) 2.012s (~) 0.627s 15 1.04x
💻 Local Nitro 1.409s (-0.8%) 2.006s (~) 0.597s 15 1.05x
💻 Local Express 1.419s (+2.2%) 2.006s (~) 0.587s 15 1.06x
🐘 Postgres Next.js (Turbopack) 1.807s (+2.4%) 2.013s (~) 0.206s 15 1.35x
🌐 MongoDB Next.js (Turbopack) 2.130s (-1.2%) 3.014s (~) 0.884s 10 1.59x
🌐 Turso Next.js (Turbopack) 2.153s (-2.6%) 3.014s (~) 0.862s 10 1.61x
🐘 Postgres Express 2.387s (+6.3% 🔺) 3.012s (+6.4% 🔺) 0.625s 10 1.79x
🐘 Postgres Nitro 2.486s (+5.5% 🔺) 3.012s (~) 0.526s 10 1.86x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.847s (-12.9% 🟢) 3.779s (-8.2% 🟢) 0.931s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.156s (-1.9%) 3.913s (-3.0%) 0.757s 8 1.11x
▲ Vercel Nitro 3.706s (+15.7% 🔺) 4.633s (+21.1% 🔺) 0.928s 8 1.30x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 2.158s (+4.3%) 3.065s (+8.1% 🔺) 0.907s 10 1.00x
💻 Local Nitro 2.238s (+2.4%) 3.166s (+1.3%) 0.928s 10 1.04x
💻 Local Express 2.255s (+4.4%) 3.218s (+3.4%) 0.963s 10 1.04x
🌐 Redis Next.js (Turbopack) 2.373s (-4.6%) 3.012s (~) 0.639s 10 1.10x
🌐 Starter Next.js (Turbopack) 2.455s (+0.8%) 3.009s (~) 0.554s 10 1.14x
🐘 Postgres Next.js (Turbopack) 2.668s (+5.5% 🔺) 3.026s (~) 0.358s 10 1.24x
🐘 Postgres Express 2.771s (+13.1% 🔺) 3.017s (~) 0.246s 10 1.28x
🐘 Postgres Nitro 2.879s (-3.5%) 3.012s (-10.2% 🟢) 0.134s 10 1.33x
🌐 Turso Next.js (Turbopack) 4.652s (~) 5.179s (~) 0.527s 6 2.16x
🌐 MongoDB Next.js (Turbopack) 4.792s (+3.7%) 5.183s (~) 0.391s 6 2.22x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.465s (-14.4% 🟢) 3.963s (-15.9% 🟢) 0.498s 9 1.00x
▲ Vercel Nitro 3.581s (~) 4.320s (-11.5% 🟢) 0.739s 7 1.03x
▲ Vercel Next.js (Turbopack) 3.659s (+3.8%) 4.335s (-1.9%) 0.676s 7 1.06x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.358s (~) 2.011s (~) 0.653s 15 1.00x
🌐 Starter Next.js (Turbopack) 1.364s (+0.9%) 2.008s (~) 0.643s 15 1.00x
💻 Local Next.js (Turbopack) 1.411s (+1.2%) 2.018s (~) 0.607s 15 1.04x
💻 Local Express 1.435s (+2.8%) 2.006s (~) 0.571s 15 1.06x
💻 Local Nitro 1.438s (+1.7%) 2.006s (~) 0.568s 15 1.06x
🐘 Postgres Next.js (Turbopack) 1.606s (-4.1%) 2.014s (~) 0.408s 15 1.18x
🐘 Postgres Express 1.904s (+7.1% 🔺) 2.010s (~) 0.106s 15 1.40x
🐘 Postgres Nitro 2.058s (+7.8% 🔺) 2.482s (+23.6% 🔺) 0.424s 13 1.52x
🌐 MongoDB Next.js (Turbopack) 2.141s (~) 3.015s (~) 0.874s 10 1.58x
🌐 Turso Next.js (Turbopack) 2.184s (-1.2%) 3.012s (~) 0.828s 10 1.61x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.958s (+5.9% 🔺) 3.713s (+3.0%) 0.755s 9 1.00x
▲ Vercel Next.js (Turbopack) 2.965s (+5.2% 🔺) 3.727s (~) 0.762s 9 1.00x
▲ Vercel Express 3.323s (+12.5% 🔺) 4.176s (+8.2% 🔺) 0.852s 8 1.12x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 2.174s (~) 3.048s (+2.2%) 0.874s 10 1.00x
💻 Local Express 2.238s (+2.5%) 3.190s (+1.3%) 0.952s 10 1.03x
💻 Local Nitro 2.310s (+4.5%) 3.238s (+2.6%) 0.929s 10 1.06x
🌐 Redis Next.js (Turbopack) 2.418s (-1.9%) 3.015s (~) 0.597s 10 1.11x
🌐 Starter Next.js (Turbopack) 2.454s (-0.7%) 3.009s (~) 0.555s 10 1.13x
🐘 Postgres Next.js (Turbopack) 2.510s (+0.9%) 3.018s (~) 0.507s 10 1.15x
🐘 Postgres Nitro 2.628s (-0.7%) 3.012s (~) 0.385s 10 1.21x
🐘 Postgres Express 2.810s (+8.4% 🔺) 3.013s (~) 0.203s 10 1.29x
🌐 MongoDB Next.js (Turbopack) 4.666s (-1.0%) 5.183s (~) 0.517s 6 2.15x
🌐 Turso Next.js (Turbopack) 4.790s (+3.4%) 5.182s (~) 0.392s 6 2.20x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.537s (-3.2%) 4.293s (-0.8%) 0.756s 7 1.00x
▲ Vercel Nitro 3.665s (+7.7% 🔺) 4.431s (+6.1% 🔺) 0.767s 7 1.04x
▲ Vercel Express 3.962s (+4.4%) 4.945s (+12.5% 🔺) 0.983s 7 1.12x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Starter 🥇 Next.js (Turbopack) 0.122s (-2.5%) 1.005s (~) 0.000s (~) 1.010s (~) 0.888s 10 1.00x
🌐 Redis Next.js (Turbopack) 0.129s (-10.0% 🟢) 1.004s (~) 0.000s (+Infinity% 🔺) 1.016s (~) 0.886s 10 1.06x
💻 Local Next.js (Turbopack) 0.145s (+4.2%) 1.003s (~) 0.018s (+1.1%) 1.029s (~) 0.884s 10 1.18x
💻 Local Nitro 0.179s (+0.8%) 0.992s (~) 0.017s (+1.8%) 1.023s (~) 0.844s 10 1.46x
💻 Local Express 0.179s (+2.8%) 0.992s (~) 0.017s (+7.0% 🔺) 1.023s (~) 0.844s 10 1.46x
🌐 Turso Next.js (Turbopack) 0.474s (-7.4% 🟢) 0.979s (+4.6%) 0.000s (NaN%) 1.013s (~) 0.540s 10 3.87x
🌐 MongoDB Next.js (Turbopack) 0.480s (-8.0% 🟢) 0.970s (+4.4%) 0.000s (+Infinity% 🔺) 1.014s (~) 0.535s 10 3.92x
🐘 Postgres Next.js (Turbopack) 0.724s (-38.8% 🟢) 0.916s (-40.4% 🟢) 0.000s (+Infinity% 🔺) 1.016s (-41.1% 🟢) 0.292s 10 5.91x
🐘 Postgres Express 2.330s (-2.8%) 2.711s (+2.5%) 0.000s (NaN%) 3.014s (~) 0.684s 10 19.02x
🐘 Postgres Nitro 2.390s (+7.6% 🔺) 2.654s (-6.0% 🟢) 0.000s (-50.0% 🟢) 3.014s (~) 0.624s 10 19.51x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.796s (-5.2% 🟢) 3.261s (+1.0%) 0.340s (-15.7% 🟢) 4.102s (-2.0%) 1.306s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.817s (-10.5% 🟢) 3.257s (-9.5% 🟢) 0.582s (+17.6% 🔺) 4.273s (-7.2% 🟢) 1.457s 10 1.01x
▲ Vercel Nitro 3.007s (+3.3%) 3.295s (~) 0.628s (+43.9% 🔺) 4.461s (+5.8% 🔺) 1.454s 10 1.08x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Next.js (Turbopack) 8/8
🐘 Postgres Next.js (Turbopack) 8/8
▲ Vercel Express 4/8
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 💻 Local 8/8
Next.js (Turbopack) 🌐 Starter 3/8
Nitro 💻 Local 8/8
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Starter: Community world (local development)
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)

📋 View full workflow run

Comment on lines +394 to +398
// Get output content if not writing to file
const outputContent =
!writeToFile && stepsResult.outputFiles?.[0]
? stepsResult.outputFiles[0].text
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing error check for esbuild output when creating steps bundle in-memory. The TypeScript overload promises a non-optional outputContent: string, but the implementation can return undefined, which will cause runtime errors in the second build pass.

View Details
📝 Patch Details
diff --git a/packages/builders/src/base-builder.ts b/packages/builders/src/base-builder.ts
index 0b37f3f..be5939a 100644
--- a/packages/builders/src/base-builder.ts
+++ b/packages/builders/src/base-builder.ts
@@ -391,6 +391,13 @@ export abstract class BaseBuilder {
     // Create .gitignore in .swc directory
     await this.createSwcGitignore();
 
+    // Ensure output files exist when not writing to disk
+    if (!writeToFile) {
+      if (!stepsResult.outputFiles || stepsResult.outputFiles.length === 0) {
+        throw new Error('No output files generated from esbuild');
+      }
+    }
+
     // Get output content if not writing to file
     const outputContent =
       !writeToFile && stepsResult.outputFiles?.[0]

Analysis

Missing error check for esbuild output in createStepsBundle

What fails: The createStepsBundle() method in base-builder.ts has TypeScript overloads that promise a non-optional outputContent: string when the outfile parameter is omitted, but the implementation can return undefined, violating the type contract. This causes the callers in standalone.ts and vercel-build-output-api.ts to use the outputContent directly in esbuild's stdin.contents without null-checking, creating a type-safety issue.

How to reproduce:

  • Call createStepsBundle() without the outfile parameter (as done in standalone.ts line 54 and vercel-build-output-api.ts line 59)
  • The TypeScript overload guarantees outputContent: string
  • The implementation uses: !writeToFile && stepsResult.outputFiles?.[0] ? stepsResult.outputFiles[0].text : undefined
  • If stepsResult.outputFiles is empty or falsy, outputContent becomes undefined, violating the contract

Result: TypeScript incorrectly allows undefined to be passed where string is required by the overload contract. While esbuild should always populate outputFiles when write: false succeeds, the lack of explicit error checking creates a defensive programming gap.

Expected: The code should validate that outputFiles exists and is non-empty before extracting content, similar to the error check in createWorkflowsBundle() at line 548-550:

if (!interimBundle.outputFiles || interimBundle.outputFiles.length === 0) {
  throw new Error('No output files generated from esbuild');
}

Fix: Added explicit error check in createStepsBundle() at line 393-397 to ensure outputFiles exists when writeToFile is false, making the type contract enforceable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants