Skip to content

Refactor e2e tests to no longer use "trigger" endpoint#958

Merged
TooTallNate merged 17 commits intomainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint
Feb 7, 2026
Merged

Refactor e2e tests to no longer use "trigger" endpoint#958
TooTallNate merged 17 commits intomainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

@TooTallNate TooTallNate commented Feb 6, 2026

Summary

Refactors the E2E tests to call start() from workflow/api directly instead of going through the /api/trigger HTTP endpoint in each workbench app. This removes a layer of indirection — the tests now use the same API that users would use to start workflows programmatically.

Before

const run = await triggerWorkflow('addTenWorkflow', [123]);
const returnValue = await getWorkflowReturnValue(run.runId);
  • triggerWorkflow() sent an HTTP POST to /api/trigger on the workbench app
  • The workbench app looked up the workflow function, called start(), and returned the run ID
  • getWorkflowReturnValue() polled GET /api/trigger?runId=... until the workflow completed

After

const run = await start(await e2e('addTenWorkflow'), [123]);
const returnValue = await run.returnValue;
  • e2e() / getWorkflowMetadata() fetches the manifest from /.well-known/workflow/v1/manifest.json to look up the correct workflowId
  • start() is called directly from the test process via the configured World
  • run.returnValue polls for completion via the World (no HTTP polling endpoint needed)

Changes

