NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3
, '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

NestJS framework support - #840

Merged
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs
Jan 26, 2026
Merged

NestJS framework support#840
VaguelySerious merged 31 commits into
mainfrom
peter/nestjs

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Jan 23, 2026

Copy link
Copy Markdown
Member

Add workbench app + local world tests for NestJS.

Can be merged safely since tests pass and there's no public project or docs associated. Docs and Vercel prod tests will be added in #850 once this is merged

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot

changeset-botBot commented Jan 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fe8c6d1

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

This PR includes changesets to release 13 packages
NameType
@workflow/nestPatch
@workflow/buildersPatch
workflowPatch
@workflow/example-nestPatch
@workflow/astroPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/sveltekitPatch
@workflow/aiPatch
@workflow/docs-typecheckPatch
@workflow/world-testingPatch
@workflow/nuxtPatch

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

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentReviewUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentJan 26, 2026 9:22pm
example-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-astro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-express-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-fastify-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-hono-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nitro-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-nuxt-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-sveltekit-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
workbench-vite-workflowReadyReadyPreview, CommentJan 26, 2026 9:22pm
1 Skipped Deployment
ProjectDeploymentReviewUpdated (UTC)
workflow-docsSkippedSkippedJan 26, 2026 9:22pm

@github-actions

github-actionsBot commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production457038495
❌ 💻 Local Development416232450
✅ 📦 Local Production418032450
✅ 🐘 Local Postgres418032450
✅ 🪟 Windows450045
❌ 🌍 Community Worlds311610192
✅ 📋 Other123012135
Total19081631462217

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (2 failed):

  • 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
🌍 Community Worlds (161 failed)

