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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

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

Merged
TooTallNate merged 17 commits into
mainfrom
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 into
mainfrom
02-05-refactor_e2e_tests_to_no_longer_use_trigger_endpoint

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
Member

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

construn=awaittriggerWorkflow('addTenWorkflow',[123]);constreturnValue=awaitgetWorkflowReturnValue(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

construn=awaitstart(awaite2e('addTenWorkflow'),[123]);constreturnValue=awaitrun.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

changeset-botBot commented Feb 6, 2026

Copy link
Copy Markdown

🦋 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

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production479038517
✅ 💻 Local Development408062470
✅ 📦 Local Production408062470
✅ 🐘 Local Postgres408062470
✅ 🪟 Windows440347
❌ 🌍 Community Worlds3115712200
✅ 📋 Other120021141
Total18981572602315

❌ 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
AppPassedFailedSkipped
✅ astro4304
✅ example4304
✅ express4304
✅ fastify4304
✅ hono4304
✅ nextjs-turbopack4601
✅ nextjs-webpack4601
✅ nitro4304
✅ nuxt4304
✅ sveltekit4304
✅ vite4304
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4007
✅ express-stable4007
✅ fastify-stable4007
✅ hono-stable4007
✅ nextjs-turbopack-stable4403
✅ nextjs-webpack-stable4403
✅ nitro-stable4007
✅ nuxt-stable4007
✅ sveltekit-stable4007
✅ vite-stable4007
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4403
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5393
✅ redis-dev300
❌ redis5393
✅ starter-dev300
❌ starter4403
✅ turso-dev300
❌ turso5393
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4007
✅ e2e-local-postgres-nest-stable4007
✅ e2e-local-prod-nest-stable4007

📋 View full workflow run

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.032s (-25.3% 🟢)1.006s (~)0.974s101.00x
💻 LocalNitro0.032s (-25.7% 🟢)1.006s (~)0.974s101.00x
💻 LocalNext.js (Turbopack)0.045s (+16.9% 🔺)1.007s (-0.9%)0.962s101.39x
🐘 PostgresExpress0.299s (+61.8% 🔺)1.011s (-0.7%)0.712s109.29x
🐘 PostgresNitro0.327s (+51.2% 🔺)1.012s (~)0.685s1010.15x
🐘 PostgresNext.js (Turbopack)0.371s (-12.5% 🟢)1.011s (-0.9%)0.641s1011.52x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.803s (+22.4% 🔺)2.229s (+38.7% 🔺)1.427s101.00x
▲ VercelNext.js (Turbopack)0.815s (-1.5%)2.262s (+28.0% 🔺)1.447s101.02x
▲ VercelExpress0.899s (+20.8% 🔺)2.166s (+34.4% 🔺)1.266s101.12x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.101s (~)2.005s (~)0.903s101.00x
💻 LocalNitro1.104s (-1.1%)2.005s (~)0.901s101.00x
💻 LocalExpress1.104s (-1.1%)2.005s (~)0.900s101.00x
🐘 PostgresNext.js (Turbopack)1.869s (-17.3% 🟢)2.213s (-26.7% 🟢)0.343s101.70x
🐘 PostgresNitro2.185s (-1.5%)3.014s (~)0.829s101.98x
🐘 PostgresExpress2.413s (~)3.013s (~)0.600s102.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)4.987s (+98.7% 🔺)5.978s (+71.6% 🔺)0.991s101.00x
▲ VercelExpress5.222s (+107.4% 🔺)6.000s (+60.4% 🔺)0.778s101.05x
▲ VercelNitro5.770s (+126.8% 🔺)6.813s (+82.7% 🔺)1.042s101.16x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.686s (~)11.021s (~)0.335s31.00x
💻 LocalExpress10.808s (~)11.020s (~)0.212s31.01x
💻 LocalNitro10.814s (~)11.021s (~)0.208s31.01x
🐘 PostgresNext.js (Turbopack)15.140s (-24.8% 🟢)16.046s (-23.7% 🟢)0.906s21.42x
🐘 PostgresNitro20.277s (+30.9% 🔺)21.056s (+31.4% 🔺)0.778s21.90x
🐘 PostgresExpress20.297s (~)21.055s (~)0.757s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro20.816s (+3.5%)22.196s (+6.4% 🔺)1.380s21.00x
▲ VercelNext.js (Turbopack)20.855s (+2.0%)22.061s (+3.5%)1.205s21.00x
▲ VercelExpress21.338s (+6.7% 🔺)22.519s (+7.9% 🔺)1.180s21.03x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.217s (~)28.041s (~)0.824s31.00x
💻 LocalExpress27.421s (~)28.042s (~)0.621s31.01x
💻 LocalNitro27.457s (~)28.039s (~)0.582s31.01x
🐘 PostgresNext.js (Turbopack)37.513s (-25.4% 🟢)38.087s (-25.4% 🟢)0.574s21.38x
🐘 PostgresNitro50.319s (+30.7% 🔺)51.136s (+30.9% 🔺)0.817s21.85x
🐘 PostgresExpress50.369s (~)51.118s (~)0.749s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro56.790s (+13.9% 🔺)58.615s (+15.4% 🔺)1.825s21.00x
▲ VercelExpress61.400s (+21.9% 🔺)62.091s (+21.5% 🔺)0.691s11.08x
▲ VercelNext.js (Turbopack)72.898s (+44.4% 🔺)74.329s (+44.7% 🔺)1.431s11.28x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)56.533s (~)57.095s (~)0.562s21.00x
💻 LocalExpress57.008s (~)57.095s (-1.6%)0.086s21.01x
💻 LocalNitro57.060s (~)57.593s (-0.8%)0.532s21.01x
🐘 PostgresNext.js (Turbopack)75.359s (-25.0% 🟢)76.175s (-24.7% 🟢)0.816s21.33x
🐘 PostgresExpress100.315s (~)101.223s (~)0.908s11.77x
🐘 PostgresNitro100.466s (+31.6% 🔺)101.253s (+31.3% 🔺)0.787s11.78x