packages/core/e2e/e2e.test.ts

  • Removed triggerWorkflow() and getWorkflowReturnValue() helpers
  • Added fetchManifest() to fetch and cache the workflow manifest from the deployment
  • Added getWorkflowMetadata(file, fn) to look up { workflowId } from the manifest
  • Added e2e(fn) shorthand for the common case of workflows/99_e2e.ts
  • All tests call start() and run.returnValue directly
  • Error tests use .catch() to inspect WorkflowRunFailedError
  • Output stream tests use run.getReadable() directly (skipped on local world where cross-process streaming isn't supported)
  • beforeAll configures the local World with the correct data directory and base URL
  • Pages Router tests use startWorkflowViaHttp() to specifically validate the HTTP trigger path

Workbench apps (hono, express, fastify, nest)

  • Removed /api/trigger route handlers
  • Kept /api/hook, /api/test-direct-step-call, /api/test-health-check endpoints
  • Re-added _workflows.js side-effect import for hono/express/fastify to maintain Nitro's HMR dependency graph

Deleted trigger-only route files from: nextjs-turbopack, nextjs-webpack, vite, sveltekit, astro, nuxt, nitro-v2, nitro-v3, example

.github/workflows/tests.yml

  • Added WORKFLOW_PUBLIC_MANIFEST: '1' to all E2E test jobs

Dependencies

Stacked on #963 which adds WORKFLOW_PUBLIC_MANIFEST support to all framework builders.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 6, 2026

🦋 Changeset detected

Latest commit: 92dcd4e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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
Copy Markdown
Contributor

github-actions Bot commented Feb 6, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 479 0 38 517
✅ 💻 Local Development 408 0 62 470
✅ 📦 Local Production 408 0 62 470
✅ 🐘 Local Postgres 408 0 62 470
✅ 🪟 Windows 44 0 3 47
❌ 🌍 Community Worlds 31 157 12 200
✅ 📋 Other 120 0 21 141
Total 1898 157 260 2315

❌ Failed Tests

🌍 Community Worlds (157 failed)

mongodb (39 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • 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
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (39 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • 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
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • 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 (CLI) - workflow health command reports healthy endpoints
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (39 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • 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
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 43 0 4
✅ example 43 0 4
✅ express 43 0 4
✅ fastify 43 0 4
✅ hono 43 0 4
✅ nextjs-turbopack 46 0 1
✅ nextjs-webpack 46 0 1
✅ nitro 43 0 4
✅ nuxt 43 0 4
✅ sveltekit 43 0 4
✅ vite 43 0 4
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 40 0 7
✅ express-stable 40 0 7
✅ fastify-stable 40 0 7
✅ hono-stable 40 0 7
✅ nextjs-turbopack-stable 44 0 3
✅ nextjs-webpack-stable 44 0 3
✅ nitro-stable 40 0 7
✅ nuxt-stable 40 0 7
✅ sveltekit-stable 40 0 7
✅ vite-stable 40 0 7
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 40 0 7
✅ express-stable 40 0 7
✅ fastify-stable 40 0 7
✅ hono-stable 40 0 7
✅ nextjs-turbopack-stable 44 0 3
✅ nextjs-webpack-stable 44 0 3
✅ nitro-stable 40 0 7
✅ nuxt-stable 40 0 7
✅ sveltekit-stable 40 0 7
✅ vite-stable 40 0 7
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 40 0 7
✅ express-stable 40 0 7
✅ fastify-stable 40 0 7
✅ hono-stable 40 0 7
✅ nextjs-turbopack-stable 44 0 3
✅ nextjs-webpack-stable 44 0 3
✅ nitro-stable 40 0 7
✅ nuxt-stable 40 0 7
✅ sveltekit-stable 40 0 7
✅ vite-stable 40 0 7
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 44 0 3
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 0
❌ mongodb 5 39 3
✅ redis-dev 3 0 0
❌ redis 5 39 3
✅ starter-dev 3 0 0
❌ starter 4 40 3
✅ turso-dev 3 0 0
❌ turso 5 39 3
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 40 0 7
✅ e2e-local-postgres-nest-stable 40 0 7
✅ e2e-local-prod-nest-stable 40 0 7

📋 View full workflow run

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 6, 2026

📊 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
💻 Local 🥇 Express 0.032s (-25.3% 🟢) 1.006s (~) 0.974s 10 1.00x
💻 Local Nitro 0.032s (-25.7% 🟢) 1.006s (~) 0.974s 10 1.00x
💻 Local Next.js (Turbopack) 0.045s (+16.9% 🔺) 1.007s (-0.9%) 0.962s 10 1.39x
🐘 Postgres Express 0.299s (+61.8% 🔺) 1.011s (-0.7%) 0.712s 10 9.29x
🐘 Postgres Nitro 0.327s (+51.2% 🔺) 1.012s (~) 0.685s 10 10.15x
🐘 Postgres Next.js (Turbopack) 0.371s (-12.5% 🟢) 1.011s (-0.9%) 0.641s 10 11.52x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.803s (+22.4% 🔺) 2.229s (+38.7% 🔺) 1.427s 10 1.00x
▲ Vercel Next.js (Turbopack) 0.815s (-1.5%) 2.262s (+28.0% 🔺) 1.447s 10 1.02x
▲ Vercel Express 0.899s (+20.8% 🔺) 2.166s (+34.4% 🔺) 1.266s 10 1.12x

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

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 1.101s (~) 2.005s (~) 0.903s 10 1.00x
💻 Local Nitro 1.104s (-1.1%) 2.005s (~) 0.901s 10 1.00x
💻 Local Express 1.104s (-1.1%) 2.005s (~) 0.900s 10 1.00x
🐘 Postgres Next.js (Turbopack) 1.869s (-17.3% 🟢) 2.213s (-26.7% 🟢) 0.343s 10 1.70x
🐘 Postgres Nitro 2.185s (-1.5%) 3.014s (~) 0.829s 10 1.98x
🐘 Postgres Express 2.413s (~) 3.013s (~) 0.600s 10 2.19x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.987s (+98.7% 🔺) 5.978s (+71.6% 🔺) 0.991s 10 1.00x
▲ Vercel Express 5.222s (+107.4% 🔺) 6.000s (+60.4% 🔺) 0.778s 10 1.05x
▲ Vercel Nitro 5.770s (+126.8% 🔺) 6.813s (+82.7% 🔺) 1.042s 10 1.16x

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

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 10.686s (~) 11.021s (~) 0.335s 3 1.00x
💻 Local Express 10.808s (~) 11.020s (~) 0.212s 3 1.01x
💻 Local Nitro 10.814s (~) 11.021s (~) 0.208s 3 1.01x
🐘 Postgres Next.js (Turbopack) 15.140s (-24.8% 🟢) 16.046s (-23.7% 🟢) 0.906s 2 1.42x
🐘 Postgres Nitro 20.277s (+30.9% 🔺) 21.056s (+31.4% 🔺) 0.778s 2 1.90x
🐘 Postgres Express 20.297s (~) 21.055s (~) 0.757s 2 1.90x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 20.816s (+3.5%) 22.196s (+6.4% 🔺) 1.380s 2 1.00x
▲ Vercel Next.js (Turbopack) 20.855s (+2.0%) 22.061s (+3.5%) 1.205s 2 1.00x
▲ Vercel Express 21.338s (+6.7% 🔺) 22.519s (+7.9% 🔺) 1.180s 2 1.03x

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

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 27.217s (~) 28.041s (~) 0.824s 3 1.00x
💻 Local Express 27.421s (~) 28.042s (~) 0.621s 3 1.01x
💻 Local Nitro 27.457s (~) 28.039s (~) 0.582s 3 1.01x
🐘 Postgres Next.js (Turbopack) 37.513s (-25.4% 🟢) 38.087s (-25.4% 🟢) 0.574s 2 1.38x
🐘 Postgres Nitro 50.319s (+30.7% 🔺) 51.136s (+30.9% 🔺) 0.817s 2 1.85x
🐘 Postgres Express 50.369s (~) 51.118s (~) 0.749s 2 1.85x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 56.790s (+13.9% 🔺) 58.615s (+15.4% 🔺) 1.825s 2 1.00x
▲ Vercel Express 61.400s (+21.9% 🔺) 62.091s (+21.5% 🔺) 0.691s 1 1.08x
▲ Vercel Next.js (Turbopack) 72.898s (+44.4% 🔺) 74.329s (+44.7% 🔺) 1.431s 1 1.28x

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

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 56.533s (~) 57.095s (~) 0.562s 2 1.00x
💻 Local Express 57.008s (~) 57.095s (-1.6%) 0.086s 2 1.01x
💻 Local Nitro 57.060s (~) 57.593s (-0.8%) 0.532s 2 1.01x
🐘 Postgres Next.js (Turbopack) 75.359s (-25.0% 🟢) 76.175s (-24.7% 🟢) 0.816s 2 1.33x
🐘 Postgres Express 100.315s (~) 101.223s (~) 0.908s 1 1.77x
🐘 Postgres Nitro 100.466s (+31.6% 🔺) 101.253s (+31.3% 🔺) 0.787s 1 1.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.395s (~) 2.004s (~) 0.609s 15 1.00x
💻 Local Express 1.404s (~) 2.005s (~) 0.602s 15 1.01x
💻 Local Next.js (Turbopack) 1.409s (+1.7%) 2.004s (~) 0.596s 15 1.01x
🐘 Postgres Next.js (Turbopack) 2.007s (-6.2% 🟢) 2.741s (-3.3%) 0.734s 11 1.44x
🐘 Postgres Express 2.405s (+6.0% 🔺) 3.013s (~) 0.608s 10 1.72x
🐘 Postgres Nitro 2.501s (+10.9% 🔺) 3.014s (+9.6% 🔺) 0.513s 10 1.79x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.203s (+16.5% 🔺) 4.348s (+15.9% 🔺) 1.145s 7 1.00x
▲ Vercel Express 3.344s (+16.6% 🔺) 4.431s (+16.6% 🔺) 1.086s 7 1.04x
▲ Vercel Nitro 5.916s (+126.5% 🔺) 7.370s (+101.0% 🔺) 1.454s 5 1.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 2.530s (-2.5%) 3.006s (~) 0.476s 10 1.00x
💻 Local Express 2.533s (-2.5%) 3.007s (~) 0.474s 10 1.00x
💻 Local Next.js (Turbopack) 2.538s (+2.8%) 3.008s (~) 0.470s 10 1.00x
🐘 Postgres Express 8.824s (-3.0%) 9.030s (-5.6% 🟢) 0.206s 4 3.49x
🐘 Postgres Nitro 9.200s (-16.8% 🟢) 9.779s (-16.3% 🟢) 0.579s 4 3.64x
🐘 Postgres Next.js (Turbopack) 12.678s (~) 13.038s (-2.8%) 0.360s 3 5.01x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 4.719s (+22.3% 🔺) 5.900s (+26.8% 🔺) 1.180s 6 1.00x
▲ Vercel Next.js (Turbopack) 8.161s (+131.4% 🔺) 9.678s (+124.4% 🔺) 1.517s 4 1.73x
▲ Vercel Nitro 10.078s (+218.1% 🔺) 11.430s (+187.6% 🔺) 1.351s 3 2.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 7.053s (-3.2%) 7.518s (-9.2% 🟢) 0.465s 4 1.00x
💻 Local Next.js (Turbopack) 7.228s (+1.3%) 7.764s (~) 0.536s 4 1.02x
💻 Local Nitro 7.288s (+1.4%) 8.020s (-1.6%) 0.732s 4 1.03x
🐘 Postgres Express 45.032s (-7.8% 🟢) 45.093s (-8.4% 🟢) 0.061s 1 6.39x
🐘 Postgres Nitro 50.581s (-3.1%) 51.110s (-3.8%) 0.529s 1 7.17x
🐘 Postgres Next.js (Turbopack) 56.411s (~) 57.128s (~) 0.717s 1 8.00x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 6.750s (-54.1% 🟢) 8.133s (-48.5% 🟢) 1.382s 4 1.00x
▲ Vercel Next.js (Turbopack) 9.152s (-15.5% 🟢) 10.367s (-10.4% 🟢) 1.215s 3 1.36x
▲ Vercel Express 9.623s (-15.0% 🟢) 10.604s (-14.2% 🟢) 0.981s 3 1.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 1.417s (+1.6%) 2.005s (~) 0.588s 15 1.00x
💻 Local Nitro 1.426s (+0.6%) 2.004s (~) 0.578s 15 1.01x
💻 Local Express 1.433s (~) 2.004s (~) 0.571s 15 1.01x
🐘 Postgres Nitro 1.980s (-11.4% 🟢) 2.595s (~) 0.615s 12 1.40x
🐘 Postgres Express 2.160s (+5.3% 🔺) 3.014s (+29.8% 🔺) 0.854s 10 1.52x
🐘 Postgres Next.js (Turbopack) 2.208s (-5.0%) 2.739s (~) 0.531s 11 1.56x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.259s (+68.1% 🔺) 5.477s (+53.9% 🔺) 1.218s 6 1.00x
▲ Vercel Next.js (Turbopack) 9.202s (+260.5% 🔺) 10.251s (+181.8% 🔺) 1.049s 3 2.16x
▲ Vercel Express 10.013s (+219.8% 🔺) 11.122s (+163.1% 🔺) 1.109s 3 2.35x

🔍 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 🥇 Nitro 2.643s (-1.6%) 3.006s (~) 0.363s 10 1.00x
💻 Local Next.js (Turbopack) 2.683s (+1.9%) 3.008s (-5.4% 🟢) 0.324s 10 1.02x
💻 Local Express 2.693s (~) 3.005s (-0.7%) 0.312s 10 1.02x
🐘 Postgres Express 10.900s (-1.1%) 11.363s (~) 0.463s 3 4.12x
🐘 Postgres Nitro 12.081s (+2.4%) 13.040s (+8.5% 🔺) 0.959s 3 4.57x
🐘 Postgres Next.js (Turbopack) 12.884s (-5.9% 🟢) 13.367s (-7.0% 🟢) 0.483s 3 4.87x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.072s (-8.6% 🟢) 4.302s (-1.3%) 1.230s 7 1.00x
▲ Vercel Next.js (Turbopack) 3.925s (+23.8% 🔺) 5.382s (+35.6% 🔺) 1.457s 6 1.28x
▲ Vercel Express 4.656s (+67.6% 🔺) 6.018s (+61.7% 🔺) 1.362s 5 1.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 7.511s (-8.0% 🟢) 8.016s (-11.8% 🟢) 0.505s 4 1.00x
💻 Local Next.js (Turbopack) 7.648s (+1.5%) 8.263s (-3.5%) 0.615s 4 1.02x
💻 Local Nitro 7.658s (-3.3%) 8.019s (-10.1% 🟢) 0.360s 4 1.02x
🐘 Postgres Express 47.367s (-6.6% 🟢) 48.099s (-5.9% 🟢) 0.732s 1 6.31x
🐘 Postgres Nitro 52.370s (+0.6%) 53.111s (~) 0.741s 1 6.97x
🐘 Postgres Next.js (Turbopack) 56.734s (-1.3%) 57.110s (-1.8%) 0.376s 1 7.55x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.302s (-57.8% 🟢) 4.789s (-44.0% 🟢) 1.487s 7 1.00x
▲ Vercel Nitro 3.835s (-38.1% 🟢) 5.066s (-25.7% 🟢) 1.230s 6 1.16x
▲ Vercel Next.js (Turbopack) 3.884s (-52.8% 🟢) 5.270s (-41.2% 🟢) 1.386s 6 1.18x

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

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 0.142s (~) 1.000s (~) 0.011s (-28.6% 🟢) 1.015s (-1.0%) 0.873s 10 1.00x
💻 Local Express 0.167s (-7.7% 🟢) 1.002s (+1.0%) 0.010s (-28.4% 🟢) 1.015s (-0.6%) 0.848s 10 1.17x
💻 Local Nitro 0.170s (-5.6% 🟢) 1.002s (+1.0%) 0.011s (-21.6% 🟢) 1.015s (~) 0.845s 10 1.19x
🐘 Postgres Next.js (Turbopack) 1.204s (-38.5% 🟢) 1.839s (-26.4% 🟢) 0.001s (+1000.0% 🔺) 2.013s (-25.9% 🟢) 0.809s 10 8.47x
🐘 Postgres Nitro 2.298s (+82.1% 🔺) 2.745s (+54.3% 🔺) 0.001s (+Infinity% 🔺) 3.016s (+49.8% 🔺) 0.718s 10 16.17x
🐘 Postgres Express 2.451s (+4.5%) 2.590s (-3.9%) 0.001s (+600.0% 🔺) 3.014s (~) 0.562s 10 17.25x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.815s (-47.1% 🟢) 3.135s (-28.4% 🟢) 0.139s (-24.7% 🟢) 3.993s (-36.3% 🟢) 1.178s 10 1.00x
▲ Vercel Express 2.834s (-54.6% 🟢) 2.963s (-49.9% 🟢) 0.138s (-13.1% 🟢) 3.882s (-47.2% 🟢) 1.048s 10 1.01x
▲ Vercel Nitro 3.078s (-46.6% 🟢) 3.623s (-40.8% 🟢) 0.101s (-57.8% 🟢) 4.412s (-38.2% 🟢) 1.334s 10 1.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Next.js (Turbopack) 6/12
🐘 Postgres Next.js (Turbopack) 6/12
▲ Vercel Nitro 6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 💻 Local 11/12
Next.js (Turbopack) 💻 Local 11/12
Nitro 💻 Local 10/12
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

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 6, 2026

Copy link
Copy Markdown
Member Author

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the core end-to-end tests to stop relying on the workbench /api/trigger endpoint by starting runs directly via the runtime using workflow IDs from the published manifest, and removes the now-unused trigger endpoints across the various workbench framework implementations.

Changes:

  • Remove /api/trigger handlers from multiple workbench apps (Vite, SvelteKit, Nuxt/Nitro v2/v3, Next.js app router, Astro, “example”, plus framework servers like Express/Fastify/Hono/Nest).
  • Update packages/core/e2e/e2e.test.ts to fetch /.well-known/workflow/v1/manifest.json and use start({ workflowId }) + run.returnValue/run.getReadable() instead of HTTP polling.
  • Ensure CI/build environment exposes the manifest (WORKFLOW_PUBLIC_MANIFEST=1) and update the dev test matrix to reference /api/chat instead of /api/trigger.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
workbench/vite/routes/api/trigger.post.ts Removes Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.ts Removes Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.ts Removes SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.ts Removes Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.ts Removes Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.ts Removes Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.ts Removes Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.ts Removes Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.ts Removes Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.ts Removes Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.ts Removes Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.ts Removes Nest trigger routes and related imports.
workbench/hono/src/index.ts Removes Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.ts Removes Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.ts Removes Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.ts Removes example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.ts Removes Astro trigger endpoint implementation.
turbo.json Adds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjs Updates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.ts Switches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.yml Sets WORKFLOW_PUBLIC_MANIFEST=1 for relevant CI jobs so the manifest is accessible.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/e2e/e2e.test.ts
Comment thread packages/core/e2e/e2e.test.ts
Copy link
Copy Markdown
Member Author

TooTallNate commented Feb 6, 2026

Merge activity

  • Feb 6, 8:22 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 6, 8:24 PM UTC: Graphite rebased this pull request as part of a merge.
  • Feb 6, 8:25 PM UTC: Graphite couldn't merge this PR because it was not satisfying all requirements (Failed CI: 'DCO').

…ng IDs manually

Fetch the manifest from GET /manifest.json on the deployment to discover
the correct workflow IDs. This handles symlink resolution differences
between frameworks since the manifest contains the exact IDs produced
by the SWC transform during the actual build.
- Update manifest fetch URL to /.well-known/workflow/v1/manifest.json
- Remove publicAssets config from hono/express/fastify nitro configs
  (no longer needed since Nitro virtual handler serves the manifest)
- Add WORKFLOW_PUBLIC_MANIFEST=1 to all CI e2e test jobs
- Note: Vercel prod tests also need this env var set in each Vercel
  project's environment settings for the deployed builds
The data dir discovery approach failed for Nitro-based apps because the
data directory doesn't exist until the app processes its first queue
message. By that time, the test runner had already created its own
.workflow-data at the monorepo root, causing the test runner and app
to use different data directories.

Fix: explicitly compute the data dir path based on the app name and
framework convention (.next/workflow-data for Next.js, .workflow-data
for everything else).
… HMR

The Nitro dev server uses the dependency graph to determine which file
changes trigger a dev:reload. Removing the _workflows.js import from
src/index.ts broke HMR rebuilds for workflow file changes because Nitro
no longer knew that src/index.ts depended on the workflow files.

Re-add the import as a side-effect import (no named exports) to maintain
the dependency chain for Nitro's file watcher.
…run.returnValue directly

- Replace startWorkflow() with direct start(await e2e('fn'), args) calls
- Replace run.returnValue access via getWorkflowReturnValue() with direct run.returnValue
- Add getWorkflowMetadata() helper that returns { workflowId } for start()
- Add e2e() shorthand for workflows/99_e2e.ts lookups
- Pages Router tests use startWorkflowViaHttp() explicitly
- Remove debug logging from all builder packages
…trigger

The benchmarks were using the deleted /api/trigger endpoint, causing
'No benchmark data found' in CI. Apply the same refactoring as the
e2e tests: use start() directly with manifest-based workflow ID lookup.

Also adds WORKFLOW_PUBLIC_MANIFEST=1 to all benchmark CI jobs.
…hmarks

The Vercel benchmark job was missing VERCEL_DEPLOYMENT_ID, causing the
World to default to local instead of Vercel. Also include the generated
packages/core/src/version.ts in build artifacts.

Added fail-fast check for missing VERCEL_DEPLOYMENT_ID when
WORKFLOW_VERCEL_ENV is set, and a 2-minute timeout wrapper around
run.returnValue to prevent benchmarks from hanging indefinitely if
the workflow never completes.
Add 30s AbortSignal.timeout to fetchManifest() to prevent hanging on
unresponsive Vercel deployments. Log response URL to detect redirects.
…Sync

The Nitro virtual handler used readFileSync with an absolute path baked
in at build time. In prod mode on a different machine (CI), this path
doesn't exist, causing 404s.

Fix: in dev mode, keep the virtual handler (same machine, path is valid).
In prod mode, copy the manifest to Nitro's public output directory
(.output/public/) during the 'compiled' hook, so Nitro serves it as
a static asset.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 6, 2026

Deployment failed with the following error:

Creating the Deployment Timed Out.

…tual module

Virtual modules in Nitro are resolved once during setup and can't be
updated later. For prod builds, write a physical manifest-handler.mjs
file with inlined manifest content during build:before (after the
builder generates the manifest). Nitro's rollup then bundles this file
into the compiled output.

Dev mode continues to use a virtual handler with readFileSync since the
absolute path is valid on the build machine.
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.

3 participants