mongodb (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

redis (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

starter (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

turso (40 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • readableStreamWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • outputStreamWorkflow
  • outputStreamInsideStepWorkflow - getWritable() called inside step functions
  • 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
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4104
✅ example4104
✅ express4104
✅ fastify4104
✅ hono4104
✅ nextjs-turbopack4401
✅ nextjs-webpack4401
✅ nitro4104
✅ nuxt4104
✅ sveltekit4104
✅ vite4104
❌ 💻 Local Development
AppPassedFailedSkipped
❌ astro-stable3924
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4104
✅ express-stable4104
✅ fastify-stable4104
✅ hono-stable4104
✅ nextjs-turbopack-stable4500
✅ nextjs-webpack-stable4500
✅ nitro-stable4104
✅ nuxt-stable4104
✅ sveltekit-stable4104
✅ vite-stable4104
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4500
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
❌ mongodb5400
✅ redis-dev300
❌ redis5400
✅ starter-dev300
❌ starter4410
✅ turso-dev300
❌ turso5400
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4104
✅ e2e-local-postgres-nest-stable4104
✅ e2e-local-prod-nest-stable4104

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented Jan 23, 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🥇 Nitro0.042s (-5.9% 🟢)1.006s (~)0.965s101.00x
💻 LocalNext.js (Turbopack)0.043s (-13.2% 🟢)1.021s (~)0.978s101.03x
💻 LocalExpress0.043s (+3.8%)1.008s (~)0.965s101.04x
🐘 PostgresNext.js (Turbopack)0.159s (-4.8%)1.020s (~)0.861s103.82x
🐘 PostgresExpress0.205s (-6.7% 🟢)1.018s (~)0.813s104.91x
🐘 PostgresNitro0.242s (+16.8% 🔺)1.014s (-0.7%)0.772s105.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.674s (-2.2%)1.652s (+6.5% 🔺)0.978s101.00x
▲ VercelExpress0.757s (+1.8%)1.575s (-3.2%)0.818s101.12x
▲ VercelNext.js (Turbopack)0.800s (-8.8% 🟢)1.838s (+8.5% 🔺)1.038s101.19x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.093s (~)2.014s (~)0.921s101.00x
💻 LocalNitro1.112s (~)2.006s (~)0.893s101.02x
💻 LocalExpress1.116s (~)2.008s (~)0.892s101.02x
🐘 PostgresNext.js (Turbopack)1.910s (-0.5%)2.116s (+4.9%)0.207s101.75x
🐘 PostgresNitro1.945s (-19.5% 🟢)2.714s (-10.0% 🟢)0.769s101.78x
🐘 PostgresExpress2.292s (+1.8%)3.014s (~)0.722s102.10x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.808s (-4.3%)3.796s (+4.8%)0.987s101.00x
▲ VercelExpress2.901s (-1.1%)3.653s (-1.0%)0.751s101.03x
▲ VercelNext.js (Turbopack)2.908s (-5.3% 🟢)3.888s (+1.7%)0.980s101.04x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.702s (~)11.018s (~)0.316s51.00x
💻 LocalNitro10.814s (~)11.012s (~)0.198s51.01x
💻 LocalExpress10.831s (~)11.015s (~)0.184s51.01x
🐘 PostgresNext.js (Turbopack)15.211s (+0.8%)15.836s (~)0.624s51.42x
🐘 PostgresNitro15.751s (-22.3% 🟢)16.029s (-23.8% 🟢)0.279s51.47x
🐘 PostgresExpress20.435s (+1.0%)21.033s (~)0.597s51.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro22.525s (-2.8%)23.586s (-1.7%)1.061s51.00x
▲ VercelNext.js (Turbopack)22.818s (-2.1%)23.700s (-0.6%)0.882s51.01x
▲ VercelExpress23.436s (+1.3%)24.463s (+2.1%)1.027s51.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.411s (~)2.006s (~)0.594s151.00x
💻 LocalNext.js (Turbopack)1.414s (+2.9%)2.014s (~)0.600s151.00x
💻 LocalExpress1.429s (+2.1%)2.006s (~)0.577s151.01x
🐘 PostgresNext.js (Turbopack)2.110s (+22.6% 🔺)2.755s (+32.4% 🔺)0.645s111.50x
🐘 PostgresExpress2.186s (-9.8% 🟢)3.013s (~)0.828s101.55x
🐘 PostgresNitro2.305s (+2.8%)3.013s (+16.0% 🔺)0.708s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.912s (-37.9% 🟢)3.647s (-33.9% 🟢)0.735s91.00x
▲ VercelNext.js (Turbopack)2.991s (-29.2% 🟢)3.815s (-23.1% 🟢)0.824s81.03x
▲ VercelNitro3.135s (-29.0% 🟢)4.043s (-23.2% 🟢)0.907s81.08x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.456s (+2.8%)3.063s (-6.6% 🟢)0.607s101.00x
💻 LocalNitro2.467s (-1.7%)3.043s (+0.7%)0.576s101.00x
💻 LocalExpress2.496s (+2.7%)3.041s (-1.7%)0.545s101.02x
🐘 PostgresNitro7.731s (-14.1% 🟢)8.332s (-10.3% 🟢)0.601s43.15x
🐘 PostgresExpress8.712s (+17.8% 🔺)9.305s (+15.2% 🔺)0.592s43.55x
🐘 PostgresNext.js (Turbopack)12.196s (+14.5% 🔺)12.692s (+15.0% 🔺)0.496s34.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.320s (-32.6% 🟢)3.913s (-32.3% 🟢)0.593s81.00x
▲ VercelNitro3.436s (-7.1% 🟢)4.108s (-9.8% 🟢)0.672s81.03x
▲ VercelExpress3.450s (-10.8% 🟢)4.266s (-10.0% 🟢)0.817s81.04x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.403s (~)2.009s (~)0.606s151.00x
💻 LocalExpress1.408s (-1.1%)2.007s (~)0.599s151.00x
💻 LocalNitro1.425s (~)2.005s (~)0.580s151.02x
🐘 PostgresExpress2.111s (+3.6%)2.511s (~)0.400s121.50x
🐘 PostgresNitro2.245s (+7.9% 🔺)2.740s (+1.8%)0.495s111.60x
🐘 PostgresNext.js (Turbopack)2.486s (+20.9% 🔺)2.835s (+8.2% 🔺)0.349s111.77x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.826s (-22.5% 🟢)3.852s (-12.3% 🟢)1.025s81.00x
▲ VercelExpress2.990s (+2.5%)3.799s (+3.6%)0.809s81.06x
▲ VercelNext.js (Turbopack)3.079s (+5.1% 🔺)4.386s (+20.4% 🔺)1.307s71.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.449s (-2.2%)3.021s (-0.9%)0.572s101.00x
💻 LocalNitro2.508s (-3.5%)3.010s (~)0.503s101.02x
💻 LocalExpress2.696s (+7.9% 🔺)3.122s (+3.5%)0.426s101.10x
🐘 PostgresExpress10.477s (-7.1% 🟢)11.022s (-5.9% 🟢)0.544s34.28x
🐘 PostgresNitro11.165s (-3.6%)11.359s (-8.3% 🟢)0.194s34.56x
🐘 PostgresNext.js (Turbopack)12.929s (-0.9%)13.387s (-2.5%)0.458s35.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.384s (-18.7% 🟢)4.012s (-16.8% 🟢)0.628s81.00x
▲ VercelNitro3.402s (-4.7%)4.006s (-6.6% 🟢)0.605s81.01x
▲ VercelExpress3.527s (+5.1% 🔺)4.123s (+1.7%)0.596s81.04x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.142s (+2.1%)1.003s (~)0.018s (+1.1%)1.029s (~)0.887s101.00x
💻 LocalNitro0.178s (-2.7%)0.992s (~)0.016s (+4.6%)1.022s (~)0.845s101.25x
💻 LocalExpress0.180s (+0.7%)0.992s (~)0.014s (+0.7%)1.020s (~)0.840s101.27x
🐘 PostgresNext.js (Turbopack)0.913s (-27.7% 🟢)1.557s (~)0.000s (+Infinity% 🔺)1.616s (-11.2% 🟢)0.703s106.44x
🐘 PostgresNitro1.286s (-20.2% 🟢)1.755s (-23.5% 🟢)0.000s (+Infinity% 🔺)2.013s (-16.7% 🟢)0.727s109.06x
🐘 PostgresExpress2.253s (-4.3%)2.789s (+3.8%)0.000s (NaN%)3.015s (~)0.762s1015.88x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.963s (-15.2% 🟢)3.163s (-12.3% 🟢)0.520s (-13.1% 🟢)4.197s (-13.0% 🟢)1.234s101.00x
▲ VercelNitro3.002s (-19.1% 🟢)3.324s (-8.1% 🟢)0.392s (-45.9% 🟢)4.232s (-15.7% 🟢)1.230s101.01x
▲ VercelNext.js (Turbopack)3.019s (-24.5% 🟢)3.352s (-18.5% 🟢)0.747s (+38.1% 🔺)4.616s (-12.5% 🟢)1.597s101.02x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)6/8
🐘 PostgresNext.js (Turbopack)5/8
▲ VercelNitro4/8
Fastest World by Framework

Winner determined by most benchmark wins

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

Worlds:

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

📋 View full workflow run

This adds a @workflow/nest package that provides NestJS integration for the
Workflow DevKit. The integration includes:
- NestLocalBuilder: Extends BaseBuilder to generate workflow bundles in
.nestjs/workflow directory
- WorkflowModule: NestJS module that can be imported via forRoot() with
configuration options
- WorkflowController: Handles the /.well-known/workflow/v1/* endpoints for
workflow execution, step execution, and webhooks
The workbench/nest example app demonstrates how to use the integration with
a standard NestJS application, including:
- API endpoints for triggering workflows and testing
- Integration with the e2e test suite
- Support for both ESM and body parsing
Also updates:
- Test matrix to include NestJS in CI
- Workflow package to export nest module
- Builders package to support 'nest' build target
- Biome config to support parameter decorators used by NestJS
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@socket-security

socket-securityBot commented Jan 23, 2026

Copy link
Copy Markdown

NestJS preserves source maps in production builds, so update the
hasStepSourceMaps() function to return true for NestJS.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Add support for overriding the import path in generated _workflows.ts.
This is useful when workflows are compiled to a different location than
the source files, enabling future symlink support scenarios.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@@ -0,0 +1,11 @@
// Workflow-only file (no steps)

@pranaygppranaygpJan 23, 2026

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.

all the workflows should be symlinks from example

Comment on lines +45 to +47
const relativeWorkflowsPath = runtimePath
? runtimePath
: path.relative(outputDir, workflowsDir).replace(/\\/g, '/');

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.

Am worried about this and if it's actually relevant or needed just for nest (spoke offline)

- Update resolve-symlinks.sh to handle src/workflows symlinks
- Replace committed workflow files with symlink to example/workflows
- Disable typeCheck in NestJS build (symlinked files cause path issues)
- Fix @repo path alias to resolve to repo root
- Remove redundant local paths-alias-test.ts file
The symlinks are resolved to real files by resolve-symlinks.sh in CI,
enabling workflow file sharing across workbenches while maintaining
compatibility with production builds.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/tsconfig.json
Comment threadworkbench/nest/.swcrc Outdated
},
"experimental": {
"plugins": [
["@workflow/swc-plugin", { "mode": "client" }]

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.

erm I don't think nestjs users should have to setup the swc plugin explicitly. we don't/shouldn't do this on any of the other frameworks

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.

the swc plugin should be configured by the framework integration (see the other builders)

Comment threadworkbench/nest/package.json Outdated
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@workflow/ai": "workspace:*",
"@workflow/swc-plugin": "workspace:*",

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.

this shouldn't be a dep. workflow (and henceworkflow/nest) is all you need

@pranaygppranaygp 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.

the user shouldn't be setting up an swcrc. that's to be configured by the builders. infact I believe we ignore any project level swcrc in next for example :)

VaguelySeriousand others added 9 commits January 23, 2026 14:36
The --runtime-path option was added for potential symlink scenarios but
is not being used. Revert to keep the script simple.
Also fix whitespace in resolve-symlinks.sh.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Move the NestJS-specific parser option to nested biome.json files in
packages/nest and workbench/nest, keeping the root config cleaner.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest
- Remove @swc/cli and @swc/core from workbench/nest
- Keep @workflow/swc-plugin in workbench (required by .swcrc resolution)
- Add @workflow/nest as devDependency in workbench
The swc-plugin must remain a direct dependency in the workbench because
SWC's plugin resolution requires it to be resolvable from the project's
node_modules (referenced by name in .swcrc).
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The SWC dependencies were added to try making them transitively available
to the workbench, but pnpm's strict isolation prevents this.
The @workflow/swc-plugin must remain a direct devDependency in the NestJS
workbench because NestJS's SWC builder reads .swcrc which references the
plugin by name - it must be resolvable from the project's node_modules.
This is different from other frameworks (Nitro, Vite, Next.js) which
integrate the workflow plugin into their build systems internally.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
- Add @swc/core and @workflow/swc-plugin to packages/nest dependencies
- Remove @workflow/swc-plugin from workbench/nest
- Add .npmrc with public-hoist-pattern to make swc-plugin resolvable
The workbench app now only depends on @workflow/nest (like other
workbenches), and pnpm hoisting ensures the swc-plugin is available
for NestJS's SWC builder to resolve from .swcrc.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Instead of requiring users to configure pnpm hoisting for @workflow/swc-plugin,
@workflow/nest now provides a CLI command that generates .swcrc with the
resolved plugin path. This eliminates the need for root-level .npmrc changes.
Changes:
- Add workflow-nest CLI with 'init' command that generates .swcrc
- Remove .npmrc public-hoist-pattern configuration
- Update workbench/nest to use the CLI in prebuild/predev scripts
- Add .swcrc to .gitignore (contains machine-specific absolute paths)
Users can now simply run 'npx @workflow/nest init' after installing the package.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
The workflow-nest bin command isn't available in CI because pnpm install
runs before the packages are built. Use node to run the CLI directly
from the dist folder instead.
Also add README.md to packages/nest with setup instructions and
explanation of the CLI approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment threadworkbench/nest/package.json Outdated
Comment on lines +33 to +34
"@workflow/ai": "workspace:*",
"@workflow/nest": "workspace:*",

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.

I don't think these 2 are needed?
workflow/nest is already re-exporting @workflow/nest, and @worklfow/ai should not be a dev dep?

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
This reverts commit ebaf18c.
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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

@VaguelySerious@pranaygp@karthikscale3