▲ Production (Vercel)

No data available

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.395s (~)2.004s (~)0.609s151.00x
💻 LocalExpress1.404s (~)2.005s (~)0.602s151.01x
💻 LocalNext.js (Turbopack)1.409s (+1.7%)2.004s (~)0.596s151.01x
🐘 PostgresNext.js (Turbopack)2.007s (-6.2% 🟢)2.741s (-3.3%)0.734s111.44x
🐘 PostgresExpress2.405s (+6.0% 🔺)3.013s (~)0.608s101.72x
🐘 PostgresNitro2.501s (+10.9% 🔺)3.014s (+9.6% 🔺)0.513s101.79x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.203s (+16.5% 🔺)4.348s (+15.9% 🔺)1.145s71.00x
▲ VercelExpress3.344s (+16.6% 🔺)4.431s (+16.6% 🔺)1.086s71.04x
▲ VercelNitro5.916s (+126.5% 🔺)7.370s (+101.0% 🔺)1.454s51.85x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.530s (-2.5%)3.006s (~)0.476s101.00x
💻 LocalExpress2.533s (-2.5%)3.007s (~)0.474s101.00x
💻 LocalNext.js (Turbopack)2.538s (+2.8%)3.008s (~)0.470s101.00x
🐘 PostgresExpress8.824s (-3.0%)9.030s (-5.6% 🟢)0.206s43.49x
🐘 PostgresNitro9.200s (-16.8% 🟢)9.779s (-16.3% 🟢)0.579s43.64x
🐘 PostgresNext.js (Turbopack)12.678s (~)13.038s (-2.8%)0.360s35.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.719s (+22.3% 🔺)5.900s (+26.8% 🔺)1.180s61.00x
▲ VercelNext.js (Turbopack)8.161s (+131.4% 🔺)9.678s (+124.4% 🔺)1.517s41.73x
▲ VercelNitro10.078s (+218.1% 🔺)11.430s (+187.6% 🔺)1.351s32.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.053s (-3.2%)7.518s (-9.2% 🟢)0.465s41.00x
💻 LocalNext.js (Turbopack)7.228s (+1.3%)7.764s (~)0.536s41.02x
💻 LocalNitro7.288s (+1.4%)8.020s (-1.6%)0.732s41.03x
🐘 PostgresExpress45.032s (-7.8% 🟢)45.093s (-8.4% 🟢)0.061s16.39x
🐘 PostgresNitro50.581s (-3.1%)51.110s (-3.8%)0.529s17.17x
🐘 PostgresNext.js (Turbopack)56.411s (~)57.128s (~)0.717s18.00x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro6.750s (-54.1% 🟢)8.133s (-48.5% 🟢)1.382s41.00x
▲ VercelNext.js (Turbopack)9.152s (-15.5% 🟢)10.367s (-10.4% 🟢)1.215s31.36x
▲ VercelExpress9.623s (-15.0% 🟢)10.604s (-14.2% 🟢)0.981s31.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.417s (+1.6%)2.005s (~)0.588s151.00x
💻 LocalNitro1.426s (+0.6%)2.004s (~)0.578s151.01x
💻 LocalExpress1.433s (~)2.004s (~)0.571s151.01x
🐘 PostgresNitro1.980s (-11.4% 🟢)2.595s (~)0.615s121.40x
🐘 PostgresExpress2.160s (+5.3% 🔺)3.014s (+29.8% 🔺)0.854s101.52x
🐘 PostgresNext.js (Turbopack)2.208s (-5.0%)2.739s (~)0.531s111.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.259s (+68.1% 🔺)5.477s (+53.9% 🔺)1.218s61.00x
▲ VercelNext.js (Turbopack)9.202s (+260.5% 🔺)10.251s (+181.8% 🔺)1.049s32.16x
▲ VercelExpress10.013s (+219.8% 🔺)11.122s (+163.1% 🔺)1.109s32.35x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro2.643s (-1.6%)3.006s (~)0.363s101.00x
💻 LocalNext.js (Turbopack)2.683s (+1.9%)3.008s (-5.4% 🟢)0.324s101.02x
💻 LocalExpress2.693s (~)3.005s (-0.7%)0.312s101.02x
🐘 PostgresExpress10.900s (-1.1%)11.363s (~)0.463s34.12x
🐘 PostgresNitro12.081s (+2.4%)13.040s (+8.5% 🔺)0.959s34.57x
🐘 PostgresNext.js (Turbopack)12.884s (-5.9% 🟢)13.367s (-7.0% 🟢)0.483s34.87x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.072s (-8.6% 🟢)4.302s (-1.3%)1.230s71.00x
▲ VercelNext.js (Turbopack)3.925s (+23.8% 🔺)5.382s (+35.6% 🔺)1.457s61.28x
▲ VercelExpress4.656s (+67.6% 🔺)6.018s (+61.7% 🔺)1.362s51.52x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express7.511s (-8.0% 🟢)8.016s (-11.8% 🟢)0.505s41.00x
💻 LocalNext.js (Turbopack)7.648s (+1.5%)8.263s (-3.5%)0.615s41.02x
💻 LocalNitro7.658s (-3.3%)8.019s (-10.1% 🟢)0.360s41.02x
🐘 PostgresExpress47.367s (-6.6% 🟢)48.099s (-5.9% 🟢)0.732s16.31x
🐘 PostgresNitro52.370s (+0.6%)53.111s (~)0.741s16.97x
🐘 PostgresNext.js (Turbopack)56.734s (-1.3%)57.110s (-1.8%)0.376s17.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.302s (-57.8% 🟢)4.789s (-44.0% 🟢)1.487s71.00x
▲ VercelNitro3.835s (-38.1% 🟢)5.066s (-25.7% 🟢)1.230s61.16x
▲ VercelNext.js (Turbopack)3.884s (-52.8% 🟢)5.270s (-41.2% 🟢)1.386s61.18x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (~)1.000s (~)0.011s (-28.6% 🟢)1.015s (-1.0%)0.873s101.00x
💻 LocalExpress0.167s (-7.7% 🟢)1.002s (+1.0%)0.010s (-28.4% 🟢)1.015s (-0.6%)0.848s101.17x
💻 LocalNitro0.170s (-5.6% 🟢)1.002s (+1.0%)0.011s (-21.6% 🟢)1.015s (~)0.845s101.19x
🐘 PostgresNext.js (Turbopack)1.204s (-38.5% 🟢)1.839s (-26.4% 🟢)0.001s (+1000.0% 🔺)2.013s (-25.9% 🟢)0.809s108.47x
🐘 PostgresNitro2.298s (+82.1% 🔺)2.745s (+54.3% 🔺)0.001s (+Infinity% 🔺)3.016s (+49.8% 🔺)0.718s1016.17x
🐘 PostgresExpress2.451s (+4.5%)2.590s (-3.9%)0.001s (+600.0% 🔺)3.014s (~)0.562s1017.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.815s (-47.1% 🟢)3.135s (-28.4% 🟢)0.139s (-24.7% 🟢)3.993s (-36.3% 🟢)1.178s101.00x
▲ VercelExpress2.834s (-54.6% 🟢)2.963s (-49.9% 🟢)0.138s (-13.1% 🟢)3.882s (-47.2% 🟢)1.048s101.01x
▲ VercelNitro3.078s (-46.6% 🟢)3.623s (-40.8% 🟢)0.101s (-57.8% 🟢)4.412s (-38.2% 🟢)1.334s101.09x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/12
🐘 PostgresNext.js (Turbopack)6/12
▲ VercelNitro6/11
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local11/12
Next.js (Turbopack)💻 Local11/12
Nitro💻 Local10/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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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
FileDescription
workbench/vite/routes/api/trigger.post.tsRemoves Vite trigger POST endpoint implementation.
workbench/vite/routes/api/trigger.get.tsRemoves Vite trigger GET endpoint implementation.
workbench/sveltekit/src/routes/api/trigger/+server.tsRemoves SvelteKit trigger endpoint implementation.
workbench/nuxt/server/api/trigger.post.tsRemoves Nuxt trigger POST endpoint implementation.
workbench/nuxt/server/api/trigger.get.tsRemoves Nuxt trigger GET endpoint implementation.
workbench/nitro-v3/routes/api/trigger.post.tsRemoves Nitro v3 trigger POST endpoint implementation.
workbench/nitro-v3/routes/api/trigger.get.tsRemoves Nitro v3 trigger GET endpoint implementation.
workbench/nitro-v2/server/api/trigger.post.tsRemoves Nitro v2 trigger POST endpoint implementation.
workbench/nitro-v2/server/api/trigger.get.tsRemoves Nitro v2 trigger GET endpoint implementation.
workbench/nextjs-webpack/app/api/trigger/route.tsRemoves Next.js (webpack) app-router trigger endpoint.
workbench/nextjs-turbopack/app/api/trigger/route.tsRemoves Next.js (turbopack) app-router trigger endpoint.
workbench/nest/src/app.controller.tsRemoves Nest trigger routes and related imports.
workbench/hono/src/index.tsRemoves Hono trigger routes; keeps _workflows in graph via side-effect import.
workbench/fastify/src/index.tsRemoves Fastify trigger routes; keeps _workflows in graph via side-effect import.
workbench/express/src/index.tsRemoves Express trigger routes; keeps _workflows in graph via side-effect import.
workbench/example/api/trigger.tsRemoves example trigger endpoint implementation.
workbench/astro/src/pages/api/trigger.tsRemoves Astro trigger endpoint implementation.
turbo.jsonAdds WORKFLOW_PUBLIC_MANIFEST as a build task env input for Turbo caching.
scripts/create-test-matrix.mjsUpdates Vite dev-test API file path from trigger to chat.
packages/core/e2e/e2e.test.tsSwitches e2e workflow startup to manifest-based start({workflowId}) and updates streaming/error assertions accordingly.
.github/workflows/tests.ymlSets 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 threadpackages/core/e2e/e2e.test.ts
Comment threadpackages/core/e2e/e2e.test.ts
@TooTallNateGraphite App

TooTallNate commented Feb 6, 2026

Copy link
Copy Markdown
MemberAuthor

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

vercelBot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

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 freeto 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

@TooTallNate@VaguelySerious